From f35bd0c245ab352d2c90707b9f2e2d76a22a6c3f Mon Sep 17 00:00:00 2001 From: Brian Heston <47367562+bheston@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:49:02 -0700 Subject: [PATCH 1/2] Expand styling capabilities to not assume `:host` container --- examples/customize-component/src/index.ts | 2 +- packages/adaptive-ui/docs/api-report.md | 10 +++--- .../src/modules/element-styles-renderer.ts | 12 ++++++- packages/adaptive-ui/src/modules/selector.ts | 32 +++++++++++-------- packages/adaptive-ui/src/modules/types.ts | 32 ++++++++++++------- .../anchor/anchor.styles.modules.ts | 2 +- .../breadcrumb-item.styles.modules.ts | 6 ++-- .../checkbox/checkbox.styles.modules.ts | 2 +- .../components/checkbox/checkbox.template.ts | 2 +- .../data-grid-cell.styles.modules.ts | 4 +-- .../data-grid-cell/data-grid-cell.template.ts | 2 +- .../divider/divider.styles.modules.ts | 4 +-- .../components/flipper/flipper.template.ts | 2 +- .../listbox-option.styles.modules.ts | 2 +- .../components/listbox/listbox.template.ts | 2 +- .../menu-item/menu-item.template.ts | 2 +- .../picker-list-item.template.ts | 2 +- .../picker-menu-option.styles.modules.ts | 2 +- .../components/radio/radio.styles.modules.ts | 2 +- .../src/components/radio/radio.template.ts | 2 +- .../select/select.styles.modules.ts | 4 +-- .../src/components/select/select.template.ts | 2 +- .../skeleton/skeleton.styles.modules.ts | 4 +-- .../src/components/slider/slider.template.ts | 2 +- .../switch/switch.styles.modules.ts | 2 +- .../src/components/switch/switch.template.ts | 2 +- .../src/components/tab/tab.template.ts | 2 +- .../tree-item/tree-item.template.ts | 2 +- .../src/global.styles.modules.ts | 2 +- 29 files changed, 88 insertions(+), 60 deletions(-) diff --git a/examples/customize-component/src/index.ts b/examples/customize-component/src/index.ts index d3158445..9959a0af 100644 --- a/examples/customize-component/src/index.ts +++ b/examples/customize-component/src/index.ts @@ -63,7 +63,7 @@ const myCheckboxDefinition = composeCheckbox( }, { target: { - hostCondition: CheckboxAnatomy.conditions.checked, + contextCondition: CheckboxAnatomy.conditions.checked, part: CheckboxAnatomy.parts.control, }, styles: accentFillReadableControlStyles, diff --git a/packages/adaptive-ui/docs/api-report.md b/packages/adaptive-ui/docs/api-report.md index 64b04236..59c65018 100644 --- a/packages/adaptive-ui/docs/api-report.md +++ b/packages/adaptive-ui/docs/api-report.md @@ -101,6 +101,7 @@ export type ColorRecipeParams = { // @public export interface ComponentAnatomy { conditions: TConditions; + context?: string; focus?: FocusDefinition; interactivity?: InteractivityDefinition; parts: TParts; @@ -299,8 +300,8 @@ export const Fill: { // @public export const Focus: { - readonly hostFocused: () => FocusDefinition; - readonly hostChildFocused: (indicatorPart: keyof TParts & string) => FocusDefinition; + readonly contextFocused: () => FocusDefinition; + readonly contextChildFocused: (indicatorPart: keyof TParts & string) => FocusDefinition; readonly partFocused: (part: keyof TParts_1 & string) => FocusDefinition; readonly partWithin: (indicatorPart: keyof TParts_2 & string, focusablePart: keyof TParts_2 & string) => FocusDefinition; }; @@ -482,13 +483,14 @@ export type StyleModuleEvaluateParameters = StyleModuleTarget & InteractivityDef // @public export interface StyleModuleTarget { + context?: string; + contextCondition?: string; focusSelector?: FocusSelector; - hostCondition?: string; // @beta ignoreInteractivity?: boolean; part?: string; partCondition?: string; - stateOnHost?: boolean; + stateOnContext?: boolean; } // @public diff --git a/packages/adaptive-ui/src/modules/element-styles-renderer.ts b/packages/adaptive-ui/src/modules/element-styles-renderer.ts index 306bd22f..77ea2512 100644 --- a/packages/adaptive-ui/src/modules/element-styles-renderer.ts +++ b/packages/adaptive-ui/src/modules/element-styles-renderer.ts @@ -185,7 +185,17 @@ export class ElementStylesRenderer { public static renderStyleRules(baseStyles: ComposableStyles[] = [], styleRules: StyleRules, anatomy?: ComponentAnatomy) { for (const rule of styleRules) { const styles = Styles.fromDeclaration(rule); - const renderedStyles = new ElementStylesRenderer(styles).render(rule.target || {}, anatomy?.interactivity); + + // Transform the target selector if necessary + const target = rule.target || {}; + if (anatomy?.context && target.context === undefined) { + target.context = anatomy.context; + if (anatomy.context === target.part) { + target.part = undefined; + } + } + + const renderedStyles = new ElementStylesRenderer(styles).render(target, anatomy?.interactivity); baseStyles.push(renderedStyles); } diff --git a/packages/adaptive-ui/src/modules/selector.ts b/packages/adaptive-ui/src/modules/selector.ts index d765253b..b2cc81a5 100644 --- a/packages/adaptive-ui/src/modules/selector.ts +++ b/packages/adaptive-ui/src/modules/selector.ts @@ -1,5 +1,9 @@ import type { StateSelector, StyleModuleEvaluateParameters } from "./types.js"; +const HOST_CONTEXT = ":host"; + +const defaultContext = HOST_CONTEXT; + /** * Creates a single css selector for the provided `params` and optional `state`. * @@ -14,35 +18,37 @@ export function makeSelector(params: StyleModuleEvaluateParameters, state?: Stat // `disabled` is a `state`, but it's not a css pseudo selector. const statePseudo = state && state !== "disabled" ? ":" + state : ""; + const context = params.context && params.context !== defaultContext ? `.${params.context}` : defaultContext; - if (params.hostCondition || + if (params.contextCondition || (state && state !== "disabled" && params.interactivitySelector !== undefined) || (state && state === "disabled" && params.disabledSelector !== undefined) ) { - // Start with any base host condition like `[appearance='accent']`. - let hostCondition = params.hostCondition || ""; + // Start with any base context element condition like `[appearance='accent']`. + let contextCondition = params.contextCondition || ""; if (state) { if (state !== "disabled") { // Add any interactive condition like `:not([disabled])`. - hostCondition += (params.interactivitySelector || ""); + contextCondition += (params.interactivitySelector || ""); - // If this is not targeting a part, or if configured, apply the state at the `:host`. - if (!params.part || params.stateOnHost === true) { - hostCondition += statePseudo; + // If this is not targeting a part, or if configured, apply the state on the context element. + if (!params.part || params.stateOnContext === true) { + contextCondition += statePseudo; } } else { // Add the non-interactive condition like `[disabled]`. - hostCondition += (params.disabledSelector || ""); + contextCondition += (params.disabledSelector || ""); } } - if (hostCondition !== "") { - selectors.push(`:host(${hostCondition})`); + if (contextCondition !== "") { + const contextSelector = context === HOST_CONTEXT ? `${HOST_CONTEXT}(${contextCondition})` : `${context}${contextCondition}`; + selectors.push(contextSelector); } } else if (!params.part) { - // There wasn't a host condition, and there isn't a part, so basic host selector. - selectors.push(":host"); + // There wasn't a context condition, and there isn't a part, so basic context element selector. + selectors.push(context); } if (params.part) { @@ -50,7 +56,7 @@ export function makeSelector(params: StyleModuleEvaluateParameters, state?: Stat selectors.push("*"); } else { // Using class selector notation for now. - selectors.push(`.${params.part}${params.partCondition || ""}${params.stateOnHost !== true ? statePseudo : ""}`); + selectors.push(`.${params.part}${params.partCondition || ""}${params.stateOnContext !== true ? statePseudo : ""}`); } } const ret = selectors.join(" "); diff --git a/packages/adaptive-ui/src/modules/types.ts b/packages/adaptive-ui/src/modules/types.ts index 7436917d..8f945809 100644 --- a/packages/adaptive-ui/src/modules/types.ts +++ b/packages/adaptive-ui/src/modules/types.ts @@ -35,6 +35,11 @@ export type ComponentParts = Record; * @public */ export interface ComponentAnatomy { + /** + * The context element selector. Implementation defaults to `:host` if not provided. + */ + context?: string; + /** * Description of the conditions for when the component is interactive or not. */ @@ -63,15 +68,20 @@ export interface ComponentAnatomy { */ export const Focus = { /** - * The simple case of the host element accepting and indicating focus. + * The simple case of the context element accepting and indicating focus. */ - hostFocused: () => { + contextFocused: () => { return { focusTarget: { ignoreInteractivity: true, @@ -201,14 +211,14 @@ export const Focus = { }, /** - * The host has focus, but a child element is the indicator. + * The context element has focus, but a child element is the indicator. * * @param indicatorPart - The part name of where to indicate focus. */ - hostChildFocused: (indicatorPart: keyof TParts & string) => { + contextChildFocused: (indicatorPart: keyof TParts & string) => { return { focusTarget: { - stateOnHost: true, + stateOnContext: true, part: indicatorPart, ignoreInteractivity: true, }, diff --git a/packages/adaptive-web-components/src/components/anchor/anchor.styles.modules.ts b/packages/adaptive-web-components/src/components/anchor/anchor.styles.modules.ts index 996d5c9f..f10a372d 100644 --- a/packages/adaptive-web-components/src/components/anchor/anchor.styles.modules.ts +++ b/packages/adaptive-web-components/src/components/anchor/anchor.styles.modules.ts @@ -29,7 +29,7 @@ export const styleModules: StyleRules = [ }, { target : { - hostCondition: AnchorAnatomy.conditions.noHref, + contextCondition: AnchorAnatomy.conditions.noHref, part: AnchorAnatomy.parts.control, }, styles: neutralForegroundStrongElementStyles, diff --git a/packages/adaptive-web-components/src/components/breadcrumb-item/breadcrumb-item.styles.modules.ts b/packages/adaptive-web-components/src/components/breadcrumb-item/breadcrumb-item.styles.modules.ts index 50f2069c..4d2be508 100644 --- a/packages/adaptive-web-components/src/components/breadcrumb-item/breadcrumb-item.styles.modules.ts +++ b/packages/adaptive-web-components/src/components/breadcrumb-item/breadcrumb-item.styles.modules.ts @@ -30,14 +30,14 @@ export const styleModules: StyleRules = [ }, { target : { - hostCondition: BreadcrumbItemAnatomy.conditions.noHref, + contextCondition: BreadcrumbItemAnatomy.conditions.noHref, part: BreadcrumbItemAnatomy.parts.control, }, styles: neutralForegroundStrongElementStyles, }, { target : { - hostCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector, + contextCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector, part: BreadcrumbItemAnatomy.parts.control, partCondition: BreadcrumbItemAnatomy.conditions.current, }, @@ -45,7 +45,7 @@ export const styleModules: StyleRules = [ }, { target : { - hostCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector, + contextCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector, part: BreadcrumbItemAnatomy.parts.control, }, styles: accentForegroundReadableControlStyles, diff --git a/packages/adaptive-web-components/src/components/checkbox/checkbox.styles.modules.ts b/packages/adaptive-web-components/src/components/checkbox/checkbox.styles.modules.ts index 875092b0..a4dd3e48 100644 --- a/packages/adaptive-web-components/src/components/checkbox/checkbox.styles.modules.ts +++ b/packages/adaptive-web-components/src/components/checkbox/checkbox.styles.modules.ts @@ -32,7 +32,7 @@ export const styleModules: StyleRules = [ }, { target : { - hostCondition: CheckboxAnatomy.conditions.checked, + contextCondition: CheckboxAnatomy.conditions.checked, part: CheckboxAnatomy.parts.control, }, styles: selectableSelectedStyles, diff --git a/packages/adaptive-web-components/src/components/checkbox/checkbox.template.ts b/packages/adaptive-web-components/src/components/checkbox/checkbox.template.ts index 6b951054..7c978cf5 100644 --- a/packages/adaptive-web-components/src/components/checkbox/checkbox.template.ts +++ b/packages/adaptive-web-components/src/components/checkbox/checkbox.template.ts @@ -42,7 +42,7 @@ export const CheckboxAnatomy: ComponentAnatomy interactivity: Interactivity.disabledAttribute, conditions: TabConditions, parts: TabParts, - focus: Focus.hostFocused(), + focus: Focus.contextFocused(), }; /** diff --git a/packages/adaptive-web-components/src/components/tree-item/tree-item.template.ts b/packages/adaptive-web-components/src/components/tree-item/tree-item.template.ts index 818767e0..bc022ebf 100644 --- a/packages/adaptive-web-components/src/components/tree-item/tree-item.template.ts +++ b/packages/adaptive-web-components/src/components/tree-item/tree-item.template.ts @@ -42,7 +42,7 @@ export const TreeItemAnatomy: ComponentAnatomy): StyleRul styles.push( { target : { - hostCondition: anatomy.interactivity.disabledSelector, + contextCondition: anatomy.interactivity.disabledSelector, part: "*", }, styles: disabledStyles, From 6b8815dcb9f40270202bd19211e177c7a39fd4d4 Mon Sep 17 00:00:00 2001 From: Brian Heston <47367562+bheston@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:59:05 -0700 Subject: [PATCH 2/2] Change files --- ...b-adaptive-ui-3ea279c0-91e4-47db-8015-8b43925b9c60.json | 7 +++++++ ...eb-components-fee5fd32-8612-44e2-9b82-07a9b21b20e4.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@adaptive-web-adaptive-ui-3ea279c0-91e4-47db-8015-8b43925b9c60.json create mode 100644 change/@adaptive-web-adaptive-web-components-fee5fd32-8612-44e2-9b82-07a9b21b20e4.json diff --git a/change/@adaptive-web-adaptive-ui-3ea279c0-91e4-47db-8015-8b43925b9c60.json b/change/@adaptive-web-adaptive-ui-3ea279c0-91e4-47db-8015-8b43925b9c60.json new file mode 100644 index 00000000..93e51cdc --- /dev/null +++ b/change/@adaptive-web-adaptive-ui-3ea279c0-91e4-47db-8015-8b43925b9c60.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Expand styling capabilities to not assume `:host` container", + "packageName": "@adaptive-web/adaptive-ui", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@adaptive-web-adaptive-web-components-fee5fd32-8612-44e2-9b82-07a9b21b20e4.json b/change/@adaptive-web-adaptive-web-components-fee5fd32-8612-44e2-9b82-07a9b21b20e4.json new file mode 100644 index 00000000..a76c2b0f --- /dev/null +++ b/change/@adaptive-web-adaptive-web-components-fee5fd32-8612-44e2-9b82-07a9b21b20e4.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Expand styling capabilities to not assume `:host` container", + "packageName": "@adaptive-web/adaptive-web-components", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +}