diff --git a/.changeset/popular-bags-itch.md b/.changeset/popular-bags-itch.md new file mode 100644 index 00000000..15d30b1f --- /dev/null +++ b/.changeset/popular-bags-itch.md @@ -0,0 +1,5 @@ +--- +"@devup-ui/react": patch +--- + +Add group selector diff --git a/.changeset/strong-parents-change.md b/.changeset/strong-parents-change.md new file mode 100644 index 00000000..9e408467 --- /dev/null +++ b/.changeset/strong-parents-change.md @@ -0,0 +1,5 @@ +--- +"@devup-ui/wasm": patch +--- + +Add group selector, Support conditional expression for typography diff --git a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx index ce3a5bec..75dc71e4 100644 --- a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx +++ b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx @@ -1,109 +1,90 @@ -import { Box, Flex, Image, Text, VStack } from '@devup-ui/react' +import { VStack } from '@devup-ui/react' + +import { URL_PREFIX } from '../../../constants' +import { MenuItem } from './MenuItem' export function LeftMenu() { return ( - - - 개요 - - - Overview + Installation + Features + + API + + - - - 설치 - - - - - 개념 - - - - - - - - - 유틸리티 퍼스트 - - - - - 하이브리드 접근 방식 - - - - - 헤드리스 컴포넌트 - - - - - - - 구성 요소 - - - - - - API - - - - - - - - - 스타일이 지정됨 - - - - - CSS - - - - - - - 테마 - - - - - - - - - 테마 사용자 정의 - - - - - 테마 토큰 - - - - - 중단점 - - - - - 구성 요소 테마 - - - - + Devup + ) } diff --git a/apps/landing/src/app/(detail)/docs/MenuItem.tsx b/apps/landing/src/app/(detail)/docs/MenuItem.tsx new file mode 100644 index 00000000..7b599645 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/MenuItem.tsx @@ -0,0 +1,52 @@ +import { Box, css, Flex, Image, Text } from '@devup-ui/react' +import Link from 'next/link' + +import { OpenMenuItem } from './OpenMenuItem' + +export interface MenuItemProps { + selected?: boolean + children?: React.ReactNode + to?: string + subMenu?: { + selected?: boolean + children?: React.ReactNode + to?: string + }[] +} + +export function MenuItem(props: MenuItemProps) { + const { selected, children, to, subMenu } = props + if (subMenu) return + const inner = ( + + {selected && } + + {children} + + {subMenu && } + + ) + return to ? ( + + {inner} + + ) : ( + inner + ) +} diff --git a/apps/landing/src/app/(detail)/docs/OpenMenuItem.tsx b/apps/landing/src/app/(detail)/docs/OpenMenuItem.tsx new file mode 100644 index 00000000..2e6046e2 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/OpenMenuItem.tsx @@ -0,0 +1,83 @@ +'use client' +import { Box, css, Flex, Image, Text, VStack } from '@devup-ui/react' +import Link from 'next/link' +import { Fragment, useReducer } from 'react' + +import { MenuItemProps } from './MenuItem' + +export function OpenMenuItem({ + selected, + children, + subMenu, +}: Omit & + Required>) { + const [open, handleOpen] = useReducer((state) => !state, false) + return ( + <> + + {selected && } + + {children} + + {subMenu && ( + + )} + + {open && ( + + + + {subMenu.map(({ children, to }, idx) => { + const inner = ( + + + + {children} + + + ) + + return to ? ( + + {inner} + + ) : ( + {inner} + ) + })} + + + )} + + ) +} diff --git a/apps/landing/src/app/(detail)/docs/api/box/page.mdx b/apps/landing/src/app/(detail)/docs/api/box/page.mdx new file mode 100644 index 00000000..2436f01e --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/box/page.mdx @@ -0,0 +1 @@ +# Box diff --git a/apps/landing/src/app/(detail)/docs/api/button/page.mdx b/apps/landing/src/app/(detail)/docs/api/button/page.mdx new file mode 100644 index 00000000..88423dcb --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/button/page.mdx @@ -0,0 +1 @@ +# Button diff --git a/apps/landing/src/app/(detail)/docs/api/center/page.mdx b/apps/landing/src/app/(detail)/docs/api/center/page.mdx new file mode 100644 index 00000000..160a1609 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/center/page.mdx @@ -0,0 +1 @@ +# Center diff --git a/apps/landing/src/app/(detail)/docs/api/css/page.mdx b/apps/landing/src/app/(detail)/docs/api/css/page.mdx new file mode 100644 index 00000000..4988447f --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/css/page.mdx @@ -0,0 +1 @@ +# css diff --git a/apps/landing/src/app/(detail)/docs/api/flex/page.mdx b/apps/landing/src/app/(detail)/docs/api/flex/page.mdx new file mode 100644 index 00000000..61104142 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/flex/page.mdx @@ -0,0 +1 @@ +# Flex diff --git a/apps/landing/src/app/(detail)/docs/api/group-selector/page.mdx b/apps/landing/src/app/(detail)/docs/api/group-selector/page.mdx new file mode 100644 index 00000000..3fcbaa3b --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/group-selector/page.mdx @@ -0,0 +1 @@ +# Group Selector diff --git a/apps/landing/src/app/(detail)/docs/api/image/page.mdx b/apps/landing/src/app/(detail)/docs/api/image/page.mdx new file mode 100644 index 00000000..77cfc546 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/image/page.mdx @@ -0,0 +1 @@ +# Image diff --git a/apps/landing/src/app/(detail)/docs/api/input/page.mdx b/apps/landing/src/app/(detail)/docs/api/input/page.mdx new file mode 100644 index 00000000..135b6af3 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/input/page.mdx @@ -0,0 +1 @@ +# Input diff --git a/apps/landing/src/app/(detail)/docs/api/selector/page.mdx b/apps/landing/src/app/(detail)/docs/api/selector/page.mdx new file mode 100644 index 00000000..45ceab8f --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/selector/page.mdx @@ -0,0 +1 @@ +# Selector diff --git a/apps/landing/src/app/(detail)/docs/api/style-props/page.mdx b/apps/landing/src/app/(detail)/docs/api/style-props/page.mdx new file mode 100644 index 00000000..30dcd82e --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/style-props/page.mdx @@ -0,0 +1 @@ +# Style Props diff --git a/apps/landing/src/app/(detail)/docs/api/text/page.mdx b/apps/landing/src/app/(detail)/docs/api/text/page.mdx new file mode 100644 index 00000000..bb182be3 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/text/page.mdx @@ -0,0 +1 @@ +# Text diff --git a/apps/landing/src/app/(detail)/docs/api/v-stack/page.mdx b/apps/landing/src/app/(detail)/docs/api/v-stack/page.mdx new file mode 100644 index 00000000..7172ca68 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/api/v-stack/page.mdx @@ -0,0 +1 @@ +# VStack diff --git a/apps/landing/src/app/(detail)/docs/devup/breakpoints/page.mdx b/apps/landing/src/app/(detail)/docs/devup/breakpoints/page.mdx new file mode 100644 index 00000000..0c337a77 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/devup/breakpoints/page.mdx @@ -0,0 +1 @@ +# Breakpoints diff --git a/apps/landing/src/app/(detail)/docs/devup/colors/page.mdx b/apps/landing/src/app/(detail)/docs/devup/colors/page.mdx new file mode 100644 index 00000000..302161a2 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/devup/colors/page.mdx @@ -0,0 +1 @@ +# Colors diff --git a/apps/landing/src/app/(detail)/docs/devup/devup-json/page.mdx b/apps/landing/src/app/(detail)/docs/devup/devup-json/page.mdx new file mode 100644 index 00000000..b46107a7 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/devup/devup-json/page.mdx @@ -0,0 +1 @@ +# devup.json diff --git a/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx b/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx new file mode 100644 index 00000000..c972c3a8 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx @@ -0,0 +1 @@ +# Typography diff --git a/apps/landing/src/app/(detail)/docs/features/page.mdx b/apps/landing/src/app/(detail)/docs/features/page.mdx new file mode 100644 index 00000000..ead02231 --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/features/page.mdx @@ -0,0 +1 @@ +# Features diff --git a/apps/landing/src/app/(detail)/docs/installation/page.mdx b/apps/landing/src/app/(detail)/docs/installation/page.mdx new file mode 100644 index 00000000..935fcb2f --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/installation/page.mdx @@ -0,0 +1,6 @@ +# Installation + + +```bash +npm install @deuvp-ui/react +``` diff --git a/apps/landing/src/app/page.tsx b/apps/landing/src/app/page.tsx index 7049be2e..d40efa7f 100644 --- a/apps/landing/src/app/page.tsx +++ b/apps/landing/src/app/page.tsx @@ -28,9 +28,9 @@ export default function HomePage() { diff --git a/apps/landing/src/components/Header/HeaderWrap.tsx b/apps/landing/src/components/Header/HeaderWrap.tsx index 440c13df..3b5487c8 100644 --- a/apps/landing/src/components/Header/HeaderWrap.tsx +++ b/apps/landing/src/components/Header/HeaderWrap.tsx @@ -5,33 +5,23 @@ import { usePathname } from 'next/navigation' export function HeaderWrap({ children }: { children: React.ReactNode }) { const path = usePathname() - if (path !== '/') { - return ( - - - {children} - - - ) - } + const isRoot = path === '/' return ( - + diff --git a/apps/landing/src/components/Header/index.tsx b/apps/landing/src/components/Header/index.tsx index 3d3b1712..d08fed89 100644 --- a/apps/landing/src/components/Header/index.tsx +++ b/apps/landing/src/components/Header/index.tsx @@ -9,9 +9,9 @@ export function Header() { @@ -20,9 +20,9 @@ export function Header() { @@ -32,9 +32,9 @@ export function Header() { @@ -45,9 +45,9 @@ export function Header() { @@ -56,9 +56,9 @@ export function Header() { diff --git a/bindings/devup-ui-wasm/src/lib.rs b/bindings/devup-ui-wasm/src/lib.rs index 88c0df51..11592055 100644 --- a/bindings/devup-ui-wasm/src/lib.rs +++ b/bindings/devup-ui-wasm/src/lib.rs @@ -8,7 +8,8 @@ use std::collections::HashSet; use std::sync::Mutex; use wasm_bindgen::prelude::*; -static GLOBAL_STYLE_SHEET: Lazy> = Lazy::new(|| Mutex::new(StyleSheet::new())); +static GLOBAL_STYLE_SHEET: Lazy> = + Lazy::new(|| Mutex::new(StyleSheet::default())); #[wasm_bindgen] pub struct Output { @@ -131,7 +132,7 @@ pub fn object_to_typography(obj: Object, level: u8) -> Result Result { - let mut theme = Theme::new(); + let mut theme = Theme::default(); if let Ok(obj) = js_value.dyn_into::() { // get colors @@ -147,7 +148,7 @@ fn theme_object_to_hashmap(js_value: JsValue) -> Result { if let (Some(key_str), Some(theme_value)) = (key.as_string(), value.dyn_into::().ok()) { - let mut color_theme = ColorTheme::new(); + let mut color_theme = ColorTheme::default(); for var_entry in Object::entries(&theme_value).into_iter() { if let (Ok(var_key), Ok(var_value)) = ( Reflect::get(&var_entry, &JsValue::from_f64(0f64)), diff --git a/libs/css/src/lib.rs b/libs/css/src/lib.rs index 992eedfe..40d01436 100644 --- a/libs/css/src/lib.rs +++ b/libs/css/src/lib.rs @@ -1,7 +1,59 @@ +use crate::StyleSelector::{Dual, Postfix, Prefix}; use once_cell::sync::Lazy; use std::collections::{HashMap, HashSet}; +use std::fmt; +use std::fmt::{Display, Formatter}; use std::sync::Mutex; +#[derive(Debug, PartialEq, Clone, Hash, Eq)] +pub enum StyleSelector { + Postfix(String), + Prefix(String), + Dual(String, String), +} + +impl From<&str> for StyleSelector { + fn from(value: &str) -> Self { + if let Some(s) = value.strip_prefix("group") { + Dual("*[role=group]".to_string(), to_kebab_case(s)) + } else { + Postfix(value.to_string()) + } + } +} + +impl Display for StyleSelector { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + Postfix(value) => format!("-{}", value), + Prefix(value) => format!("-{}-", value), + Dual(prefix, postfix) => format!("-{}-{}", prefix, postfix), + } + ) + } +} + +pub fn merge_selector(class_name: &str, selector: Option<&StyleSelector>) -> String { + if let Some(selector) = selector { + match selector { + Postfix(postfix) => match get_selector_separator(postfix) { + SelectorSeparator::Single => format!(".{}:{}", class_name, postfix), + SelectorSeparator::Double => format!(".{}::{}", class_name, postfix), + }, + Prefix(prefix) => format!("{} {}", prefix, class_name), + Dual(prefix, postfix) => match get_selector_separator(postfix) { + SelectorSeparator::Single => format!("{}:{} .{}", prefix, postfix, class_name), + SelectorSeparator::Double => format!("{}::{} .{}", prefix, postfix, class_name), + }, + } + } else { + format!(".{}", class_name) + } +} + pub enum SelectorSeparator { Single, Double, @@ -441,5 +493,22 @@ mod tests { assert_eq!(to_kebab_case("maxWidth"), "max-width"); assert_eq!(to_kebab_case("maxHeight"), "max-height"); assert_eq!(to_kebab_case("MaxHeight"), "max-height"); + assert_eq!(to_kebab_case("Hover"), "hover"); + } + + #[test] + fn test_style_selector() { + assert_eq!(StyleSelector::from("hover"), Postfix("hover".to_string())); + assert_eq!( + StyleSelector::from("groupHover"), + Dual("*[role=group]".to_string(), "hover".to_string()) + ); + assert_eq!( + StyleSelector::from("group1"), + Dual("*[role=group]".to_string(), "1".to_string()) + ); + + assert_eq!(Prefix(".cls".to_string()).to_string(), "-.cls-"); + assert_eq!(Postfix(".cls".to_string()).to_string(), "-.cls"); } } diff --git a/libs/extractor/src/extract_style/mod.rs b/libs/extractor/src/extract_style/mod.rs index 4d92d91f..81d21ec1 100644 --- a/libs/extractor/src/extract_style/mod.rs +++ b/libs/extractor/src/extract_style/mod.rs @@ -1,6 +1,6 @@ use crate::utils::convert_value; use crate::StyleProperty; -use css::{css_to_classname, sheet_to_classname, sheet_to_variable_name}; +use css::{css_to_classname, sheet_to_classname, sheet_to_variable_name, StyleSelector}; use once_cell::sync::Lazy; use std::collections::HashSet; @@ -13,7 +13,7 @@ pub struct ExtractStaticStyle { /// responsive level level: u8, /// selector - selector: Option, + selector: Option, /// basic, if the value is true then css created by this style will be added to the first basic: bool, } @@ -30,7 +30,7 @@ static MAINTAIN_VALUE_PROPERTIES: Lazy> = Lazy::new(|| { impl ExtractStaticStyle { /// create a new ExtractStaticStyle - pub fn new(property: &str, value: &str, level: u8, selector: Option<&str>) -> Self { + pub fn new(property: &str, value: &str, level: u8, selector: Option) -> Self { Self { value: if MAINTAIN_VALUE_PROPERTIES.contains(property) { value.to_string() @@ -39,12 +39,17 @@ impl ExtractStaticStyle { }, property: property.to_string(), level, - selector: selector.map(|s| s.to_string()), + selector, basic: false, } } - pub fn new_basic(property: &str, value: &str, level: u8, selector: Option<&str>) -> Self { + pub fn new_basic( + property: &str, + value: &str, + level: u8, + selector: Option, + ) -> Self { Self { value: if MAINTAIN_VALUE_PROPERTIES.contains(property) { value.to_string() @@ -53,7 +58,7 @@ impl ExtractStaticStyle { }, property: property.to_string(), level, - selector: selector.map(|s| s.to_string()), + selector, basic: true, } } @@ -70,8 +75,8 @@ impl ExtractStaticStyle { self.level } - pub fn selector(&self) -> Option<&str> { - self.selector.as_deref() + pub fn selector(&self) -> Option<&StyleSelector> { + self.selector.as_ref() } pub fn basic(&self) -> bool { @@ -86,15 +91,12 @@ pub trait ExtractStyleProperty { impl ExtractStyleProperty for ExtractStaticStyle { fn extract(&self) -> StyleProperty { + let s = self.selector.clone().map(|s| s.to_string()); StyleProperty::ClassName(sheet_to_classname( self.property.as_str(), self.level, Some(convert_value(self.value.as_str()).as_str()), - if let Some(selector) = &self.selector { - Some(selector.as_str()) - } else { - None - }, + s.as_deref(), )) } } @@ -120,17 +122,22 @@ pub struct ExtractDynamicStyle { identifier: String, /// selector - selector: Option, + selector: Option, } impl ExtractDynamicStyle { /// create a new ExtractDynamicStyle - pub fn new(property: &str, level: u8, identifier: &str, selector: Option<&str>) -> Self { + pub fn new( + property: &str, + level: u8, + identifier: &str, + selector: Option, + ) -> Self { Self { property: property.to_string(), level, identifier: identifier.to_string(), - selector: selector.map(|s| s.to_string()), + selector, } } @@ -142,8 +149,8 @@ impl ExtractDynamicStyle { self.level } - pub fn selector(&self) -> Option<&str> { - self.selector.as_deref() + pub fn selector(&self) -> Option<&StyleSelector> { + self.selector.as_ref() } pub fn identifier(&self) -> &str { @@ -153,25 +160,18 @@ impl ExtractDynamicStyle { impl ExtractStyleProperty for ExtractDynamicStyle { fn extract(&self) -> StyleProperty { + let selector = self.selector.clone().map(|s| s.to_string()); StyleProperty::Variable { class_name: sheet_to_classname( self.property.as_str(), self.level, None, - if let Some(selector) = &self.selector { - Some(selector.as_str()) - } else { - None - }, + selector.as_deref(), ), variable_name: sheet_to_variable_name( self.property.as_str(), self.level, - if let Some(selector) = &self.selector { - Some(selector.as_str()) - } else { - None - }, + selector.as_deref(), ), identifier: self.identifier.clone(), } @@ -197,3 +197,37 @@ impl ExtractStyleValue { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extract_static_style() { + let style = ExtractStaticStyle::new("color", "red", 0, None); + assert_eq!(style.property(), "color"); + assert_eq!(style.value(), "red"); + assert_eq!(style.level(), 0); + assert_eq!(style.selector(), None); + assert_eq!(style.basic(), false); + } + + #[test] + fn test_extract_dynamic_style() { + let style = ExtractDynamicStyle::new("color", 0, "primary", None); + assert_eq!(style.property(), "color"); + assert_eq!(style.level(), 0); + assert_eq!(style.selector(), None); + assert_eq!(style.identifier(), "primary"); + } + + #[test] + fn test_extract_basic_static_style() { + let style = ExtractStaticStyle::new_basic("color", "red", 0, None); + assert_eq!(style.property(), "color"); + assert_eq!(style.value(), "red"); + assert_eq!(style.level(), 0); + assert_eq!(style.selector(), None); + assert_eq!(style.basic(), true); + } +} diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 030a4cc9..3dee1252 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -500,6 +500,19 @@ mod tests { "test.tsx", r#"import { Box } from "@devup-ui/core"; ; +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import { Box } from "@devup-ui/core"; +; "#, ExtractOption { package: "@devup-ui/core".to_string(), @@ -753,6 +766,52 @@ mod tests { ; +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import { css as c } from "@devup-ui/core"; +; +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import { css } from "@devup-ui/core"; +; +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import { css as c } from "@devup-ui/core"; +; "#, ExtractOption { package: "@devup-ui/core".to_string(), @@ -794,6 +853,32 @@ mod tests { } ) .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import {Text} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + + reset_class_map(); + assert_debug_snapshot!(extract( + "test.tsx", + r#"import {Text} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); } #[test] @@ -1210,4 +1295,21 @@ PROCESS_DATA.map(({ id, title, content }, idx) => ( ) .unwrap()); } + + #[test] + #[serial] + fn group_selector_props() { + reset_class_map(); + assert_debug_snapshot!(extract( + "test.js", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap()); + } } diff --git a/libs/extractor/src/snapshots/extractor__tests__apply_typography-2.snap b/libs/extractor/src/snapshots/extractor__tests__apply_typography-2.snap new file mode 100644 index 00000000..84bffb23 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__apply_typography-2.snap @@ -0,0 +1,12 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import {Text} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Typography( + "bold", + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__apply_typography-3.snap b/libs/extractor/src/snapshots/extractor__tests__apply_typography-3.snap new file mode 100644 index 00000000..f3f6128f --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__apply_typography-3.snap @@ -0,0 +1,15 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import {Text} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Typography( + "bold", + ), + Typography( + "bold2", + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_conditional_style_props-4.snap b/libs/extractor/src/snapshots/extractor__tests__extract_conditional_style_props-4.snap new file mode 100644 index 00000000..83ff5bdd --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_conditional_style_props-4.snap @@ -0,0 +1,8 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import { Box } from \"@devup-ui/core\";\n;\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [], + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_selector.snap b/libs/extractor/src/snapshots/extractor__tests__extract_selector.snap index fee9aa34..cbb6dcd4 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_selector.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_selector.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "4px", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive-2.snap b/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive-2.snap index fb2bed60..669e177c 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive-2.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive-2.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "40px", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, @@ -21,7 +23,9 @@ ExtractOutput { value: "80px", level: 1, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive.snap b/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive.snap index c6e93bc2..793a3636 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_selector_with_responsive.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "4px", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, @@ -21,7 +23,9 @@ ExtractOutput { value: "8px", level: 1, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-2.snap b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-2.snap new file mode 100644 index 00000000..a035cc80 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-2.snap @@ -0,0 +1,14 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import { css as c } from \"@devup-ui/core\";\n;\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Css( + ExtractCss { + css: "background-color: red;", + }, + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { css as c } from \"@devup-ui/core\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-3.snap b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-3.snap new file mode 100644 index 00000000..043a737d --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-3.snap @@ -0,0 +1,27 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import { css } from \"@devup-ui/core\";\n;\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Static( + ExtractStaticStyle { + property: "color", + value: "blue", + level: 0, + selector: None, + basic: false, + }, + ), + Static( + ExtractStaticStyle { + property: "bg", + value: "red", + level: 0, + selector: None, + basic: false, + }, + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { css } from \"@devup-ui/core\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-4.snap b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-4.snap new file mode 100644 index 00000000..53b5a8cd --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_static_css_class_name_props-4.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.tsx\",\nr#\"import { css as c } from \"@devup-ui/core\";\n;\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Static( + ExtractStaticStyle { + property: "bg", + value: "red", + level: 0, + selector: None, + basic: false, + }, + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { css as c } from \"@devup-ui/core\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__group_selector_props.snap b/libs/extractor/src/snapshots/extractor__tests__group_selector_props.snap new file mode 100644 index 00000000..c9e43d17 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__group_selector_props.snap @@ -0,0 +1,23 @@ +--- +source: libs/extractor/src/lib.rs +expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()" +--- +ExtractOutput { + styles: [ + Static( + ExtractStaticStyle { + property: "bg", + value: "red", + level: 0, + selector: Some( + Dual( + "*[role=group]", + "hover", + ), + ), + basic: false, + }, + ), + ], + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-2.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-2.snap index 860207da..c01c5230 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-2.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-2.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "blue", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-3.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-3.snap index ca01a1ac..13ddbfd4 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-3.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs-3.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "blue", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs.snap index 24ef10c1..bf44a672 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "blue", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-2.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-2.snap index fe0d4cce..328983c9 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-2.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-2.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "blue", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs.snap index f1333282..aa903f9b 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs.snap @@ -10,7 +10,9 @@ ExtractOutput { value: "blue", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector-2.snap b/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector-2.snap index 52826f22..4bd74638 100644 --- a/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector-2.snap +++ b/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector-2.snap @@ -19,7 +19,9 @@ ExtractOutput { value: "red", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector.snap b/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector.snap index 9ceb9297..19e94a6c 100644 --- a/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector.snap +++ b/libs/extractor/src/snapshots/extractor__tests__ternary_operator_in_selector.snap @@ -19,7 +19,9 @@ ExtractOutput { value: "red", level: 0, selector: Some( - "hover", + Postfix( + "hover", + ), ), basic: false, }, diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index db370618..acf37f9a 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -3,8 +3,8 @@ use crate::ExtractStyleProp; use oxc_allocator::CloneIn; use oxc_ast::ast::{Expression, JSXAttributeValue, ObjectPropertyKind, PropertyKey}; -use crate::extract_style::ExtractStyleValue::{Dynamic, Static}; -use crate::extract_style::{ExtractDynamicStyle, ExtractStaticStyle, ExtractStyleValue}; +use crate::extract_style::ExtractStyleValue::{Dynamic, Static, Typography}; +use crate::extract_style::{ExtractDynamicStyle, ExtractStaticStyle}; use oxc_ast::AstBuilder; use oxc_span::SPAN; use oxc_syntax::operator::{BinaryOperator, LogicalOperator}; @@ -74,18 +74,11 @@ pub fn extract_style_from_expression<'a>( level: u8, selector: Option<&str>, ) -> ExtractResult<'a> { + let mut typo = false; if let Some(name) = name { if is_special_property(name) { return ExtractResult::Maintain; } - if name == "typography" { - if let Expression::StringLiteral(ident) = &expression { - return ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static( - ExtractStyleValue::Typography(ident.value.to_string()), - )]); - } - return ExtractResult::Remove; - } if name == "as" { if let Expression::StringLiteral(ident) = &expression { @@ -103,6 +96,9 @@ pub fn extract_style_from_expression<'a>( Some(selector), ); } + if name == "typography" { + typo = true; + } } match expression { Expression::ComputedMemberExpression(mem) => { @@ -157,7 +153,7 @@ pub fn extract_style_from_expression<'a>( name, level, expression_to_code(expression).as_str(), - selector, + selector.map(|s| s.into()), ))), ]); } @@ -191,7 +187,7 @@ pub fn extract_style_from_expression<'a>( name, level, expression_to_code(expression).as_str(), - selector, + selector.map(|s| s.into()), ), )), ]); @@ -208,7 +204,7 @@ pub fn extract_style_from_expression<'a>( name, level, expression_to_code(expression).as_str(), - selector, + selector.map(|s| s.into()), )), )]); } @@ -222,7 +218,12 @@ pub fn extract_style_from_expression<'a>( Expression::NumericLiteral(v) => { if let Some(name) = name { ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(Static( - ExtractStaticStyle::new(name, &v.value.to_string(), level, selector), + ExtractStaticStyle::new( + name, + &v.value.to_string(), + level, + selector.map(|s| s.into()), + ), ))]) } else { ExtractResult::Maintain @@ -231,21 +232,23 @@ pub fn extract_style_from_expression<'a>( Expression::TemplateLiteral(tmp) => { if let Some(name) = name { if tmp.quasis.len() == 1 { - ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(Static( - ExtractStaticStyle::new( + ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(if typo { + Typography(tmp.quasis[0].value.raw.as_str().to_string()) + } else { + Static(ExtractStaticStyle::new( name, tmp.quasis[0].value.raw.as_str(), level, - selector, - ), - ))]) + selector.map(|s| s.into()), + )) + })]) } else { ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(Dynamic( ExtractDynamicStyle::new( name, level, expression_to_code(expression).as_str(), - selector, + selector.map(|s| s.into()), ), ))]) } @@ -255,9 +258,16 @@ pub fn extract_style_from_expression<'a>( } Expression::StringLiteral(v) => { if let Some(name) = name { - ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(Static( - ExtractStaticStyle::new(name, v.value.as_str(), level, selector), - ))]) + ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(if typo { + Typography(v.value.as_str().to_string()) + } else { + Static(ExtractStaticStyle::new( + name, + v.value.as_str(), + level, + selector.map(|s| s.into()), + )) + })]) } else { ExtractResult::Maintain } @@ -267,7 +277,12 @@ pub fn extract_style_from_expression<'a>( ExtractResult::Maintain } else if let Some(name) = name { ExtractResult::ExtractStyle(vec![ExtractStyleProp::Static(Dynamic( - ExtractDynamicStyle::new(name, level, identifier.name.as_str(), selector), + ExtractDynamicStyle::new( + name, + level, + identifier.name.as_str(), + selector.map(|s| s.into()), + ), ))]) } else { ExtractResult::Maintain diff --git a/libs/extractor/src/visit.rs b/libs/extractor/src/visit.rs index 69b280f3..e98ad933 100644 --- a/libs/extractor/src/visit.rs +++ b/libs/extractor/src/visit.rs @@ -1,5 +1,5 @@ use crate::component::ExportVariableKind; -use crate::extract_style::ExtractCss; +use crate::extract_style::{ExtractCss, ExtractStyleProperty}; use crate::prop_modify_utils::{modify_prop_object, modify_props}; use crate::style_extractor::{ extract_style_from_expression, extract_style_from_jsx_attr, ExtractResult, @@ -27,6 +27,7 @@ pub struct DevupVisitor<'a> { imports: HashMap, import_object: Option, jsx_imports: HashMap, + css_imports: HashMap, jsx_object: Option, package: String, css_file: String, @@ -44,6 +45,7 @@ impl<'a> DevupVisitor<'a> { styles: vec![], import_object: None, jsx_object: None, + css_imports: HashMap::new(), } } } @@ -172,11 +174,72 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { } } } + println!("{:?} {:?}", it, self.css_imports); + if let Expression::Identifier(ident) = &it.callee { + if self.css_imports.contains_key(ident.name.as_str()) && it.arguments.len() == 1 { + if let Argument::ObjectExpression(ref mut obj) = it.arguments[0] { + let mut props_styles = vec![]; + for idx in (0..obj.properties.len()).rev() { + let mut prop = obj.properties.remove(idx); + let mut rm = false; + if let ObjectPropertyKind::ObjectProperty(prop) = &mut prop { + if let PropertyKey::StaticIdentifier(ident) = &prop.key { + let name = ident.name.to_string(); + rm = match extract_style_from_expression( + &self.ast, + Some(&name), + &mut prop.value, + 0, + None, + ) { + ExtractResult::Maintain => false, + ExtractResult::Remove => true, + ExtractResult::ExtractStyle(mut styles) => { + styles.reverse(); + props_styles.append(&mut styles); + true + } + ExtractResult::ChangeTag(_) => true, + } + } + } + if !rm { + obj.properties.insert(idx, prop); + } + } + let mut styles = props_styles + .into_iter() + .flat_map(|ex| ex.extract()) + .collect::>(); + let class_name = styles + .iter() + .filter_map(|ex| match ex { + ExtractStyleValue::Static(css) => { + if let StyleProperty::ClassName(cls) = css.extract() { + Some(cls.to_string()) + } else { + None + } + } + _ => None, + }) + .collect::>() + .join(" "); + + self.styles.append(&mut styles); + it.arguments[0] = Argument::StringLiteral(self.ast.alloc_string_literal( + SPAN, + self.ast.atom(&class_name), + None, + )); + } + } + } walk_call_expression(self, it); } fn visit_tagged_template_expression(&mut self, it: &mut TaggedTemplateExpression<'a>) { if let Expression::Identifier(ident) = &it.tag { - if ident.name != "css" { + if !self.css_imports.contains_key(ident.name.as_str()) { walk_tagged_template_expression(self, it); return; } @@ -336,6 +399,9 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { // remove specifier specifiers.remove(i); + } else if import.imported.to_string() == "css" { + self.css_imports + .insert(import.local.to_string(), it.source.value.to_string()); } } } diff --git a/libs/sheet/src/lib.rs b/libs/sheet/src/lib.rs index 080d5560..04762240 100644 --- a/libs/sheet/src/lib.rs +++ b/libs/sheet/src/lib.rs @@ -1,7 +1,7 @@ pub mod theme; use crate::theme::Theme; -use css::{convert_property, get_selector_separator, to_kebab_case, PropertyType}; +use css::{convert_property, merge_selector, PropertyType, StyleSelector}; use std::cmp::Ordering::{Greater, Less}; use std::collections::{BTreeMap, HashSet}; @@ -14,36 +14,24 @@ pub struct StyleSheetProperty { pub class_name: String, pub property: String, pub value: String, - pub selector: Option, + pub selector: Option, pub basic: bool, } impl ExtractStyle for StyleSheetProperty { fn extract(&self) -> String { - let selector = if let Some(selector) = &self.selector { - let selector = to_kebab_case(selector); - format!( - "{}{}", - get_selector_separator(&selector).separator(), - selector - ) - } else { - String::new() - }; match convert_property(self.property.as_str()) { PropertyType::Single(prop) => { format!( - ".{}{}{{{}:{}}}", - self.class_name, - selector, + "{}{{{}:{}}}", + merge_selector(&self.class_name, self.selector.as_ref()), prop, convert_theme_variable_value(&self.value) ) } PropertyType::Multi(multi) => format!( - ".{}{}{{{}}}", - self.class_name, - selector, + "{}{{{}}}", + merge_selector(&self.class_name, self.selector.as_ref()), multi .into_iter() .map(|prop| format!("{}:{};", prop, convert_theme_variable_value(&self.value))) @@ -74,6 +62,7 @@ impl ExtractStyle for StyleSheetCss { } } +#[derive(Default)] pub struct StyleSheet { /// level -> properties pub properties: BTreeMap>, @@ -82,36 +71,21 @@ pub struct StyleSheet { theme_declaration: String, } -impl Default for StyleSheet { - fn default() -> Self { - Self::new() - } -} - impl StyleSheet { - pub fn new() -> Self { - Self { - properties: BTreeMap::new(), - css: HashSet::new(), - theme_declaration: String::new(), - theme: Theme::new(), - } - } - pub fn add_property( &mut self, class_name: &str, property: &str, level: u8, value: &str, - selector: Option<&str>, + selector: Option<&StyleSelector>, basic: bool, ) -> bool { let prop = StyleSheetProperty { class_name: class_name.to_string(), property: property.to_string(), value: value.to_string(), - selector: selector.map(|s| s.to_string()), + selector: selector.cloned(), basic, }; self.properties.entry(level).or_default().insert(prop) @@ -152,7 +126,7 @@ impl StyleSheet { let inner_css = sorted_props .into_iter() - .map(|prop| prop.extract()) + .map(ExtractStyle::extract) .collect::>() .join(""); if *level == 0 { @@ -216,72 +190,102 @@ mod tests { } #[test] fn test_create_css_with_selector_sort_test() { - let mut sheet = StyleSheet::new(); - sheet.add_property("test", "background-color", 1, "red", Some("hover"), false); + let mut sheet = StyleSheet::default(); + sheet.add_property( + "test", + "background-color", + 1, + "red", + Some(&StyleSelector::Postfix("hover".to_string())), + false, + ); sheet.add_property("test", "background-color", 1, "some", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "background-color", 1, "red", None, false); - sheet.add_property("test", "background-color", 1, "some", Some("hover"), false); - sheet.set_theme(Theme::new()); + sheet.add_property( + "test", + "background-color", + 1, + "some", + Some(&StyleSelector::Postfix("hover".to_string())), + false, + ); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "background-color", 1, "red", None, false); sheet.add_property("test", "background", 1, "some", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); } #[test] fn test_create_css_with_basic_sort_test() { - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "background-color", 1, "red", None, true); sheet.add_property("test", "background", 1, "some", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "border", 0, "1px solid", None, false); sheet.add_property("test", "border-color", 0, "red", None, true); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "display", 0, "flex", None, true); sheet.add_property("test", "display", 0, "block", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); } #[test] fn test_create_css_with_selector_and_basic_sort_test() { - let mut sheet = StyleSheet::new(); - sheet.add_property("test", "background-color", 1, "red", Some("hover"), false); + let mut sheet = StyleSheet::default(); + sheet.add_property( + "test", + "background-color", + 1, + "red", + Some(&StyleSelector::Postfix("hover".to_string())), + false, + ); sheet.add_property("test", "background-color", 1, "some", None, true); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); } #[test] fn test_create_css() { - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "mx", 1, "40px", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_css("test", "display:flex;"); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); } #[test] fn wrong_breakpoint() { - let mut sheet = StyleSheet::new(); + let mut sheet = StyleSheet::default(); sheet.add_property("test", "mx", 10, "40px", None, false); - sheet.set_theme(Theme::new()); + sheet.set_theme(Theme::default()); + assert_debug_snapshot!(sheet.create_css()); + } + + #[test] + fn test_selector_with_prefix() { + let mut sheet = StyleSheet::default(); + sheet.add_property("test", "mx", 1, "40px", Some(&"groupHover".into()), false); + sheet.add_property("test", "mx", 2, "50px", Some(&"groupHover".into()), false); + sheet.set_theme(Theme::default()); assert_debug_snapshot!(sheet.create_css()); } } diff --git a/libs/sheet/src/snapshots/sheet__tests__selector_with_prefix.snap b/libs/sheet/src/snapshots/sheet__tests__selector_with_prefix.snap new file mode 100644 index 00000000..0b08388e --- /dev/null +++ b/libs/sheet/src/snapshots/sheet__tests__selector_with_prefix.snap @@ -0,0 +1,5 @@ +--- +source: libs/sheet/src/lib.rs +expression: sheet.create_css() +--- +"\n@media (min-width:480px){*[role=group]:hover .test{margin-left:40px;margin-right:40px;}}\n@media (min-width:768px){*[role=group]:hover .test{margin-left:50px;margin-right:50px;}}" diff --git a/libs/sheet/src/theme.rs b/libs/sheet/src/theme.rs index c6042ea7..d8420144 100644 --- a/libs/sheet/src/theme.rs +++ b/libs/sheet/src/theme.rs @@ -1,34 +1,15 @@ use std::collections::{BTreeMap, HashMap}; +#[derive(Default)] pub struct ColorTheme { data: HashMap, } -impl Default for ColorTheme { - fn default() -> Self { - Self::new() - } -} - impl ColorTheme { - pub fn new() -> Self { - Self { - data: HashMap::new(), - } - } - pub fn add_color(&mut self, name: &str, value: &str) { self.data.insert(name.to_string(), value.to_string()); } - pub fn get_color(&self, name: &str) -> Option<&String> { - self.data.get(name) - } - - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - pub fn iter(&self) -> impl Iterator { self.data.iter() } @@ -37,31 +18,16 @@ impl ColorTheme { } } +#[derive(Default)] pub struct Color { pub themes: HashMap, } -impl Default for Color { - fn default() -> Self { - Self::new() - } -} - impl Color { - pub fn new() -> Self { - Self { - themes: HashMap::new(), - } - } - pub fn add_theme(&mut self, name: &str, theme: ColorTheme) { self.themes.insert(name.to_string(), theme); } - pub fn get_theme(&self, name: &str) -> Option<&ColorTheme> { - self.themes.get(name) - } - pub fn to_css(&self) -> String { let mut theme_declaration = String::new(); let default_theme_key = self @@ -132,19 +98,15 @@ pub struct Theme { impl Default for Theme { fn default() -> Self { - Self::new() - } -} - -impl Theme { - pub fn new() -> Self { Self { - colors: Color::new(), + colors: Color::default(), break_points: vec![0, 480, 768, 992, 1280], typography: BTreeMap::new(), } } +} +impl Theme { pub fn update_break_points(&mut self, break_points: Vec) { for (idx, value) in break_points.iter().enumerate() { let prev = self.break_points.get_mut(idx); @@ -219,10 +181,6 @@ impl Theme { } css } - - pub fn get_color_theme(&self, name: &str) -> Option<&ColorTheme> { - self.colors.get_theme(name) - } } #[cfg(test)] @@ -231,11 +189,14 @@ mod tests { #[test] fn to_css_from_theme() { - let mut theme = Theme::new(); - let mut color_theme = ColorTheme::new(); + let mut theme = Theme::default(); + let mut color_theme = ColorTheme::default(); color_theme.add_color("primary", "#000"); + + assert_eq!(color_theme.keys().count(), 1); + theme.add_color_theme("default", color_theme); - let mut color_theme = ColorTheme::new(); + let mut color_theme = ColorTheme::default(); color_theme.add_color("primary", "#fff"); theme.add_color_theme("dark", color_theme); theme.add_typography( @@ -276,5 +237,13 @@ mod tests { css, ":root{--primary:#000;}\n:root[data-theme=dark]{--primary:#fff;}\n.typo-default{font-family:Arial;font-size:16px;font-weight:400;line-height:1.5;letter-spacing:0.5}\n@media (min-width:480px){.typo-default{font-family:Arial;font-size:24px;font-weight:400;line-height:1.5;letter-spacing:0.5}.typo-default1{font-family:Arial;font-size:24px;font-weight:400;line-height:1.5;letter-spacing:0.5}}" ); + + assert_eq!(Theme::default().to_css(), ""); + let mut theme = Theme::default(); + theme.add_typography( + "default", + vec![Typography::new(None, None, None, None, None, 0)], + ); + assert_eq!(theme.to_css(), ""); } } diff --git a/packages/react/src/types/props/selector/index.ts b/packages/react/src/types/props/selector/index.ts index cc219cc6..61df135c 100644 --- a/packages/react/src/types/props/selector/index.ts +++ b/packages/react/src/types/props/selector/index.ts @@ -22,6 +22,27 @@ export interface DevupSelectorProps { _optional?: DevupCommonProps _readOnly?: DevupCommonProps + _groupActive?: DevupCommonProps + _groupChecked?: DevupCommonProps + _groupDefault?: DevupCommonProps + _groupDisabled?: DevupCommonProps + _groupEmpty?: DevupCommonProps + _groupEnabled?: DevupCommonProps + _groupFirst?: DevupCommonProps + _groupFirstChild?: DevupCommonProps + _groupFirstOfType?: DevupCommonProps + _groupFocus?: DevupCommonProps + _groupFocusVisible?: DevupCommonProps + _groupFocusWithin?: DevupCommonProps + _groupHover?: DevupCommonProps + _groupInvalid?: DevupCommonProps + _groupLastChild?: DevupCommonProps + _groupLastOfType?: DevupCommonProps + _groupLink?: DevupCommonProps + _groupOnlyChild?: DevupCommonProps + _groupOptional?: DevupCommonProps + _groupReadOnly?: DevupCommonProps + // double separator _placeholder?: DevupCommonProps _before?: DevupCommonProps diff --git a/packages/react/src/utils/__tests__/index.test.ts b/packages/react/src/utils/__tests__/index.test.ts index 493faa27..4bb18d54 100644 --- a/packages/react/src/utils/__tests__/index.test.ts +++ b/packages/react/src/utils/__tests__/index.test.ts @@ -3,5 +3,6 @@ import { css } from '../css' describe('css', () => { it('should return className', async () => { expect(css`virtual-css`).toEqual('virtual-css') + expect(css('class name' as any)).toEqual('class name') }) }) diff --git a/packages/react/src/utils/css.ts b/packages/react/src/utils/css.ts index 93a27026..734c76de 100644 --- a/packages/react/src/utils/css.ts +++ b/packages/react/src/utils/css.ts @@ -1,3 +1,11 @@ -export function css(strings: TemplateStringsArray) { - return strings.join('') +import { DevupCommonProps } from '../types/props' + +export function css(props: DevupCommonProps): string +export function css(strings: TemplateStringsArray): string + +export function css(strings: TemplateStringsArray | DevupCommonProps): string { + if (Array.isArray(strings)) { + return strings.join('') + } + return strings as string }