From 97e753464f623c2b9cd458ecc5f2c55961da9095 Mon Sep 17 00:00:00 2001 From: Brian Heston <47367562+bheston@users.noreply.github.com> Date: Tue, 18 Oct 2022 13:53:38 -0700 Subject: [PATCH] Adaptive UI Explorer - initial port (#6424) Initial port of Color Explorer into the new Adaptive UI Explorer component --- ...-ba5b0bf9-7335-4d14-a6ce-140f265d1674.json | 7 + package.json | 1 + .../adaptive-ui-explorer/.eslintignore | 10 + .../adaptive-ui-explorer/.eslintrc.json | 6 + .../tooling/adaptive-ui-explorer/.gitignore | 3 + .../tooling/adaptive-ui-explorer/.npmignore | 7 + packages/tooling/adaptive-ui-explorer/.npmrc | 1 + .../adaptive-ui-explorer/.prettierignore | 3 + .../tooling/adaptive-ui-explorer/README.md | 0 .../tooling/adaptive-ui-explorer/package.json | 50 ++ .../tooling/adaptive-ui-explorer/src/app.ts | 349 +++++++++++ .../adaptive-ui-explorer/src/colors.ts | 25 + .../src/component-type.ts | 6 + .../src/components/adaptive-component.ts | 117 ++++ .../src/components/color-block.ts | 558 ++++++++++++++++++ .../control-pane/control-pane.styles.ts | 38 ++ .../control-pane/control-pane.template.ts | 79 +++ .../components/control-pane/control-pane.ts | 19 + .../src/components/control-pane/index.ts | 11 + .../src/components/index.ts | 8 + .../src/components/layer-background/index.ts | 131 ++++ .../src/components/palette-gradient/index.ts | 3 + .../palette-gradient.styles.ts | 38 ++ .../palette-gradient.template.ts | 32 + .../palette-gradient/palette-gradient.ts | 21 + .../src/components/sample-app/index.ts | 9 + .../sample-app/sample-app.styles.ts | 179 ++++++ .../sample-app/sample-app.template.ts | 118 ++++ .../src/components/sample-app/sample-app.ts | 3 + .../src/components/sample-page/index.ts | 9 + .../sample-page/sample-page.styles.ts | 137 +++++ .../sample-page/sample-page.template.ts | 122 ++++ .../src/components/sample-page/sample-page.ts | 3 + .../src/components/swatch.ts | 217 +++++++ .../tooling/adaptive-ui-explorer/src/index.ts | 3 + .../adaptive-ui-explorer/tsconfig.json | 17 + .../adaptive-ui-explorer/webpack.config.cjs | 56 ++ yarn.lock | 21 +- 38 files changed, 2416 insertions(+), 1 deletion(-) create mode 100644 change/@microsoft-adaptive-ui-explorer-ba5b0bf9-7335-4d14-a6ce-140f265d1674.json create mode 100644 packages/tooling/adaptive-ui-explorer/.eslintignore create mode 100644 packages/tooling/adaptive-ui-explorer/.eslintrc.json create mode 100644 packages/tooling/adaptive-ui-explorer/.gitignore create mode 100644 packages/tooling/adaptive-ui-explorer/.npmignore create mode 100644 packages/tooling/adaptive-ui-explorer/.npmrc create mode 100644 packages/tooling/adaptive-ui-explorer/.prettierignore create mode 100644 packages/tooling/adaptive-ui-explorer/README.md create mode 100644 packages/tooling/adaptive-ui-explorer/package.json create mode 100644 packages/tooling/adaptive-ui-explorer/src/app.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/colors.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/component-type.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/adaptive-component.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/color-block.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.styles.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/control-pane/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/layer-background/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.styles.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.template.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-app/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.styles.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.template.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-page/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.styles.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.template.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/components/swatch.ts create mode 100644 packages/tooling/adaptive-ui-explorer/src/index.ts create mode 100644 packages/tooling/adaptive-ui-explorer/tsconfig.json create mode 100644 packages/tooling/adaptive-ui-explorer/webpack.config.cjs diff --git a/change/@microsoft-adaptive-ui-explorer-ba5b0bf9-7335-4d14-a6ce-140f265d1674.json b/change/@microsoft-adaptive-ui-explorer-ba5b0bf9-7335-4d14-a6ce-140f265d1674.json new file mode 100644 index 00000000000..ece55901e53 --- /dev/null +++ b/change/@microsoft-adaptive-ui-explorer-ba5b0bf9-7335-4d14-a6ce-140f265d1674.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Initial port of Color Explorer into the new Adaptive UI Explorer component", + "packageName": "@microsoft/adaptive-ui-explorer", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "prerelease" +} diff --git a/package.json b/package.json index 39ed87c13ae..071d06b2692 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "private": true, "workspaces": { "packages": [ + "packages/tooling/adaptive-ui-explorer", "packages/utilities/*", "packages/web-components/*", "sites/fast-color-explorer", diff --git a/packages/tooling/adaptive-ui-explorer/.eslintignore b/packages/tooling/adaptive-ui-explorer/.eslintignore new file mode 100644 index 00000000000..6fde7dc2651 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.eslintignore @@ -0,0 +1,10 @@ +# don't ever lint node_modules +node_modules +# don't lint build output (make sure it's set to your correct build folder name) +dist +# don't lint coverage output +coverage +# don't lint www +www +# don't lint test files +__test__ diff --git a/packages/tooling/adaptive-ui-explorer/.eslintrc.json b/packages/tooling/adaptive-ui-explorer/.eslintrc.json new file mode 100644 index 00000000000..42b6e48bec2 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": ["@microsoft/eslint-config-fast-dna", "prettier"], + "rules": { + "@typescript-eslint/no-non-null-assertion": "off" + } +} diff --git a/packages/tooling/adaptive-ui-explorer/.gitignore b/packages/tooling/adaptive-ui-explorer/.gitignore new file mode 100644 index 00000000000..55e944cd3e6 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.gitignore @@ -0,0 +1,3 @@ +tsdoc-metadata.json +temp +test diff --git a/packages/tooling/adaptive-ui-explorer/.npmignore b/packages/tooling/adaptive-ui-explorer/.npmignore new file mode 100644 index 00000000000..9625030161b --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.npmignore @@ -0,0 +1,7 @@ +# Tests +__test__/ +*.spec.* +*.test.* + +# Source files +src/ diff --git a/packages/tooling/adaptive-ui-explorer/.npmrc b/packages/tooling/adaptive-ui-explorer/.npmrc new file mode 100644 index 00000000000..43c97e719a5 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/tooling/adaptive-ui-explorer/.prettierignore b/packages/tooling/adaptive-ui-explorer/.prettierignore new file mode 100644 index 00000000000..60aa645faec --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/.prettierignore @@ -0,0 +1,3 @@ +dist/* +test/* +src/__test__/* diff --git a/packages/tooling/adaptive-ui-explorer/README.md b/packages/tooling/adaptive-ui-explorer/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/tooling/adaptive-ui-explorer/package.json b/packages/tooling/adaptive-ui-explorer/package.json new file mode 100644 index 00000000000..66050c6d757 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/package.json @@ -0,0 +1,50 @@ +{ + "name": "@microsoft/adaptive-ui-explorer", + "version": "1.0.0-alpha.1", + "description": "A playground for Adaptive UI", + "type": "module", + "private": true, + "main": "dist/esm/index.js", + "types": "dist/adaptive-ui-explorer.d.ts", + "unpkg": "dist/esm/index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/microsoft/fast.git", + "directory": "packages/utilities/adaptive-ui" + }, + "author": { + "name": "Microsoft", + "url": "https://discord.gg/FcSNfg4" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/microsoft/fast/issues/new/choose" + }, + "homepage": "https://fast.design", + "scripts": { + "build": "tsc -p ./tsconfig.json", + "build:app": "webpack --mode=production", + "clean": "node ../../../build/clean.js dist", + "prepare": "yarn clean && yarn build", + "prettier": "prettier --config ../../../.prettierrc --write \"**/*.ts\"", + "prettier:diff": "prettier --config ../../../.prettierrc \"**/*.ts\" --list-different", + "start": "webpack-dev-server" + }, + "dependencies": { + "@microsoft/adaptive-ui": "^1.0.0-alpha.2", + "@microsoft/fast-colors": "^5.3.1", + "@microsoft/fast-element": "^2.0.0-beta.6", + "@microsoft/fast-foundation": "^3.0.0-alpha.9" + }, + "devDependencies": { + "clean-webpack-plugin": "^4.0.0", + "html-webpack-plugin": "^5.5.0", + "inject-body-webpack-plugin": "^1.3.0", + "resolve-typescript-plugin": "^1.2.0", + "ts-loader": "^9.3.0", + "typescript": "^4.7.3", + "webpack": "^5.73.0", + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.9.2" + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/app.ts b/packages/tooling/adaptive-ui-explorer/src/app.ts new file mode 100644 index 00000000000..348d20c21fa --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/app.ts @@ -0,0 +1,349 @@ +import { + attr, + css, + customElement, + FASTElement, + html, + observable, + ref, + repeat, + ViewTemplate, +} from "@microsoft/fast-element"; +import { DesignToken } from "@microsoft/fast-foundation"; +import { + accentBaseColor, + accentPalette, + LayerBaseLuminance, + layerFillBaseLuminance, + layerFillFixedBase, + layerFillFixedMinus1, + layerFillFixedMinus2, + layerFillFixedMinus3, + layerFillFixedMinus4, + layerFillFixedPlus1, + neutralBaseColor, + neutralPalette, + Palette, + Swatch, + SwatchRGB, +} from "@microsoft/adaptive-ui"; +import { ComponentType } from "./component-type.js"; +import "./components/color-block.js"; +import "./components/control-pane/index.js"; +import "./components/layer-background/index.js"; +import "./components/palette-gradient/palette-gradient.js"; +import "./components/sample-app/sample-app.js"; + +const sampleTemplate = html` + + + + + + + + +`; + +const colorBlockTemplate = html` + ${repeat( + x => x.backgrounds, + html` + + ` + )} +`; + +const template = html` +
+
+ + +
+
+ +
+
+ +
+
+
+ ${x => x.componentTypeTemplate()} +
+
+
+
+
+ + + +
+
+
+`; + +const styles = css` + :host { + width: 100%; + } + + .container { + display: flex; + flex-direction: column; + } + + .container.fill { + width: 100%; + height: 100%; + } + + .row { + position: relative; + display: flex; + flex-direction: row; + flex-basis: auto; + } + + .row.fill { + flex: 1; + overflow: hidden; + } + + .canvas { + min-width: 300px; + flex-grow: 1; + } + + .gradient { + height: 20px; + } + + .control-pane-container { + height: 100%; + z-index: 1; + padding: 40px; + position: relative; + overflow: auto; + width: 320px; + box-sizing: border-box; + } + + app-color-block { + min-width: 400px; + } +`; + +export interface SwatchInfo { + index: number; + color: string; + title?: string; +} + +@customElement({ + name: `app-design-system-provider`, + template: html` + + `, +}) +class DesignSystemProvider extends FASTElement {} +DesignSystemProvider; + +export interface AppAttributes { + componentType: ComponentType; + neutralColor: string; + accentColor: string; + showOnlyLayerBackgrounds: boolean; +} + +@customElement({ + name: "app-main", + template, + styles, +}) +export class App extends FASTElement implements AppAttributes { + canvas: DesignSystemProvider; + + @attr({ attribute: "component-type" }) + componentType: ComponentType = ComponentType.backplate; + + @attr({ attribute: "neutral-color" }) + neutralColor: string; + private neutralColorChanged(prev?: string, next?: string) { + if (this.$fastController.isConnected && next) { + neutralBaseColor.setValueFor(this.canvas, next); + + this.neutralPalette = neutralPalette.getValueFor(this.canvas); + this.neutralColors = this.neutralPalette.swatches.map((x: SwatchRGB) => + x.toColorString() + ); + + this.updateBackgrounds(); + } + } + + @observable + neutralPalette: Palette; + + @observable + neutralColors: string[] = []; + + @attr({ attribute: "accent-color" }) + accentColor: string; + private accentColorChanged(prev?: string, next?: string) { + if (this.$fastController.isConnected && next) { + accentBaseColor.setValueFor(this.canvas, next); + + this.accentPalette = accentPalette.getValueFor(this.canvas); + } + } + + @observable + accentPalette: Palette; + + @observable + showOnlyLayerBackgrounds: boolean = true; + private showOnlyLayerBackgroundsChanged() { + if (this.$fastController.isConnected) { + this.updateBackgrounds(); + } + } + + @observable + backgrounds: SwatchInfo[]; + + connectedCallback() { + super.connectedCallback(); + this.neutralColor = "#808080"; + this.accentColor = "#0078d4"; + } + + designSystemElement: FASTElement; + + componentTypeTemplate(): ViewTemplate { + // if (this.componentType === ComponentType.sample) { + // return sampleTemplate; + // } else { + return colorBlockTemplate; + // } + } + + private updateBackgrounds(): void { + const layers: SwatchInfo[] = this.lightModeLayers.concat(this.darkModeLayers); + + this.backgrounds = this.showOnlyLayerBackgrounds + ? layers + : this.neutralColors.map( + (color: string, index: number): SwatchInfo => { + const neutralLayerIndex: number = layers.findIndex( + (config: SwatchInfo): boolean => config.color === color + ); + + return { + index, + color, + title: + neutralLayerIndex !== -1 + ? layers[neutralLayerIndex].title + : undefined, + }; + } + ); + } + + private layerTokens: Array<[DesignToken, string]> = [ + [layerFillFixedPlus1, "+1"], + [layerFillFixedBase, "Base"], + [layerFillFixedMinus1, "-1"], + [layerFillFixedMinus2, "-2"], + [layerFillFixedMinus3, "-3"], + [layerFillFixedMinus4, "-4"], + ]; + + private resolveLayerRecipes = (luminance: number): SwatchInfo[] => { + layerFillBaseLuminance.setValueFor(this.designSystemElement, luminance); + + return this.layerTokens + .map( + (conf: [DesignToken, string]): SwatchInfo => { + const color = conf[0] + .getValueFor(this.designSystemElement) + .toColorString(); + return { + index: this.neutralColors.indexOf(color), + color: color, + title: conf[1], + }; + } + ) + .reduce((accumulated: SwatchInfo[], value: SwatchInfo): Array => { + const colorIndex: number = accumulated.findIndex( + (config: SwatchInfo): boolean => config.color === value.color + ); + + return colorIndex === -1 + ? accumulated.concat(value) + : accumulated.map( + (config: SwatchInfo, index: number): SwatchInfo => + index === colorIndex + ? { + index: this.neutralColors.indexOf(value.color), + color: value.color, + title: value.title!.concat(", ", config.title!), + } + : config + ); + }, []) + .sort((a: SwatchInfo, b: SwatchInfo): number => a.index - b.index); + }; + + private get lightModeLayers(): SwatchInfo[] { + return this.resolveLayerRecipes(LayerBaseLuminance.LightMode); + } + + private get darkModeLayers(): SwatchInfo[] { + return this.resolveLayerRecipes(LayerBaseLuminance.DarkMode); + } + + controlPaneHandler(e: CustomEvent) { + const detail: { field: string; value: any } = e.detail; + (this as any)[detail.field] = detail.value; + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/colors.ts b/packages/tooling/adaptive-ui-explorer/src/colors.ts new file mode 100644 index 00000000000..e04324c7940 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/colors.ts @@ -0,0 +1,25 @@ +export enum AccentColor { + black = "#000000", + white = "#FFFFFF", + magenta = "#DA1A5F", + blue = "#0078D4", + green = "#107C10", + purple = "#5C2D91", + orange = "#D83B01", + yellow = "#F2C812", +} + +export const neutralColors: string[] = [ + "#808080", + "#73818C", + "#718E71", + "#7F738C", + "#8C7A73", + "#0078D4", + "#107C10", + "#5C2D91", + "#D83B01", +]; + +export const defaultAccentColor: string = AccentColor.blue; +export const defaultNeutralColor: string = neutralColors[0]; diff --git a/packages/tooling/adaptive-ui-explorer/src/component-type.ts b/packages/tooling/adaptive-ui-explorer/src/component-type.ts new file mode 100644 index 00000000000..65833bb2a76 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/component-type.ts @@ -0,0 +1,6 @@ +export enum ComponentType { + backplate = "backplate", + text = "text", + form = "form", + // sample = "sample", +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/adaptive-component.ts b/packages/tooling/adaptive-ui-explorer/src/components/adaptive-component.ts new file mode 100644 index 00000000000..d1b51b75294 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/adaptive-component.ts @@ -0,0 +1,117 @@ +import { + css, + customElement, + ElementViewTemplate, + FASTElement, + html, + observable, +} from "@microsoft/fast-element"; +import { CSSDesignToken } from "@microsoft/fast-foundation"; +import { Swatch } from "@microsoft/adaptive-ui"; + +// why this component is different + +function template(): ElementViewTemplate { + return html` + + `; +} + +const styles = css` + :host { + display: flex; + align-items: center; + padding: 6px 12px; + box-sizing: border-box; + font-size: 14px; + justify-items: start; + border: 1px solid transparent; + border-radius: 4px; + cursor: pointer; + } + + :host { + background: var(--ac-fill-rest); + border-color: var(--ac-stroke-rest) !important; + color: var(--ac-foreground-rest); + } + + :host(:hover) { + background: var(--ac-fill-hover); + border-color: var(--ac-stroke-hover) !important; + color: var(--ac-foreground-hover); + } + + :host(:active) { + background: var(--ac-fill-active); + border-color: var(--ac-stroke-active) !important; + color: var(--ac-foreground-active); + } + + :host(:focus) { + background: var(--ac-fill-focus); + border-color: var(--ac-stroke-focus) !important; + color: var(--ac-foreground-focus); + } +`; + +@customElement({ + name: "app-adaptive-component", + template: template(), + styles, +}) +export class AdaptiveComponent extends FASTElement { + @observable + public fillRest?: CSSDesignToken; + + @observable + public fillHover?: CSSDesignToken; + + @observable + public fillActive?: CSSDesignToken; + + @observable + public fillFocus?: CSSDesignToken; + + @observable + public strokeRest?: CSSDesignToken; + + @observable + public strokeHover?: CSSDesignToken; + + @observable + public strokeActive?: CSSDesignToken; + + @observable + public strokeFocus?: CSSDesignToken; + + @observable + public foregroundRest?: CSSDesignToken; + + @observable + public foregroundHover?: CSSDesignToken; + + @observable + public foregroundActive?: CSSDesignToken; + + @observable + public foregroundFocus?: CSSDesignToken; +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/color-block.ts b/packages/tooling/adaptive-ui-explorer/src/components/color-block.ts new file mode 100644 index 00000000000..7ff6d05e634 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/color-block.ts @@ -0,0 +1,558 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; +import { + attr, + css, + customElement, + DOM, + FASTElement, + html, + ViewTemplate, + when, +} from "@microsoft/fast-element"; +import { display } from "@microsoft/fast-foundation"; +import { + accentFillActive, + accentFillFocus, + accentFillHover, + accentFillRest, + accentForegroundActive, + accentForegroundFocus, + accentForegroundHover, + accentForegroundRest, + fillColor, + focusStrokeInner, + focusStrokeOuter, + foregroundOnAccentActive, + foregroundOnAccentFocus, + foregroundOnAccentHover, + foregroundOnAccentRest, + neutralFillActive, + neutralFillFocus, + neutralFillHover, + neutralFillInputActive, + neutralFillInputFocus, + neutralFillInputHover, + neutralFillInputRest, + neutralFillRest, + neutralFillStealthActive, + neutralFillStealthFocus, + neutralFillStealthHover, + neutralFillStealthRest, + neutralFillStrongActive, + neutralFillStrongHover, + neutralFillStrongRest, + neutralForegroundActive, + neutralForegroundFocus, + neutralForegroundHint, + neutralForegroundHover, + neutralForegroundRest, + neutralStrokeActive, + neutralStrokeDividerRest, + neutralStrokeFocus, + neutralStrokeHover, + neutralStrokeRest, + neutralStrokeStrongActive, + neutralStrokeStrongFocus, + neutralStrokeStrongHover, + neutralStrokeStrongRest, + SwatchRGB, +} from "@microsoft/adaptive-ui"; +import { ComponentType } from "../component-type.js"; +import "./adaptive-component.js"; +import "./swatch.js"; + +const backplateComponents = html` + +`; + +const textComponents = html` + +`; + +const formComponents = html` + +`; + +const template = html` +

+ SWATCH ${x => x.index} - ${x => x.color.toUpperCase()} + ${when( + x => x.layerName, + html` +

+ Layer: ${x => x.layerName} +

+ ` + )} +

+ +
+ ${x => x.componentTypeTemplate()} +
+`; + +const styles = css` + ${display("flex")} :host { + flex-direction: column; + flex-grow: 1; + align-items: stretch; + text-align: center; + position: relative; + transition: opacity 0.1s linear; + height: 100%; + min-height: 100%; + background-color: ${fillColor}; + color: ${neutralForegroundRest}; + } + + .title { + margin: 16px auto 4px; + font-weight: 600; + height: 34px; + color: ${neutralForegroundHint}; + } + + .title code { + font-weight: normal; + } + + .content { + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: center; + padding: 0 48px 36px; + } + + .example { + height: 60px; + display: flex; + align-items: center; + margin-top: 24px; + } + + .divider { + width: 150px; + } +`; + +@customElement({ + name: "app-color-block", + template, + styles, +}) +export class ColorBlock extends FASTElement { + @attr index: number; + + @attr component: ComponentType; + + @attr color: string; + private colorChanged(): void { + DOM.queueUpdate(() => this.updateColor()); + } + + @attr({ attribute: "layer-name" }) + layerName: string; + + componentTypeTemplate(): ViewTemplate { + switch (this.component) { + case ComponentType.backplate: + return backplateComponents; + case ComponentType.text: + return textComponents; + case ComponentType.form: + return formComponents; + } + } + + private updateColor(): void { + if (this.color && this.$fastController.isConnected) { + const color = parseColorHexRGB(this.color)!; + fillColor.setValueFor(this, SwatchRGB.from(color)); + } + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.styles.ts b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.styles.ts new file mode 100644 index 00000000000..0da981848be --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.styles.ts @@ -0,0 +1,38 @@ +import { css } from "@microsoft/fast-element"; +import { display } from "@microsoft/fast-foundation"; +import { typeRampPlus1FontSize, typeRampPlus1LineHeight } from "@microsoft/adaptive-ui"; + +export const controlPaneStyles = css` + ${display("flex")} :host { + flex: 0 1 auto; + flex-direction: column; + gap: 24px; + } + + .title { + font-size: ${typeRampPlus1FontSize}; + line-height: ${typeRampPlus1LineHeight}; + } + + .radio-group { + display: flex; + flex-direction: column; + gap: 8px; + } + + label { + align-items: flex-start; + display: flex; + } + + label span { + margin-top: 3px; + } + + input[type="checkbox"], + input[type="radio"] { + width: 16px; + height: 16px; + margin-right: 8px; + } +`; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts new file mode 100644 index 00000000000..4321f84b42c --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts @@ -0,0 +1,79 @@ +import { ElementViewTemplate, html, repeat } from "@microsoft/fast-element"; +import { ComponentType } from "../../component-type.js"; +import { ControlPane } from "./control-pane.js"; + +function titleCase(str: string): string { + return str + .split("") + .reduce((accumulated: string, value: string, index: number): string => { + return accumulated.concat(index === 0 ? value.toUpperCase() : value); + }, ""); +} + +export function controlPaneTemplate(): ElementViewTemplate { + return html` +

Settings

+
+ + ${repeat( + x => Object.keys(ComponentType), + html` + + ` + )} +
+
+ + ().value + ); + }}" + /> +
+ +
+ + ().value + ); + }}" + /> +
+ `; +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.ts b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.ts new file mode 100644 index 00000000000..1dc1baace88 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/control-pane.ts @@ -0,0 +1,19 @@ +import { FASTElement, observable } from "@microsoft/fast-element"; + +export class ControlPane extends FASTElement { + @observable + componentType: string; + + @observable + accentColor: string; + + @observable + neutralColor: string; + + @observable + showOnlyLayerBackgrounds: boolean = true; + + updateFormValue(field: string, value: any) { + this.$emit("formvaluechange", { field: field, value: value }); + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/control-pane/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/index.ts new file mode 100644 index 00000000000..a442498d6c6 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/control-pane/index.ts @@ -0,0 +1,11 @@ +import { ControlPane } from "./control-pane.js"; +import { controlPaneStyles as styles } from "./control-pane.styles.js"; +import { controlPaneTemplate as template } from "./control-pane.template.js"; + +ControlPane.define({ + name: "app-control-pane", + styles, + template: template(), +}); + +export { ControlPane } from "./control-pane.js"; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/index.ts new file mode 100644 index 00000000000..c4041c5befb --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/index.ts @@ -0,0 +1,8 @@ +export * from "./control-pane/index.js"; +export * from "./layer-background/index.js"; +export * from "./palette-gradient/index.js"; +export * from "./sample-app/index.js"; +export * from "./sample-page/index.js"; +export * from "./adaptive-component.js"; +export * from "./color-block.js"; +export * from "./swatch.js"; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/layer-background/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/layer-background/index.ts new file mode 100644 index 00000000000..5abe2707d74 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/layer-background/index.ts @@ -0,0 +1,131 @@ +import { + attr, + css, + ElementViewTemplate, + FASTElement, + html, + nullableNumberConverter, +} from "@microsoft/fast-element"; +import { + DesignToken, + DesignTokenChangeRecord, + display, +} from "@microsoft/fast-foundation"; +import { + fillColor, + layerFillBaseLuminance, + layerFillFixedBase, + layerFillFixedMinus1, + layerFillFixedMinus2, + layerFillFixedMinus3, + layerFillFixedMinus4, + layerFillFixedPlus1, + layerFillFixedPlus2, + layerFillFixedPlus3, + layerFillFixedPlus4, + layerPalette, + neutralForegroundRest, + Swatch, +} from "@microsoft/adaptive-ui"; + +export class LayerBackground extends FASTElement { + @attr({ attribute: "base-layer-luminance", converter: nullableNumberConverter }) + public baseLayerLuminance: number = 1; + private baseLayerLuminanceChanged(prev: number, next: number): void { + layerFillBaseLuminance.setValueFor(this, this.baseLayerLuminance); + this.updateBackgroundColor(); + } + + @attr({ attribute: "background-layer-recipe" }) + public backgroundLayerRecipe: string = "Base"; + private backgroundLayerRecipeChanged(prev: string, next: string): void { + this.updateBackgroundColor(); + } + + private updateBackgroundColor(): void { + if (!this.$fastController.isConnected) { + return; + } + + if (this.backgroundLayerRecipe !== undefined) { + let swatch: Swatch | null = null; + switch (this.backgroundLayerRecipe) { + case "-1": + swatch = layerFillFixedMinus1.getValueFor(this); + break; + case "-2": + swatch = layerFillFixedMinus2.getValueFor(this); + break; + case "-3": + swatch = layerFillFixedMinus3.getValueFor(this); + break; + case "-4": + swatch = layerFillFixedMinus4.getValueFor(this); + break; + case "Base": + swatch = layerFillFixedBase.getValueFor(this); + break; + case "+1": + swatch = layerFillFixedPlus1.getValueFor(this); + break; + case "+2": + swatch = layerFillFixedPlus2.getValueFor(this); + break; + case "+3": + swatch = layerFillFixedPlus3.getValueFor(this); + break; + case "+4": + swatch = layerFillFixedPlus4.getValueFor(this); + break; + } + + if (swatch !== null) { + fillColor.setValueFor(this, swatch); + } + } + } + + public handleChange( + token: DesignToken, + record: DesignTokenChangeRecord + ): void { + if (record.target === this && token === layerPalette) { + this.updateBackgroundColor(); + } + } + + public connectedCallback(): void { + super.connectedCallback(); + + layerPalette.subscribe(this); + + this.updateBackgroundColor(); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + + layerPalette.unsubscribe(this); + } +} + +export function layerBackgroundTemplate(): ElementViewTemplate< + T +> { + return html` + + `; +} + +export const layerBackgroundStyles = css` + ${display("block")} :host { + background: ${fillColor}; + color: ${neutralForegroundRest}; + } +`; + +LayerBackground.define({ + name: "app-layer-background", + styles: layerBackgroundStyles, + template: layerBackgroundTemplate(), +}); diff --git a/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/index.ts new file mode 100644 index 00000000000..58a71bbc735 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/index.ts @@ -0,0 +1,3 @@ +export { PaletteGradient } from "./palette-gradient.js"; +export { paletteGradientStyles as styles } from "./palette-gradient.styles.js"; +export { paletteGradientTemplate as template } from "./palette-gradient.template.js"; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.styles.ts b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.styles.ts new file mode 100644 index 00000000000..6508e588e03 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.styles.ts @@ -0,0 +1,38 @@ +import { css } from "@microsoft/fast-element"; +import { display } from "@microsoft/fast-foundation"; + +export const paletteGradientStyles = css` + ${display("flex")} :host { + display: flex; + width: 100%; + } + + a { + display: flex; + flex: 1; + height: 100%; + } + + a.source { + position: relative; + } + + a.source::before { + width: 6px; + height: 6px; + margin: 0 auto; + content: ""; + opacity: 0.7; + position: relative; + border: solid 1px currentcolor; + border-radius: 50%; + display: block; + align-self: center; + } + + a.closest::before { + content: "~"; + border: none; + line-height: 6px; + } +`; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.template.ts b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.template.ts new file mode 100644 index 00000000000..205119d14bf --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.template.ts @@ -0,0 +1,32 @@ +import { html, repeat } from "@microsoft/fast-element"; +import { isDark, Swatch } from "@microsoft/adaptive-ui"; +import { PaletteGradient } from "./palette-gradient.js"; + +function getClass(swatch: Swatch, source?: Swatch, closestSource?: Swatch) { + return swatch.toColorString() === source?.toColorString() + ? "source" + : swatch.toColorString() === closestSource?.toColorString() + ? "source closest" + : ""; +} + +function getColor(background: Swatch) { + const darkMode = isDark(background); + return darkMode ? "white" : "black"; +} + +export const paletteGradientTemplate = html` + ${repeat( + x => x.palette?.swatches || [], + html` + + `, + { positioning: true } + )} +`; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.ts b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.ts new file mode 100644 index 00000000000..14f20cd4584 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/palette-gradient/palette-gradient.ts @@ -0,0 +1,21 @@ +import { Palette, Swatch } from "@microsoft/adaptive-ui"; +import { customElement, FASTElement, observable } from "@microsoft/fast-element"; +import { paletteGradientStyles as styles } from "./palette-gradient.styles.js"; +import { paletteGradientTemplate as template } from "./palette-gradient.template.js"; + +@customElement({ + name: "app-palette-gradient", + template, + styles, +}) +export class PaletteGradient extends FASTElement { + closestSource?: Swatch; + + @observable + palette?: Palette; + private paletteChanged() { + this.closestSource = this.palette?.get( + this.palette?.closestIndexOf(this.palette?.source) + ); + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-app/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/index.ts new file mode 100644 index 00000000000..4d53f52299e --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/index.ts @@ -0,0 +1,9 @@ +import { SampleApp } from "./sample-app.js"; +import { sampleAppStyles as styles } from "./sample-app.styles.js"; +import { sampleAppTemplate as template } from "./sample-app.template.js"; + +SampleApp.define({ + name: "app-sample-app", + styles, + template: template(), +}); diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.styles.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.styles.ts new file mode 100644 index 00000000000..cdceac418e0 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.styles.ts @@ -0,0 +1,179 @@ +import { css } from "@microsoft/fast-element"; +import { display, forcedColorsStylesheetBehavior } from "@microsoft/fast-foundation"; +import { SystemColors } from "@microsoft/fast-web-utilities"; +import { + bodyFont, + controlCornerRadius, + designUnit, + fillColor, + neutralForegroundHint, + neutralForegroundRest, + typeRampMinus2FontSize, + typeRampPlus3FontSize, + typeRampPlus3LineHeight, +} from "@microsoft/adaptive-ui"; + +export const sampleAppStyles = css` + ${display("flex")} + + :host { + flex-direction: column; + font-family: ${bodyFont}; + color: ${neutralForegroundRest}; + box-sizing: border-box; + min-height: 650px; + min-width: 775px; + background: ${fillColor}; + border-radius: calc(${controlCornerRadius} * 1px); + --gutter: 20; + } + + app-layer-background { + display: flex; + flex-grow: 1; + } + + p { + margin: 0; + } + + .icon { + pointer-events: none; + } + + .wrapper { + display: flex; + flex-direction: column; + width: 100%; + position: relative; + } + + .toolbar { + display: flex; + align-items: center; + box-sizing: border-box; + height: 40px; + padding: 0 12px; + } + + fluent-tabs { + flex-grow: 1; + } + + fluent-tabs::part(tablist) { + padding: 0 4px; + align-self: start; + } + + fluent-tabs::part(activeIndicator) { + margin: 0; + } + + fluent-tab { + padding: calc(${designUnit} * 5px) calc(${designUnit} * 3px); + } + + fluent-tab-panel { + padding: 0; + height: 100%; + } + + .content { + display: flex; + align-items: stretch; + width: 100%; + text-align: start; + box-shadow: none; + } + + .pane { + width: 240px; + } + + .pane > fluent-listbox { + width: 100%; + } + + .details { + height: unset; + box-shadow: none; + } + + /* wrapper, toolbar, content, pane, details */ + + .content .heading { + font-size: ${typeRampPlus3FontSize}; + line-height: ${typeRampPlus3LineHeight}; + margin: 0; + margin-bottom: 10px; + font-weight: bold; + } + + .icon { + fill: currentColor; + } + + .saturation-slider-track { + height: 100%; + border-radius: calc(${controlCornerRadius} * 1px); + } + + .hue-slider-track { + height: 100%; + border-radius: calc(${controlCornerRadius} * 1px); + background-image: + linear-gradient( + to right, + rgb(255, 0, 0), + rgb(255, 77, 0), + rgb(255, 153, 0), + rgb(255, 230, 0), + rgb(204, 255, 0), + rgb(128, 255, 0), + rgb(51, 255, 0), + rgb(0, 255, 26), + rgb(0, 255, 102), + rgb(0, 255, 179), + rgb(0, 255, 255), + rgb(0, 179, 255), + rgb(0, 102, 255), + rgb(0, 26, 255), + rgb(51, 0, 255), + rgb(128, 0, 255), + rgb(204, 0, 255), + rgb(255, 0, 230), + rgb(255, 0, 153), + rgb(255, 0, 76), + rgb(255, 0, 4) + );" + } + + .responsive-expand-flipper { + position: absolute; + left: -30px; + align-self: center; + display: none; + visibility: hidden; + } + + site-color-swatch { + margin: 0; + } + + fluent-slider-label { + font-size: ${typeRampMinus2FontSize}; + color: ${neutralForegroundHint}; + } +`.withBehaviors( + forcedColorsStylesheetBehavior( + css` + .text-container { + color: ${SystemColors.ButtonText}; + } + fluent-tab:hover[aria-selected="true"] { + background: ${SystemColors.Highlight}; + fill: ${SystemColors.HighlightText}; + } + ` + ) +); diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.template.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.template.ts new file mode 100644 index 00000000000..c2963bdfb62 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.template.ts @@ -0,0 +1,118 @@ +import { ElementViewTemplate, html } from "@microsoft/fast-element"; +import { SampleApp } from "./sample-app.js"; +// import DataAreaIcon from "@fluentui/svg-icons/icons/data_area_24_regular.svg"; +// import DataHistogramIcon from "@fluentui/svg-icons/icons/data_histogram_24_regular.svg"; +// import DataScatterIcon from "@fluentui/svg-icons/icons/data_scatter_24_regular.svg"; + +export function sampleAppTemplate(): ElementViewTemplate { + return html` + +
+
+

Adaptive sample app

+
+ + + + + + + + + + + + + + + + + + +
+ + + Root item 1 + + + Flowers + Daisy + + Sunflower + + + Rose + + Pink + Red + White + + + Nested item 2 + Nested item 3 + + + Root item 2 + + Flowers + + + Daisy + + Sunflower + Rose + + Nested item 2 + Nested item 3 + + Root item 3 + +
+ +
+
+ + +
+ + Item 1 + Item 2 + Item 3 + +
+ +
+
+ + + + + +
+
+
+ `; +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.ts new file mode 100644 index 00000000000..f4be048ca1b --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-app/sample-app.ts @@ -0,0 +1,3 @@ +import { FASTElement } from "@microsoft/fast-element"; + +export class SampleApp extends FASTElement {} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-page/index.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/index.ts new file mode 100644 index 00000000000..5e7856f3efc --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/index.ts @@ -0,0 +1,9 @@ +import { SamplePage } from "./sample-page.js"; +import { samplePageStyles as styles } from "./sample-page.styles.js"; +import { samplePageTemplate as template } from "./sample-page.template.js"; + +SamplePage.define({ + name: "app-sample-page", + styles, + template: template(), +}); diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.styles.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.styles.ts new file mode 100644 index 00000000000..7ceedb92735 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.styles.ts @@ -0,0 +1,137 @@ +import { css } from "@microsoft/fast-element"; +import { display } from "@microsoft/fast-foundation"; +import { + accentFillRest, + controlCornerRadius, + designUnit, + neutralForegroundRest, +} from "@microsoft/adaptive-ui"; + +export const samplePageStyles = css` + ${display("flex")} :host { + display: grid; + grid-gap: calc(var(--gutter) * 2px); + grid-template-columns: auto 300px; + padding: calc(var(--gutter) * 2px); + position: relative; + border-radius: 0 calc(${controlCornerRadius} * 1px) + calc(${controlCornerRadius} * 1px) 0; + } + + .image-container { + /** Temp background */ + background: #d6d6d6; + width: 100%; + height: 215px; + display: flex; + } + + .badge { + align-self: flex-end; + margin: calc(var(--gutter) * 1px); + } + + .text-container { + display: flex; + flex-direction: column; + padding: calc(var(--gutter) * 1px); + text-align: start; + color: ${neutralForegroundRest}; + } + + .sample-control { + display: flex; + align-items: center; + width: 100%; + } + + .sample-control-actions { + margin-inline-start: auto; + } + + .sample-control-text { + margin-inline-start: calc(${designUnit} * 2px + 2px); + } + + .sample-control-icon { + width: 21px; + height: 21px; + background-color: ${accentFillRest}; + border-radius: calc(${controlCornerRadius} * 1px); + } + + .preview-controls { + display: grid; + grid-auto-rows: max-content; + grid-gap: 20px; + } + + .control-container { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 20px; + } + + .control-container-2 { + display: grid; + grid-template-columns: 1fr auto auto; + grid-gap: 20px; + } + + .control-container p { + margin-inline-start: calc(${designUnit} * 2px + 2px); + } + + .control-container-grid { + display: grid; + grid-template-columns: auto 1fr; + text-align: start; + color: ${neutralForegroundRest}; + } + + .checkbox { + grid-row: 2; + } + + .checkbox-label { + grid-row: 2; + grid-column: 2; + } + + fluent-card { + width: 280px; + } + + fluent-badge { + --badge-fill-primary: #e4bc11; + --badge-color-primary: #000000; + } + + fluent-slider { + min-width: unset; + } + + fluent-tab-panel { + height: 100%; + } + + fluent-tab[aria-selected="true"] { + background: transparent; + } + + fluent-radio-group.example-radios { + margin: 0; + } + + fluent-radio-group.example-radios::part(positioning-region) { + display: grid; + grid-template-columns: auto; + height: 100%; + } + + fluent-radio-group.swatches::part(positioning-region) { + display: grid; + grid-gap: 10px; + grid-auto-flow: column; + } +`; diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.template.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.template.ts new file mode 100644 index 00000000000..e0d6b5666f9 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.template.ts @@ -0,0 +1,122 @@ +import { ElementViewTemplate, html } from "@microsoft/fast-element"; +import { SamplePage } from "./sample-page.js"; +// import MoreIcon from "@fluentui/svg-icons/icons/more_horizontal_20_regular.svg"; +// import DownloadIcon from "@fluentui/svg-icons/icons/arrow_download_20_regular.svg"; +// import PlayIcon from "@fluentui/svg-icons/icons/play_20_regular.svg"; + +export function samplePageTemplate(): ElementViewTemplate { + return html` + +
+ + Badge + +
+
+

Example card

+

+ At purus lectus quis habitant commodo, cras. Aliquam malesuada velit a + tortor. Felis orci tellus netus risus et ultricies augue aliquet. + Suscipit mattis mus amet nibh... +

+ +
+ + Label +
+ + + + + +
+
+
+
+
+ + + + Menu item 1 + + + Menu item 2 + + + Menu item 3 + + + + Menu item 4 + + +
+ + Radio 1 + Radio 2 + +
+ Toggle + + Checkbox + +
+
+ +
+ + + +
+
+ + Button + + + + + + + + Button + + + + + + +
+
+ `; +} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.ts b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.ts new file mode 100644 index 00000000000..1efd0a5c7a7 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/sample-page/sample-page.ts @@ -0,0 +1,3 @@ +import { FASTElement } from "@microsoft/fast-element"; + +export class SamplePage extends FASTElement {} diff --git a/packages/tooling/adaptive-ui-explorer/src/components/swatch.ts b/packages/tooling/adaptive-ui-explorer/src/components/swatch.ts new file mode 100644 index 00000000000..a233ac083ac --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/components/swatch.ts @@ -0,0 +1,217 @@ +import { contrastRatio, parseColor } from "@microsoft/fast-colors"; +import { + attr, + css, + customElement, + ElementViewTemplate, + FASTElement, + html, + observable, +} from "@microsoft/fast-element"; +import { DesignToken } from "@microsoft/fast-foundation"; +import { fillColor, neutralForegroundHint, Swatch } from "@microsoft/adaptive-ui"; + +export enum SwatchType { + fill = "fill", + foreground = "foreground", + outline = "outline", +} + +function template(): ElementViewTemplate { + return html` +
+ ${x => x.recipeName} + ${x => x.colorValue} + `; +} + +const styles = css` + :host { + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto; + align-items: center; + width: 100%; + padding: 4px 0; + box-sizing: border-box; + color: ${neutralForegroundHint}; + font-size: 12px; + grid-column-gap: 16px; + justify-items: start; + } + + :host([type="foreground"]) .icon { + border: 1px solid black; + } + + :host([type="foreground"]) .icon::before { + font-size: 13px; + content: "A"; + font-weight: 400; + } + + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 2px; + box-sizing: border-box; + } + + .recipe-name { + grid-column: 2; + grid-row: 1; + } + + .hex-code { + grid-column: 3; + grid-row: 1; + } +`; + +@customElement({ + name: "app-swatch", + template: template(), + styles, +}) +export class AppSwatch extends FASTElement { + @attr + public type: SwatchType; + + @attr({ attribute: "recipe-name" }) + public recipeName: string; + + @observable + public foregroundRecipe?: DesignToken; + public foregroundRecipeChanged() { + this.updateObservables(); + } + + @observable + public fillRecipe?: DesignToken; + public fillRecipeChanged() { + this.updateObservables(); + } + + @observable + public outlineRecipe?: DesignToken; + public outlineRecipeChanged() { + this.updateObservables(); + } + + @observable + public iconStyle: string; + + @observable + public contrastMessage: string; + + @observable + public colorValue: string; + + public connectedCallback() { + super.connectedCallback(); + + fillColor.subscribe(this); + + this.updateObservables(); + } + + public disconnectedCallback() { + super.disconnectedCallback(); + + fillColor.unsubscribe(this); + } + + public handleChange() { + this.updateObservables(); + } + + private updateObservables() { + this.updateIconStyle(); + this.updateContrastMessage(); + this.updateColorValue(); + } + + private tokenCSS(token?: DesignToken): string { + return token && typeof (token as any).createCSS === "function" + ? (token as any).createCSS() + : ""; + } + + private evaluateToken(token?: DesignToken): string { + return token?.getValueFor(this).toColorString() || ""; + } + + private updateIconStyle(): void { + const background = `background-color: ${this.tokenCSS(this.fillRecipe)}`; + this.iconStyle = + this.type === SwatchType.outline + ? `border: 4px solid ${this.tokenCSS(this.outlineRecipe)}; ${background}` + : this.type === SwatchType.foreground + ? `color: ${this.tokenCSS(this.foregroundRecipe)}; ${background}` + : background; + } + + private formatContrast(a?: DesignToken, b?: DesignToken): string { + return a && b + ? contrastRatio( + parseColor(this.evaluateToken(a))!, + parseColor(this.evaluateToken(b))! + ).toFixed(2) + : ""; + } + + private formatBackgroundContrast( + a?: DesignToken, + b?: DesignToken + ): string { + return `BG contrast: ${this.formatContrast(a, b)} : 1`; + } + + private formatForegroundContrast( + a?: DesignToken, + b?: DesignToken + ): string { + return `Text contrast: ${this.formatContrast(a, b)} : 1`; + } + + private updateContrastMessage(): void { + const backgroundContrastMessage: string = this.formatBackgroundContrast( + this.type === SwatchType.foreground + ? this.foregroundRecipe + : this.type === SwatchType.outline + ? this.outlineRecipe + : this.fillRecipe, + this.type === SwatchType.foreground || this.type === SwatchType.outline + ? this.fillRecipe + : fillColor + ); + + this.contrastMessage = + this.type === SwatchType.fill + ? backgroundContrastMessage.concat( + "\n", + this.formatForegroundContrast( + this.fillRecipe, + this.foregroundRecipe + ) + ) + : backgroundContrastMessage; + } + + private updateColorValue(): void { + const recipe = + this.type === SwatchType.outline + ? this.outlineRecipe + : this.type === SwatchType.foreground + ? this.foregroundRecipe + : this.fillRecipe; + this.colorValue = this.evaluateToken(recipe).toUpperCase(); + } +} diff --git a/packages/tooling/adaptive-ui-explorer/src/index.ts b/packages/tooling/adaptive-ui-explorer/src/index.ts new file mode 100644 index 00000000000..78c60d0e3c5 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/src/index.ts @@ -0,0 +1,3 @@ +export * from "./components/index.js"; +export * from "./app.js"; +export * from "./component-type.js"; diff --git a/packages/tooling/adaptive-ui-explorer/tsconfig.json b/packages/tooling/adaptive-ui-explorer/tsconfig.json new file mode 100644 index 00000000000..8b46cab95fc --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "declarationDir": "dist/dts", + "outDir": "dist/esm", + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "target": "es2015", + "module": "ESNext", + "moduleResolution": "Node16", + "importHelpers": true, + "jsx": "react", + "types": ["webpack-env"], + "lib": ["DOM", "ES2015", "ES2016.Array.Include", "ES2017.Object"] + }, + "include": ["src"] +} diff --git a/packages/tooling/adaptive-ui-explorer/webpack.config.cjs b/packages/tooling/adaptive-ui-explorer/webpack.config.cjs new file mode 100644 index 00000000000..f73563850f8 --- /dev/null +++ b/packages/tooling/adaptive-ui-explorer/webpack.config.cjs @@ -0,0 +1,56 @@ +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const InjectBodyPlugin = require("inject-body-webpack-plugin").default; +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const ResolveTypeScriptPlugin = require("resolve-typescript-plugin"); +const path = require('path'); + +module.exports = function(env, { mode }) { + const production = mode === 'production'; + return { + mode: production ? 'production' : 'development', + devtool: production ? 'source-map' : 'inline-source-map', + entry: { + app: ['./src/app.ts'] + }, + output: { + filename: 'bundle.js', + publicPath: '/', + path: path.resolve(__dirname, './dist'), + }, + plugins: [ + new CleanWebpackPlugin(), + new InjectBodyPlugin({ + content: " ", + }), + new HtmlWebpackPlugin({ + title: "Adaptive UI Explorer", + }), + ], + devServer: { + static: { + directory: "./src/public", + }, + open: true, + port: 7700, + allowedHosts: "all", + }, + module: { + rules: [ + { + test: /\.ts$/i, + use: [ + { + loader: 'ts-loader' + } + ], + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: ['.ts', '.js'], + modules: ['src', 'node_modules'], + plugins: [new ResolveTypeScriptPlugin()] + } + } +} diff --git a/yarn.lock b/yarn.lock index a32ec939399..cac16515225 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9890,7 +9890,7 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -13427,6 +13427,15 @@ init-package-json@^3.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^4.0.0" +inject-body-webpack-plugin@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/inject-body-webpack-plugin/-/inject-body-webpack-plugin-1.3.0.tgz#e29fee59c81f0170d01191d197fda375a5c4cf12" + integrity sha512-P2H7b52je3jKVcJ8Gw2MjqccGo0QutEjoXoB3XRKbSgsqOv3NgTypqoUm5D8oEz90vx5AIgW9Bk+hJxzEoGvMg== + dependencies: + debug "^4.3.1" + insert-string-after "^1.0.0" + insert-string-before "^1.0.0" + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" @@ -13472,6 +13481,16 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" +insert-string-after@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/insert-string-after/-/insert-string-after-1.0.0.tgz#084e1e9f4cdbc6aeb961bca3e8dce9a7e78908b0" + integrity sha512-v8P35PHcglmeE6sj12AzhJfRyrH221uA9iSO4VNWf/TQCqgvb/g1Zst6HpLxFrsTVhuVHzgZPPyhJZX1YJZ3yw== + +insert-string-before@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/insert-string-before/-/insert-string-before-1.0.0.tgz#b726537bb361d9ba72f1fcfcbf20d14521fa3808" + integrity sha512-Z3sKgwLX+x22HyEUjHJe05GdetR3aYtJy17cXRaym57AwXwI8Z7dCbAiYB+3d8u9NW/qi8ZLNhTev29H7QXnXw== + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"