Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand styling capabilities to not assume :host container #184

Merged
merged 2 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Expand styling capabilities to not assume `:host` container",
"packageName": "@adaptive-web/adaptive-ui",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Expand styling capabilities to not assume `:host` container",
"packageName": "@adaptive-web/adaptive-web-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion examples/customize-component/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const myCheckboxDefinition = composeCheckbox(
},
{
target: {
hostCondition: CheckboxAnatomy.conditions.checked,
contextCondition: CheckboxAnatomy.conditions.checked,
part: CheckboxAnatomy.parts.control,
},
styles: accentFillReadableControlStyles,
Expand Down
10 changes: 6 additions & 4 deletions packages/adaptive-ui/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export type ColorRecipeParams = {
// @public
export interface ComponentAnatomy<TConditions extends ComponentConditions, TParts extends ComponentParts> {
conditions: TConditions;
context?: string;
focus?: FocusDefinition<TParts>;
interactivity?: InteractivityDefinition;
parts: TParts;
Expand Down Expand Up @@ -299,8 +300,8 @@ export const Fill: {

// @public
export const Focus: {
readonly hostFocused: () => FocusDefinition<any>;
readonly hostChildFocused: <TParts>(indicatorPart: keyof TParts & string) => FocusDefinition<TParts>;
readonly contextFocused: () => FocusDefinition<any>;
readonly contextChildFocused: <TParts>(indicatorPart: keyof TParts & string) => FocusDefinition<TParts>;
readonly partFocused: <TParts_1>(part: keyof TParts_1 & string) => FocusDefinition<TParts_1>;
readonly partWithin: <TParts_2>(indicatorPart: keyof TParts_2 & string, focusablePart: keyof TParts_2 & string) => FocusDefinition<TParts_2>;
};
Expand Down Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion packages/adaptive-ui/src/modules/element-styles-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,17 @@ export class ElementStylesRenderer {
public static renderStyleRules(baseStyles: ComposableStyles[] = [], styleRules: StyleRules, anatomy?: ComponentAnatomy<any, any>) {
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);
}

Expand Down
32 changes: 19 additions & 13 deletions packages/adaptive-ui/src/modules/selector.ts
Original file line number Diff line number Diff line change
@@ -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`.
*
Expand All @@ -14,43 +18,45 @@ 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) {
if (params.part === "*") {
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(" ");
Expand Down
32 changes: 21 additions & 11 deletions packages/adaptive-ui/src/modules/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export type ComponentParts = Record<string, string>;
* @public
*/
export interface ComponentAnatomy<TConditions extends ComponentConditions, TParts extends ComponentParts> {
/**
* 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.
*/
Expand Down Expand Up @@ -63,23 +68,28 @@ export interface ComponentAnatomy<TConditions extends ComponentConditions, TPart
*/
export interface StyleModuleTarget {
/**
* The condition to match at the host element level.
* The selector for the context element. Implementation defaults to `:host` if not provided.
*/
context?: string;

/**
* The condition to match on the context element.
*/
hostCondition?: string;
contextCondition?: string;

/**
* Normally the state applies to the host, or if specified, the part. This option forces the state to the
* apply to the host, useful for styling the host state in only a portion of the child elements.
* Normally the state applies to the context element, or if specified, the part. This option forces the state to the
* apply to the context element, useful for styling the context element state in only a portion of the child elements.
*/
stateOnHost?: boolean;
stateOnContext?: boolean;

/**
* The component part name to apply this style module.
*/
part?: string;

/**
* The condition to match at the part element level.
* The condition to match on the part element.
*/
partCondition?: string;

Expand Down Expand Up @@ -190,9 +200,9 @@ export interface FocusDefinition<TParts> {
*/
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,
Expand All @@ -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: <TParts>(indicatorPart: keyof TParts & string) => {
contextChildFocused: <TParts>(indicatorPart: keyof TParts & string) => {
return {
focusTarget: {
stateOnHost: true,
stateOnContext: true,
part: indicatorPart,
ignoreInteractivity: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: AnchorAnatomy.conditions.noHref,
contextCondition: AnchorAnatomy.conditions.noHref,
part: AnchorAnatomy.parts.control,
},
styles: neutralForegroundStrongElementStyles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ 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,
},
styles: neutralForegroundStrongElementStyles,
},
{
target : {
hostCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector,
contextCondition: BreadcrumbItemAnatomy.interactivity?.interactivitySelector,
part: BreadcrumbItemAnatomy.parts.control,
},
styles: accentForegroundReadableControlStyles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: CheckboxAnatomy.conditions.checked,
contextCondition: CheckboxAnatomy.conditions.checked,
part: CheckboxAnatomy.parts.control,
},
styles: selectableSelectedStyles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const CheckboxAnatomy: ComponentAnatomy<typeof CheckboxConditions, typeof
interactivity: Interactivity.disabledAttribute,
conditions: CheckboxConditions,
parts: CheckboxParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: DataGridCellAnatomy.conditions.cellTypeColumnHeader,
contextCondition: DataGridCellAnatomy.conditions.cellTypeColumnHeader,
},
styles: labelTextStyles,
},
{
target : {
hostCondition: DataGridCellAnatomy.conditions.cellTypeRowHeader,
contextCondition: DataGridCellAnatomy.conditions.cellTypeRowHeader,
},
styles: labelTextStyles,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const DataGridCellAnatomy: ComponentAnatomy<typeof DataGridCellConditions
interactivity: Interactivity.never,
conditions: DataGridCellConditions,
parts: DataGridCellParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { DividerAnatomy } from "./divider.template.js";
export const styleModules: StyleRules = [
{
target : {
hostCondition: DividerAnatomy.conditions.horizontal,
contextCondition: DividerAnatomy.conditions.horizontal,
},
properties: {
borderFillTop: neutralStrokeSubtle.rest,
Expand All @@ -20,7 +20,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: DividerAnatomy.conditions.vertical,
contextCondition: DividerAnatomy.conditions.vertical,
},
properties: {
borderFillLeft: neutralStrokeSubtle.rest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const FlipperAnatomy: ComponentAnatomy<typeof FlipperConditions, typeof F
interactivity: Interactivity.disabledAttribute,
conditions: FlipperConditions,
parts: FlipperParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: ListboxOptionAnatomy.conditions.selected,
contextCondition: ListboxOptionAnatomy.conditions.selected,
},
styles: selectableSelectedStyles,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const ListboxAnatomy: ComponentAnatomy<typeof ListboxConditions, typeof L
interactivity: Interactivity.disabledAttribute,
conditions: ListboxConditions,
parts: ListboxParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const MenuItemAnatomy: ComponentAnatomy<typeof MenuItemConditions, typeof
interactivity: Interactivity.disabledAttribute,
conditions: MenuItemConditions,
parts: MenuItemParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

// TODO: Temporary copy of template until https://github.com/microsoft/fast/pull/6286/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const PickerListItemAnatomy: ComponentAnatomy<typeof PickerListItemCondit
interactivity: Interactivity.always,
conditions: PickerListItemConditions,
parts: PickerListItemParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: PickerMenuOptionAnatomy.conditions.selected,
contextCondition: PickerMenuOptionAnatomy.conditions.selected,
},
styles: highlightFillReadableControlStyles,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const styleModules: StyleRules = [
},
{
target : {
hostCondition: RadioAnatomy.conditions.checked,
contextCondition: RadioAnatomy.conditions.checked,
part: RadioAnatomy.parts.control,
},
styles: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const RadioAnatomy: ComponentAnatomy<typeof RadioConditions, typeof Radio
interactivity: Interactivity.disabledAttribute,
conditions: RadioConditions,
parts: RadioParts,
focus: Focus.hostFocused(),
focus: Focus.contextFocused(),
};

/**
Expand Down
Loading
Loading