diff --git a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/forms/Button.kt b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/forms/Button.kt index 62cf57b73..24f985129 100644 --- a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/forms/Button.kt +++ b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/forms/Button.kt @@ -30,15 +30,23 @@ object Buttons { val ButtonStyle = ComponentStyle("silk-button") { val buttonColors = SilkTheme.palettes[colorMode].button - base = Modifier.background(buttonColors.default).clip(Rect(4.px)).styleModifier { - // No selecting text within buttons - userSelect(UserSelect.None) - property("role", "button") + base { + Modifier.background(buttonColors.default).clip(Rect(4.px)).styleModifier { + // No selecting text within buttons + userSelect(UserSelect.None) + property("role", "button") + } } - hover = Modifier.background(buttonColors.hover).styleModifier { - cursor(Cursor.Pointer) + + hover { + Modifier.background(buttonColors.hover).styleModifier { + cursor(Cursor.Pointer) + } + } + + active { + Modifier.background(buttonColors.pressed) } - active = Modifier.background(buttonColors.pressed) } /** diff --git a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/layout/SimpleGrid.kt b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/layout/SimpleGrid.kt index a011a7078..0003f4066 100644 --- a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/layout/SimpleGrid.kt +++ b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/layout/SimpleGrid.kt @@ -17,8 +17,10 @@ import org.jetbrains.compose.web.dom.Div private const val MAX_COLUMN_COUNT = 4 val SimpleGridStyle = ComponentStyle("silk-simple-grid") { - base = Modifier.styleModifier { - display(DisplayStyle.Grid) + base { + Modifier.styleModifier { + display(DisplayStyle.Grid) + } } } @@ -34,9 +36,9 @@ private val SimpleGridColumnVariants: Map() // LinkedHashMap preserves insertion order + + internal val pseudoElements = LinkedHashMap() // LinkedHashMap preserves insertion order + + internal val breakpoints = mutableMapOf() + + /** Define base styles for this component, will always be applied first. */ + fun base(createModifier: () -> Modifier) { + base = createModifier() + } /** * Register styles associated with pseudo classes like "hover". * - * You can either add a pseudo class modifier to this map directly or use one of the various convenience properties - * provided which will do it for you. - * * Pseudo classes will be applied in the order inserted. Be aware that you should use the LVHA order if using link, * visited, hover, and/or active pseudo classes. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes */ - val pseudoClasses = LinkedHashMap() // LinkedHashMap preserves insertion order + fun pseudoClass(name: String, createModifier: () -> Modifier) { + pseudoClasses[name] = createModifier() + } /** * Register styles associated with pseudo elements like "after". * - * You can either add a pseudo element modifier to this map directly or use one of the various convenience properties - * provided which will do it for you. - * - * Pseudo-elements will be applied in the order inserted, and after all pseudo classes. + * Pseudo-elements will be applied in the order registered, and after all pseudo classes. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements */ - val pseudoElements = LinkedHashMap() // LinkedHashMap preserves insertion order + fun pseudoElement(name: String, createModifier: () -> Modifier) { + pseudoElements[name] = createModifier() + } /** * Register layout styles which are dependent on the current window width. * * Breakpoints will be applied in order from smallest to largest, and after all pseudo classes and pseudo-elements. */ - val breakpoints = mutableMapOf() - - /** - * An alternate way to add [pseudoClasses] entries, mostly provided for supporting extension properties. - */ - fun setPseudoClassModifier(pseudoClass: String, modifier: Modifier?) { - if (modifier != null) { - pseudoClasses[pseudoClass] = modifier - } - else { - pseudoClasses.remove(pseudoClass) - } - } - - /** - * An alternate way to add [pseudoElements] entries, mostly provided for supporting extension properties. - */ - fun setPseudoElementModifier(pseudoElement: String, modifier: Modifier?) { - if (modifier != null) { - pseudoElements[pseudoElement] = modifier - } - else { - pseudoElements.remove(pseudoElement) - } + fun breakpoint(breakpoint: Breakpoint, createModifier: () -> Modifier) { + breakpoints[breakpoint] = createModifier() } /** - * An alternate way to add [breakpoints] entries, mostly provided for supporting extension properties. + * Convenience function for associating a modifier directly against a breakpoint enum. + * + * For example, you can call + * + * ``` + * Breakpoint.MD { Modifier.color(...) } + * ``` + * + * which is identical to + * + * ``` + * breakpoint(Breakpoint.MD) { Modifier.color(...) } + * ``` */ - fun setBreakpointModifier(breakpoint: Breakpoint, modifier: Modifier?) { - if (modifier != null) { - breakpoints[breakpoint] = modifier - } - else { - breakpoints.remove(breakpoint) - } + operator fun Breakpoint.invoke(createModifier: () -> Modifier) { + breakpoints[this] = createModifier() } + } /** diff --git a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/style/ComponentStyleExtensions.kt b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/style/ComponentStyleExtensions.kt index aec5cef06..02dacb7cc 100644 --- a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/style/ComponentStyleExtensions.kt +++ b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/style/ComponentStyleExtensions.kt @@ -12,9 +12,9 @@ import com.varabyte.kobweb.silk.components.style.breakpoint.Breakpoint * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:any-link */ -var ComponentModifiers.anyLink: Modifier? - get() = pseudoClasses["any-link"] - set(value) = setPseudoClassModifier("any-link", value) +fun ComponentModifiers.anyLink(createModifier: () -> Modifier) { + pseudoClass("any-link", createModifier) +} /** * Styles to apply to components that represent navigation links which have not yet been visited. @@ -23,18 +23,18 @@ var ComponentModifiers.anyLink: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:link */ -var ComponentModifiers.link: Modifier? - get() = pseudoClasses["link"] - set(value) = setPseudoClassModifier("link", value) +fun ComponentModifiers.link(createModifier: () -> Modifier) { + pseudoClass("link", createModifier) +} /** * Styles to apply to elements that are targets of links in the same document * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:target */ -var ComponentModifiers.target: Modifier? - get() = pseudoClasses["target"] - set(value) = setPseudoClassModifier("target", value) +fun ComponentModifiers.target(createModifier: () -> Modifier) { + pseudoClass("target", createModifier) +} /** * Styles to apply to components that represent navigation links which have previously been visited. @@ -43,10 +43,9 @@ var ComponentModifiers.target: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:visited */ -var ComponentModifiers.visited: Modifier? - get() = pseudoClasses["visited"] - set(value) = setPseudoClassModifier("visited", value) - +fun ComponentModifiers.visited(createModifier: () -> Modifier) { + pseudoClass("visited", createModifier) +} //endregion @@ -59,9 +58,9 @@ var ComponentModifiers.visited: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:hover */ -var ComponentModifiers.hover: Modifier? - get() = pseudoClasses["hover"] - set(value) = setPseudoClassModifier("hover", value) +fun ComponentModifiers.hover(createModifier: () -> Modifier) { + pseudoClass("hover", createModifier) +} /** * Styles to apply to components when a cursor is interacting with them. @@ -70,18 +69,19 @@ var ComponentModifiers.hover: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:active */ -var ComponentModifiers.active: Modifier? - get() = pseudoClasses["active"] - set(value) = setPseudoClassModifier("active", value) +fun ComponentModifiers.active(createModifier: () -> Modifier) { + pseudoClass("active", createModifier) +} + /** * Styles to apply to components when they have focus. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:focus */ -var ComponentModifiers.focus: Modifier? - get() = pseudoClasses["focus"] - set(value) = setPseudoClassModifier("focus", value) +fun ComponentModifiers.focus(createModifier: () -> Modifier) { + pseudoClass("focus", createModifier) +} //endregion @@ -92,108 +92,108 @@ var ComponentModifiers.focus: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill */ -var ComponentModifiers.autofill: Modifier? - get() = pseudoClasses["autofill"] - set(value) = setPseudoClassModifier("autofill", value) +fun ComponentModifiers.autofill(createModifier: () -> Modifier) { + pseudoClass("autofill", createModifier) +} /** * Represents a user interface element that is in an enabled state. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled */ -var ComponentModifiers.enabled: Modifier? - get() = pseudoClasses["enabled"] - set(value) = setPseudoClassModifier("enabled", value) +fun ComponentModifiers.enabled(createModifier: () -> Modifier) { + pseudoClass("enabled", createModifier) +} /** * Represents a user interface element that is in a disabled state. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled */ -var ComponentModifiers.disabled: Modifier? - get() = pseudoClasses["disabled"] - set(value) = setPseudoClassModifier("disabled", value) +fun ComponentModifiers.disabled(createModifier: () -> Modifier) { + pseudoClass("disabled", createModifier) +} /** * Represents any element that cannot be changed by the user. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:read-only */ -var ComponentModifiers.readOnly: Modifier? - get() = pseudoClasses["read-only"] - set(value) = setPseudoClassModifier("read-only", value) +fun ComponentModifiers.readOnly(createModifier: () -> Modifier) { + pseudoClass("read-only", createModifier) +} /** * Represents any element that is user-editable. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:read-write */ -var ComponentModifiers.readWrite: Modifier? - get() = pseudoClasses["read-write"] - set(value) = setPseudoClassModifier("read-write", value) +fun ComponentModifiers.readWrite(createModifier: () -> Modifier) { + pseudoClass("read-write", createModifier) +} /** * Matches an input element that is displaying placeholder text, for example from the HTML5 placeholder attribute. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:placeholder-shown */ -var ComponentModifiers.placeholderShown: Modifier? - get() = pseudoClasses["placeholder-shown"] - set(value) = setPseudoClassModifier("placeholder-shown", value) +fun ComponentModifiers.placeholderShown(createModifier: () -> Modifier) { + pseudoClass("placeholder-shown", createModifier) +} /** * Matches one or more UI elements that are the default among a set of elements. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:default */ -var ComponentModifiers.default: Modifier? - get() = pseudoClasses["default"] - set(value) = setPseudoClassModifier("default", value) +fun ComponentModifiers.default(createModifier: () -> Modifier) { + pseudoClass("default", createModifier) +} /** * * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:checked */ -var ComponentModifiers.checked: Modifier? - get() = pseudoClasses["checked"] - set(value) = setPseudoClassModifier("checked", value) +fun ComponentModifiers.checked(createModifier: () -> Modifier) { + pseudoClass("checked", createModifier) +} /** * Matches when elements such as checkboxes and radiobuttons are toggled on. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate */ -var ComponentModifiers.indeterminate: Modifier? - get() = pseudoClasses["indeterminate"] - set(value) = setPseudoClassModifier("indeterminate", value) +fun ComponentModifiers.indeterminate(createModifier: () -> Modifier) { + pseudoClass("indeterminate", createModifier) +} /** * Matches an element with valid contents. For example an input element with type 'email' which contains a validly formed email address. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:valid */ -var ComponentModifiers.valid: Modifier? - get() = pseudoClasses["valid"] - set(value) = setPseudoClassModifier("valid", value) +fun ComponentModifiers.valid(createModifier: () -> Modifier) { + pseudoClass("valid", createModifier) +} /** * Matches an element with invalid contents. For example an input element with type 'email' with a name entered. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid */ -var ComponentModifiers.invalid: Modifier? - get() = pseudoClasses["invalid"] - set(value) = setPseudoClassModifier("invalid", value) +fun ComponentModifiers.invalid(createModifier: () -> Modifier) { + pseudoClass("invalid", createModifier) +} /** * Applies to elements with range limitations, for example a slider control, when the selected value is in the allowed range. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:in-range */ -var ComponentModifiers.inRange: Modifier? - get() = pseudoClasses["in-range"] - set(value) = setPseudoClassModifier("in-range", value) +fun ComponentModifiers.inRange(createModifier: () -> Modifier) { + pseudoClass("in-range", createModifier) +} /** * Applies to elements with range limitations, for example a slider control, when the selected value is outside the @@ -201,36 +201,36 @@ var ComponentModifiers.inRange: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:out-of-range */ -var ComponentModifiers.outOfRange: Modifier? - get() = pseudoClasses["out-of-range"] - set(value) = setPseudoClassModifier("out-of-range", value) +fun ComponentModifiers.outOfRange(createModifier: () -> Modifier) { + pseudoClass("out-of-range", createModifier) +} /** * Matches when a form element is required. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:required */ -var ComponentModifiers.required: Modifier? - get() = pseudoClasses["required"] - set(value) = setPseudoClassModifier("required", value) +fun ComponentModifiers.required(createModifier: () -> Modifier) { + pseudoClass("required", createModifier) +} /** * Matches when a form element is optional. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:optional */ -var ComponentModifiers.optional: Modifier? - get() = pseudoClasses["optional"] - set(value) = setPseudoClassModifier("optional", value) +fun ComponentModifiers.optional(createModifier: () -> Modifier) { + pseudoClass("optional", createModifier) +} /** * Represents an element with incorrect input, but only when the user has interacted with it. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid */ -var ComponentModifiers.userInvalid: Modifier? - get() = pseudoClasses["user-invalid"] - set(value) = setPseudoClassModifier("user-invalid", value) +fun ComponentModifiers.userInvalid(createModifier: () -> Modifier) { + pseudoClass("user-invalid", createModifier) +} //endregion @@ -241,72 +241,72 @@ var ComponentModifiers.userInvalid: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:root */ -var ComponentModifiers.root: Modifier? - get() = pseudoClasses["root"] - set(value) = setPseudoClassModifier("root", value) +fun ComponentModifiers.root(createModifier: () -> Modifier) { + pseudoClass("root", createModifier) +} /** * Represents an element with no children other than white-space characters. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:empty */ -var ComponentModifiers.empty: Modifier? - get() = pseudoClasses["empty"] - set(value) = setPseudoClassModifier("empty", value) +fun ComponentModifiers.empty(createModifier: () -> Modifier) { + pseudoClass("empty", createModifier) +} /** * Matches an element that is the first of its siblings. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child */ -var ComponentModifiers.firstChild: Modifier? - get() = pseudoClasses["first-child"] - set(value) = setPseudoClassModifier("first-child", value) +fun ComponentModifiers.firstChild(createModifier: () -> Modifier) { + pseudoClass("first-child", createModifier) +} /** * Matches an element that is the last of its siblings. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child */ -var ComponentModifiers.lastChild: Modifier? - get() = pseudoClasses["last-child"] - set(value) = setPseudoClassModifier("last-child", value) +fun ComponentModifiers.lastChild(createModifier: () -> Modifier) { + pseudoClass("last-child", createModifier) +} /** * Matches an element that has no siblings. For example a list item with no other list items in that list. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child */ -var ComponentModifiers.onlyChild: Modifier? - get() = pseudoClasses["only-child"] - set(value) = setPseudoClassModifier("only-child", value) +fun ComponentModifiers.onlyChild(createModifier: () -> Modifier) { + pseudoClass("only-child", createModifier) +} /** * Matches an element that is the first of its siblings, and also matches a certain type selector. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type */ -var ComponentModifiers.firstOfType: Modifier? - get() = pseudoClasses["first-of-type"] - set(value) = setPseudoClassModifier("first-of-type", value) +fun ComponentModifiers.firstOfType(createModifier: () -> Modifier) { + pseudoClass("first-of-type", createModifier) +} /** * Matches an element that is the last of its siblings, and also matches a certain type selector. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type */ -var ComponentModifiers.lastOfType: Modifier? - get() = pseudoClasses["last-of-type"] - set(value) = setPseudoClassModifier("last-of-type", value) +fun ComponentModifiers.lastOfType(createModifier: () -> Modifier) { + pseudoClass("last-of-type", createModifier) +} /** * Matches an element that has no siblings of the chosen type selector. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type */ -var ComponentModifiers.onlyOfType: Modifier? - get() = pseudoClasses["only-of-type"] - set(value) = setPseudoClassModifier("only-of-type", value) +fun ComponentModifiers.onlyOfType(createModifier: () -> Modifier) { + pseudoClass("only-of-type", createModifier) +} //endregion @@ -319,73 +319,44 @@ var ComponentModifiers.onlyOfType: Modifier? * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/::before */ -var ComponentModifiers.before: Modifier? - get() = pseudoElements["before"] - set(value) = setPseudoElementModifier("before", value) +fun ComponentModifiers.before(createModifier: () -> Modifier) { + pseudoElement("before", createModifier) +} /** * Styles to apply to a virtual element that is created after the last element in some container. * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/::after */ -var ComponentModifiers.after: Modifier? - get() = pseudoElements["after"] - set(value) = setPseudoElementModifier("after", value) +fun ComponentModifiers.after(createModifier: () -> Modifier) { + pseudoElement("after", createModifier) +} /** * Styles to apply to the selected part of a document * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/::selection */ -var ComponentModifiers.selection: Modifier? - get() = pseudoElements["selection"] - set(value) = setPseudoElementModifier("selection", value) +fun ComponentModifiers.selection(createModifier: () -> Modifier) { + pseudoElement("selection", createModifier) +} /** * Styles to apply to the first letter in a block of text * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter */ -var ComponentModifiers.firstLetter: Modifier? - get() = pseudoElements["first-letter"] - set(value) = setPseudoElementModifier("first-letter", value) +fun ComponentModifiers.firstLetter(createModifier: () -> Modifier) { + pseudoElement("first-letter", createModifier) +} /** * Styles to apply to the first line in a block of text * * See also: https://developer.mozilla.org/en-US/docs/Web/CSS/::first-line */ -var ComponentModifiers.firstLine: Modifier? - get() = pseudoElements["first-line"] - set(value) = setPseudoElementModifier("first-line", value) - -//endregion - -//region Breakpoints - -/** Convenience property for adding a small [Breakpoint] */ -var ComponentModifiers.sm: Modifier? - get() = breakpoints[Breakpoint.SM] - set(value) = setBreakpointModifier(Breakpoint.SM, value) - -/** Convenience property for adding a medium [Breakpoint] */ -var ComponentModifiers.md: Modifier? - get() = breakpoints[Breakpoint.MD] - set(value) = setBreakpointModifier(Breakpoint.MD, value) - -/** Convenience property for adding a large [Breakpoint] */ -var ComponentModifiers.lg: Modifier? - get() = breakpoints[Breakpoint.LG] - set(value) = setBreakpointModifier(Breakpoint.LG, value) - -/** Convenience property for adding an extra-large [Breakpoint] */ -var ComponentModifiers.xl: Modifier? - get() = breakpoints[Breakpoint.XL] - set(value) = setBreakpointModifier(Breakpoint.XL, value) - -/** Convenience property for adding an extra-extra-large [Breakpoint] */ -var ComponentModifiers.xxl: Modifier? - get() = breakpoints[Breakpoint.XXL] - set(value) = setBreakpointModifier(Breakpoint.XXL, value) +fun ComponentModifiers.firstLine(createModifier: () -> Modifier) { + pseudoElement("first-line", createModifier) +} //endregion \ No newline at end of file diff --git a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/theme/SilkTheme.kt b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/theme/SilkTheme.kt index cf3f0fb94..c6be73ec1 100644 --- a/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/theme/SilkTheme.kt +++ b/frontend/kobweb-silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/theme/SilkTheme.kt @@ -116,8 +116,10 @@ class ImmutableSilkTheme(private val mutableSilkTheme: MutableSilkTheme) { // Note: We separate this function out from the SilkTheme constructor so we can construct it first and then call // this later. This allows ComponentStyles to reference SilkTheme in their logic, e.g. TextStyle: // val TextStyle = ComponentStyle("silk-text") { - // base = Modifier.color(SilkTheme.palettes[colorMode].color) - // ^^^^^^^^^ + // base { + // Modifier.color(SilkTheme.palettes[colorMode].color) + // ^^^^^^^^^ + // } // } // Silk must make sure to set the SilkTheme lateinit var (below) and then call this method right after internal fun registerStyles(componentStyleSheet: StyleSheet) {