chore: remove adaptive ui related packages (#6764)
* decouple website from packagaes * remove figma plugin
|
@ -24,7 +24,6 @@ build/ @janechu @nicholasrice @chrisdholt @awentzel @EisenbergEffect
|
|||
/specs/ @EisenbergEffect @chrisdholt @janechu @nicholasrice
|
||||
|
||||
# Sites
|
||||
/sites/fast-color-explorer/ @nicholasrice @janechu @bheston
|
||||
/sites/website/ @awentzel @chrisdholt @falkicon @EisenbergEffect
|
||||
|
||||
# Package specific owners
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
name: Deploy Color Explorer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/cd-deploy-color-staging.yml'
|
||||
- 'sites/fast-color-explorer/**'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/cd-deploy-color-staging.yml'
|
||||
- 'sites/fast-color-explorer/**'
|
||||
|
||||
env:
|
||||
AZURE_WEBAPP_ACTIVE_STAGE_NAME: color-west-app
|
||||
AZURE_WEBAPP_PASSIVE_STAGE_NAME: color-east-app
|
||||
AZURE_WEBAPP_BUILD_PATH: sites/fast-color-explorer
|
||||
AZURE_WEBAPP_DIST_PATH: sites/fast-color-explorer/www
|
||||
AZURE_WEBAPP_SLOT_NAME: stage
|
||||
ARTIFACT_NAME: color-explorer-site
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Install Lerna
|
||||
run: yarn global add lerna
|
||||
|
||||
- name: Install package dependencies / prepare workspaces
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build & Prepare Web Application
|
||||
run: |
|
||||
cd ${{ env.AZURE_WEBAPP_BUILD_PATH }}
|
||||
yarn build
|
||||
cp ../site-utilities/statics/server/package.json ../site-utilities/statics/server/server.js www
|
||||
cd www
|
||||
npm i
|
||||
ls -lta
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
|
||||
deploy_to_staging:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Deploy PR
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
|
||||
- name: 'Deploy to Active Azure Region'
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE_COLOR_ACTIVE }}
|
||||
app-name: ${{ env.AZURE_WEBAPP_ACTIVE_STAGE_NAME }}
|
||||
package: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
slot-name: ${{ env.AZURE_WEBAPP_SLOT_NAME }}
|
||||
|
||||
- name: 'Deploy to Passive Azure Region'
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE_COLOR_PASSIVE }}
|
||||
app-name: ${{ env.AZURE_WEBAPP_PASSIVE_STAGE_NAME }}
|
||||
package: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
slot-name: ${{ env.AZURE_WEBAPP_SLOT_NAME }}
|
||||
|
||||
deploy_pr:
|
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Deploy PR
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
|
||||
- name: Deploy Color Explorer
|
||||
id: deploypr
|
||||
uses: Azure/static-web-apps-deploy@v0.0.1-preview
|
||||
continue-on-error: ${{ (github.event_name == 'pull_request' && github.event.action != 'closed') }}
|
||||
with:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APP_COLOR_PR_TOKEN }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: 'upload'
|
||||
app_location: ${{ env.AZURE_WEBAPP_DIST_PATH }}
|
||||
output_location: ''
|
||||
skip_app_build: true
|
||||
|
||||
destroy_pr:
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Destroy PR
|
||||
id: closepullrequest
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APP_COLOR_PR_TOKEN }}
|
||||
action: "close"
|
||||
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
needs: deploy_to_staging
|
||||
|
||||
steps:
|
||||
- name: Notify on Discord
|
||||
uses: appleboy/discord-action@master
|
||||
with:
|
||||
webhook_id: ${{ secrets.DISCORD_NOTIFICATION_WEBHOOK_ID }}
|
||||
webhook_token: ${{ secrets.DISCORD_NOTIFICATION_WEBHOOK_TOKEN }}
|
||||
color: '#DE2D6D'
|
||||
username: 'FAST DevOps Color Explorer Bot'
|
||||
message: 'Deployment has completed to Staging on https://github.com/microsoft/fast/actions/workflows/cd-deploy-color-staging.yml'
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"packages": [
|
||||
"packages/**/*",
|
||||
"sites/fast-color-explorer"
|
||||
"packages/**/*"
|
||||
],
|
||||
"version": "independent",
|
||||
"npmClient": "yarn",
|
||||
|
|
|
@ -10,13 +10,10 @@
|
|||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/tooling/adaptive-ui-explorer",
|
||||
"packages/utilities/*",
|
||||
"packages/web-components/*",
|
||||
"sites/fast-color-explorer",
|
||||
"sites/site-utilities",
|
||||
"sites/website",
|
||||
"sites/fast-website",
|
||||
"examples/todo-app",
|
||||
"examples/ssr"
|
||||
],
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# 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__
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"extends": ["@microsoft/eslint-config-fast-dna", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-non-null-assertion": "off"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
tsdoc-metadata.json
|
||||
temp
|
||||
test
|
|
@ -1,7 +0,0 @@
|
|||
# Tests
|
||||
__test__/
|
||||
*.spec.*
|
||||
*.test.*
|
||||
|
||||
# Source files
|
||||
src/
|
|
@ -1 +0,0 @@
|
|||
package-lock=false
|
|
@ -1,3 +0,0 @@
|
|||
dist/*
|
||||
test/*
|
||||
src/__test__/*
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"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.28",
|
||||
"@microsoft/fast-colors": "^5.3.1",
|
||||
"@microsoft/fast-element": "^2.0.0-beta.24",
|
||||
"@microsoft/fast-foundation": "^3.0.0-alpha.28"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -1,345 +0,0 @@
|
|||
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<App>`
|
||||
<app-design-system-provider
|
||||
neutral-color="${x => x.neutralColor}"
|
||||
accent-color="${x => x.accentColor}"
|
||||
style="display: flex; align-items: stretch; align-content: stretch; justify-content: center; flex-grow: 1;"
|
||||
>
|
||||
<app-layer-background
|
||||
id="light-mode"
|
||||
base-layer-luminance="${LayerBaseLuminance.LightMode}"
|
||||
background-layer-recipe="-4"
|
||||
style="flex-grow: 1; padding: 100px;"
|
||||
>
|
||||
<app-sample-app></app-sample-app>
|
||||
</app-layer-background>
|
||||
<app-layer-background
|
||||
id="dark-mode"
|
||||
base-layer-luminance="${LayerBaseLuminance.DarkMode}"
|
||||
background-layer-recipe="-4"
|
||||
style="flex-grow: 1; padding: 100px;"
|
||||
>
|
||||
<app-sample-app></app-sample-app>
|
||||
</app-layer-background>
|
||||
</app-design-system-provider>
|
||||
`;
|
||||
|
||||
const colorBlockTemplate = html<App>`
|
||||
${repeat(
|
||||
x => x.backgrounds,
|
||||
html<SwatchInfo, App>`
|
||||
<app-color-block
|
||||
id="${x => x.color.toUpperCase().replace("#", "")}"
|
||||
index="${(x, c) => x.index}"
|
||||
component="${(x, c) => c.parent.componentType}"
|
||||
color="${x => x.color}"
|
||||
layer-name="${x => x.title}"
|
||||
></app-color-block>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
const template = html<App>`
|
||||
<div class="container fill">
|
||||
<div class="row fill">
|
||||
<app-design-system-provider class="canvas" ${ref("canvas")}>
|
||||
<app-design-system-provider
|
||||
${ref("designSystemElement")}
|
||||
></app-design-system-provider>
|
||||
<div class="container fill">
|
||||
<div class="row gradient">
|
||||
<app-palette-gradient
|
||||
:palette="${x => x.neutralPalette}"
|
||||
></app-palette-gradient>
|
||||
</div>
|
||||
<div class="row gradient">
|
||||
<app-palette-gradient
|
||||
:palette="${x => x.accentPalette}"
|
||||
></app-palette-gradient>
|
||||
</div>
|
||||
<div class="row fill">
|
||||
<div style="display: flex; overflow: auto;">
|
||||
${x => x.componentTypeTemplate()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</app-design-system-provider>
|
||||
<div>
|
||||
<app-layer-background
|
||||
id="control-pane"
|
||||
class="control-pane-container"
|
||||
base-layer-luminance="${LayerBaseLuminance.DarkMode}"
|
||||
background-layer-recipe="-1"
|
||||
>
|
||||
<app-control-pane
|
||||
:componentType="${x => x.componentType}"
|
||||
:neutralColor="${x => x.neutralColor}"
|
||||
:accentColor="${x => x.accentColor}"
|
||||
:showOnlyLayerBackgrounds="${x => x.showOnlyLayerBackgrounds}"
|
||||
@formvaluechange="${(x, c) =>
|
||||
x.controlPaneHandler(c.event as CustomEvent)}"
|
||||
></app-control-pane>
|
||||
</app-layer-background>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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`
|
||||
<slot></slot>
|
||||
`,
|
||||
})
|
||||
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<App, any> {
|
||||
// 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<Swatch>, 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<Swatch>, 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<SwatchInfo> => {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
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];
|
|
@ -1,6 +0,0 @@
|
|||
export enum ComponentType {
|
||||
backplate = "backplate",
|
||||
text = "text",
|
||||
form = "form",
|
||||
// sample = "sample",
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
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<T extends AdaptiveComponent>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<template
|
||||
tabindex="0"
|
||||
style="
|
||||
--ac-fill-rest: ${x => x.fillRest?.createCSS()};
|
||||
--ac-fill-hover: ${x => x.fillHover?.createCSS()};
|
||||
--ac-fill-active: ${x => x.fillActive?.createCSS()};
|
||||
--ac-fill-focus: ${x => x.fillFocus?.createCSS()};
|
||||
--ac-stroke-rest: ${x => x.strokeRest?.createCSS()};
|
||||
--ac-stroke-hover: ${x => x.strokeHover?.createCSS()};
|
||||
--ac-stroke-active: ${x => x.strokeActive?.createCSS()};
|
||||
--ac-stroke-focus: ${x => x.strokeFocus?.createCSS()};
|
||||
--ac-foreground-rest: ${x => x.foregroundRest?.createCSS()};
|
||||
--ac-foreground-hover: ${x => x.foregroundHover?.createCSS()};
|
||||
--ac-foreground-active: ${x => x.foregroundActive?.createCSS()};
|
||||
--ac-foreground-focus: ${x => x.foregroundFocus?.createCSS()};
|
||||
"
|
||||
>
|
||||
<slot></slot>
|
||||
</template>
|
||||
`;
|
||||
}
|
||||
|
||||
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<Swatch>;
|
||||
|
||||
@observable
|
||||
public fillHover?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public fillActive?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public fillFocus?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public strokeRest?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public strokeHover?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public strokeActive?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public strokeFocus?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public foregroundRest?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public foregroundHover?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public foregroundActive?: CSSDesignToken<Swatch>;
|
||||
|
||||
@observable
|
||||
public foregroundFocus?: CSSDesignToken<Swatch>;
|
||||
}
|
|
@ -1,557 +0,0 @@
|
|||
import { parseColorHexRGB } from "@microsoft/fast-colors";
|
||||
import {
|
||||
attr,
|
||||
css,
|
||||
customElement,
|
||||
DOM,
|
||||
FASTElement,
|
||||
html,
|
||||
Updates,
|
||||
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<ColorBlock>`
|
||||
<template>
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => accentFillRest}"
|
||||
:fillHover="${x => accentFillHover}"
|
||||
:fillActive="${x => accentFillActive}"
|
||||
:fillFocus="${x => accentFillFocus}"
|
||||
:foregroundRest="${x => foregroundOnAccentRest}"
|
||||
:foregroundHover="${x => foregroundOnAccentHover}"
|
||||
:foregroundActive="${x => foregroundOnAccentActive}"
|
||||
:foregroundFocus="${x => foregroundOnAccentFocus}"
|
||||
>
|
||||
Accent button
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="accentFillRest"
|
||||
:fillRecipe="${x => accentFillRest}"
|
||||
:foregroundRecipe="${x => foregroundOnAccentRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="accentFillHover"
|
||||
:fillRecipe="${x => accentFillHover}"
|
||||
:foregroundRecipe="${x => foregroundOnAccentRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="accentFillActive"
|
||||
:fillRecipe="${x => accentFillActive}"
|
||||
:foregroundRecipe="${x => foregroundOnAccentRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="foregroundOnAccent"
|
||||
:fillRecipe="${x => accentFillRest}"
|
||||
:foregroundRecipe="${x => foregroundOnAccentRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeInner"
|
||||
:fillRecipe="${x => focusStrokeOuter}"
|
||||
:foregroundRecipe="${x => focusStrokeInner}"
|
||||
:outlineRecipe="${x => focusStrokeInner}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => neutralFillRest}"
|
||||
:fillHover="${x => neutralFillHover}"
|
||||
:fillActive="${x => neutralFillActive}"
|
||||
:fillFocus="${x => neutralFillFocus}"
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
>
|
||||
Neutral button
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillRest"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillHover"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillActive"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillActive}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => fillColor}"
|
||||
:strokeRest="${x => neutralStrokeRest}"
|
||||
:strokeHover="${x => neutralStrokeHover}"
|
||||
:strokeActive="${x => neutralStrokeActive}"
|
||||
:strokeFocus="${x => neutralStrokeFocus}"
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
>
|
||||
Outline button
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeRest"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:outlineRecipe="${x => neutralStrokeRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeHover"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:outlineRecipe="${x => neutralStrokeHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeActive"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:outlineRecipe="${x => neutralStrokeActive}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => neutralFillStealthRest}"
|
||||
:fillHover="${x => neutralFillStealthHover}"
|
||||
:fillActive="${x => neutralFillStealthActive}"
|
||||
:fillFocus="${x => neutralFillStealthFocus}"
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
>
|
||||
Stealth button
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStealthRest"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillStealthRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStealthHover"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillStealthHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStealthActive"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:fillRecipe="${x => neutralFillStealthActive}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const textComponents = html<ColorBlock>`
|
||||
<template>
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
:foregroundHover="${x => neutralForegroundHover}"
|
||||
:foregroundActive="${x => neutralForegroundActive}"
|
||||
:foregroundFocus="${x => neutralForegroundFocus}"
|
||||
>
|
||||
Neutral
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundHover"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundActive"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundActive}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component :foregroundRest="${x => neutralForegroundHint}">
|
||||
Hint
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundHint"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundHint}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:foregroundRest="${x => accentForegroundRest}"
|
||||
:foregroundHover="${x => accentForegroundHover}"
|
||||
:foregroundActive="${x => accentForegroundActive}"
|
||||
:foregroundFocus="${x => accentForegroundFocus}"
|
||||
>
|
||||
Accent
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="accentForegroundRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => accentForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="accentForegroundHover"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => accentForegroundHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="accentForegroundActive"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => accentForegroundActive}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const formComponents = html<ColorBlock>`
|
||||
<template>
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => neutralFillInputRest}"
|
||||
:fillHover="${x => neutralFillInputHover}"
|
||||
:fillActive="${x => neutralFillInputActive}"
|
||||
:fillFocus="${x => neutralFillInputFocus}"
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
:strokeRest="${x => neutralStrokeRest}"
|
||||
:strokeHover="${x => neutralStrokeHover}"
|
||||
:strokeActive="${x => neutralStrokeActive}"
|
||||
:strokeFocus="${x => neutralStrokeFocus}"
|
||||
>
|
||||
Text field
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillInputRest"
|
||||
:fillRecipe="${x => neutralFillInputRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundHint"
|
||||
:fillRecipe="${x => neutralFillInputRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundHint}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => neutralFillInputRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:outlineRecipe="${x => neutralStrokeRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeHover"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:outlineRecipe="${x => neutralStrokeHover}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component
|
||||
:fillRest="${x => neutralFillInputRest}"
|
||||
:fillHover="${x => neutralFillInputHover}"
|
||||
:fillActive="${x => neutralFillInputActive}"
|
||||
:fillFocus="${x => neutralFillInputFocus}"
|
||||
:foregroundRest="${x => neutralForegroundRest}"
|
||||
:strokeRest="${x => neutralStrokeStrongRest}"
|
||||
:strokeHover="${x => neutralStrokeStrongHover}"
|
||||
:strokeActive="${x => neutralStrokeStrongActive}"
|
||||
:strokeFocus="${x => neutralStrokeStrongFocus}"
|
||||
>
|
||||
Checkbox
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillInputRest"
|
||||
:fillRecipe="${x => neutralFillInputRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStrongRest"
|
||||
:fillRecipe="${x => neutralFillStrongRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStrongHover"
|
||||
:fillRecipe="${x => neutralFillStrongHover}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="fill"
|
||||
recipe-name="neutralFillStrongActive"
|
||||
:fillRecipe="${x => neutralFillStrongActive}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="foreground"
|
||||
recipe-name="neutralForegroundRest"
|
||||
:fillRecipe="${x => neutralFillInputRest}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
></app-swatch>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="focusStrokeOuter"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => focusStrokeOuter}"
|
||||
:outlineRecipe="${x => focusStrokeOuter}"
|
||||
></app-swatch>
|
||||
|
||||
<div class="example">
|
||||
<app-adaptive-component :strokeRest="${x => neutralStrokeDividerRest}">
|
||||
Divider
|
||||
</app-adaptive-component>
|
||||
</div>
|
||||
<app-swatch
|
||||
type="outline"
|
||||
recipe-name="neutralStrokeDividerRest"
|
||||
:fillRecipe="${x => fillColor}"
|
||||
:foregroundRecipe="${x => neutralForegroundRest}"
|
||||
:outlineRecipe="${x => neutralStrokeDividerRest}"
|
||||
></app-swatch>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const template = html<ColorBlock>`
|
||||
<p class="title">
|
||||
SWATCH ${x => x.index} - ${x => x.color.toUpperCase()}
|
||||
${when(
|
||||
x => x.layerName,
|
||||
html`
|
||||
<p>
|
||||
<code>Layer: ${x => x.layerName}</code>
|
||||
</p>
|
||||
`
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div class="content">${x => x.componentTypeTemplate()}</div>
|
||||
`;
|
||||
|
||||
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 {
|
||||
Updates.enqueue(() => this.updateColor());
|
||||
}
|
||||
|
||||
@attr({ attribute: "layer-name" })
|
||||
layerName: string;
|
||||
|
||||
componentTypeTemplate(): ViewTemplate<ColorBlock, any> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
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;
|
||||
}
|
||||
`;
|
|
@ -1,79 +0,0 @@
|
|||
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<T extends ControlPane>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<p class="title">Settings</p>
|
||||
<div class="radio-group">
|
||||
<label>Component type</label>
|
||||
${repeat(
|
||||
x => Object.keys(ComponentType),
|
||||
html<string, T>`
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="componentType"
|
||||
value="${x => x}"
|
||||
?checked="${(x, c) => c.parent.componentType === x}"
|
||||
@change="${(x, c) => {
|
||||
c.parent.updateFormValue(
|
||||
"componentType",
|
||||
c.eventTarget<HTMLInputElement>().value
|
||||
);
|
||||
}}"
|
||||
/>
|
||||
<span>${x => titleCase(x)}</span>
|
||||
</label>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label>Neutral base color</label>
|
||||
<input
|
||||
type="color"
|
||||
value="${x => x.neutralColor}"
|
||||
@change="${(x, c) => {
|
||||
x.updateFormValue(
|
||||
"neutralColor",
|
||||
c.eventTarget<HTMLInputElement>().value
|
||||
);
|
||||
}}"
|
||||
/>
|
||||
</div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked="${x => x.showOnlyLayerBackgrounds}"
|
||||
@change="${(x, c) => {
|
||||
x.updateFormValue(
|
||||
"showOnlyLayerBackgrounds",
|
||||
c.eventTarget<HTMLInputElement>().checked
|
||||
);
|
||||
}}"
|
||||
/>
|
||||
<span>Show layer backgrounds only</span>
|
||||
</label>
|
||||
<div>
|
||||
<label>Accent base color</label>
|
||||
<input
|
||||
type="color"
|
||||
value="${x => x.accentColor}"
|
||||
@change="${(x, c) => {
|
||||
x.updateFormValue(
|
||||
"accentColor",
|
||||
c.eventTarget<HTMLInputElement>().value
|
||||
);
|
||||
}}"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
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 });
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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";
|
|
@ -1,8 +0,0 @@
|
|||
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";
|
|
@ -1,131 +0,0 @@
|
|||
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<any>,
|
||||
record: DesignTokenChangeRecord<any>
|
||||
): 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<
|
||||
T extends LayerBackground
|
||||
>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
|
||||
export const layerBackgroundStyles = css`
|
||||
${display("block")} :host {
|
||||
background: ${fillColor};
|
||||
color: ${neutralForegroundRest};
|
||||
}
|
||||
`;
|
||||
|
||||
LayerBackground.define({
|
||||
name: "app-layer-background",
|
||||
styles: layerBackgroundStyles,
|
||||
template: layerBackgroundTemplate(),
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
export { PaletteGradient } from "./palette-gradient.js";
|
||||
export { paletteGradientStyles as styles } from "./palette-gradient.styles.js";
|
||||
export { paletteGradientTemplate as template } from "./palette-gradient.template.js";
|
|
@ -1,38 +0,0 @@
|
|||
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;
|
||||
}
|
||||
`;
|
|
@ -1,32 +0,0 @@
|
|||
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<PaletteGradient>`
|
||||
${repeat(
|
||||
x => x.palette?.swatches || [],
|
||||
html<Swatch, PaletteGradient>`
|
||||
<a
|
||||
class="${(x, c) =>
|
||||
getClass(x, c.parent.palette?.source, c.parent.closestSource)}"
|
||||
style="background: ${x => x.toColorString()}; color: ${x => getColor(x)}"
|
||||
title="${(x, c) =>
|
||||
c.index.toString().concat(": ", x.toColorString().toUpperCase())}"
|
||||
></a>
|
||||
`,
|
||||
{ positioning: true }
|
||||
)}
|
||||
`;
|
|
@ -1,21 +0,0 @@
|
|||
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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
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(),
|
||||
});
|
|
@ -1,179 +0,0 @@
|
|||
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};
|
||||
}
|
||||
`
|
||||
)
|
||||
);
|
|
@ -1,118 +0,0 @@
|
|||
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<T extends SampleApp>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<app-layer-background background-layer-recipe="-3">
|
||||
<div class="wrapper">
|
||||
<div class="toolbar">
|
||||
<p>Adaptive sample app</p>
|
||||
</div>
|
||||
<fluent-tabs orientation="vertical">
|
||||
<fluent-tab id="tab-1" title="Area">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 3.75a.75.75 0 011.5 0v6.26l3.65-1.92c.23-.12.5-.12.73.01l3.82 2.25 5.6-4.2a.75.75 0 011.2.6V19.5h.75a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75v-8.99-7.51zm1.5 7.95v7.8H18V8.25l-4.8 3.6a.75.75 0 01-.83.05L8.48 9.6 4.5 11.7z"
|
||||
/>
|
||||
</svg>
|
||||
</fluent-tab>
|
||||
<fluent-tab id="tab-2" title="Histogram">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.5 5.23c0-1.24 1-2.25 2.25-2.25h2.5c1.24 0 2.25 1 2.25 2.25V7h3.25C19.99 7 21 8 21 9.25v11c0 .41-.34.75-.75.75H3.75a.75.75 0 01-.75-.75v-8C3 11.01 4 10 5.25 10H8.5V5.23zM10 19.5h4V5.23a.75.75 0 00-.75-.75h-2.5a.75.75 0 00-.75.75V19.5zm-1.5-8H5.25a.75.75 0 00-.75.75v7.25h4v-8zm7 8h4V9.25a.75.75 0 00-.75-.75H15.5v11z"
|
||||
/>
|
||||
</svg>
|
||||
</fluent-tab>
|
||||
<fluent-tab id="tab-3" title="Scatter">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 3.75a.75.75 0 011.5 0V19.5h15.75a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75V3.75zM17 4a3 3 0 100 6 3 3 0 000-6zm-1.5 3a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0zM6 9a3 3 0 116 0 3 3 0 01-6 0zm3-1.5a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm6 4.5a3 3 0 100 6 3 3 0 000-6zm-1.5 3a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0z"
|
||||
/>
|
||||
</svg>
|
||||
</fluent-tab>
|
||||
<fluent-tab-panel id="tab-panel-1">
|
||||
<fluent-card class="content">
|
||||
<div class="pane">
|
||||
<fluent-tree-view render-collapsed-nodes="false">
|
||||
<fluent-tree-item>
|
||||
Root item 1
|
||||
<fluent-divider></fluent-divider>
|
||||
<fluent-tree-item expanded>
|
||||
Flowers
|
||||
<fluent-tree-item>Daisy</fluent-tree-item>
|
||||
<fluent-tree-item disabled>
|
||||
Sunflower
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item expanded>
|
||||
Rose
|
||||
<fluent-divider
|
||||
role="presentation"
|
||||
></fluent-divider>
|
||||
<fluent-tree-item>Pink</fluent-tree-item>
|
||||
<fluent-tree-item>Red</fluent-tree-item>
|
||||
<fluent-tree-item>White</fluent-tree-item>
|
||||
</fluent-tree-item>
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item>Nested item 2</fluent-tree-item>
|
||||
<fluent-tree-item>Nested item 3</fluent-tree-item>
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item>
|
||||
Root item 2
|
||||
<fluent-tree-item>
|
||||
Flowers
|
||||
<fluent-divider></fluent-divider>
|
||||
<fluent-tree-item disabled>
|
||||
Daisy
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item>Sunflower</fluent-tree-item>
|
||||
<fluent-tree-item>Rose</fluent-tree-item>
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item>Nested item 2</fluent-tree-item>
|
||||
<fluent-tree-item>Nested item 3</fluent-tree-item>
|
||||
</fluent-tree-item>
|
||||
<fluent-tree-item>Root item 3</fluent-tree-item>
|
||||
</fluent-tree-view>
|
||||
</div>
|
||||
<fluent-card class="details"></fluent-card>
|
||||
</fluent-card>
|
||||
</fluent-tab-panel>
|
||||
<fluent-tab-panel id="tab-panel-2">
|
||||
<fluent-card class="content">
|
||||
<div class="pane">
|
||||
<fluent-listbox>
|
||||
<fluent-option>Item 1</fluent-option>
|
||||
<fluent-option>Item 2</fluent-option>
|
||||
<fluent-option>Item 3</fluent-option>
|
||||
</fluent-listbox>
|
||||
</div>
|
||||
<fluent-card class="details"></fluent-card>
|
||||
</fluent-card>
|
||||
</fluent-tab-panel>
|
||||
<fluent-tab-panel id="tab-panel-3">
|
||||
<fluent-card class="content">
|
||||
<app-sample-page></app-sample-page>
|
||||
</fluent-card>
|
||||
</fluent-tab-panel>
|
||||
</fluent-tabs>
|
||||
</div>
|
||||
</app-layer-background>
|
||||
`;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import { FASTElement } from "@microsoft/fast-element";
|
||||
|
||||
export class SampleApp extends FASTElement {}
|
|
@ -1,9 +0,0 @@
|
|||
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(),
|
||||
});
|
|
@ -1,137 +0,0 @@
|
|||
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;
|
||||
}
|
||||
`;
|
|
@ -1,122 +0,0 @@
|
|||
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<T extends SamplePage>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<fluent-card>
|
||||
<div class="image-container">
|
||||
<fluent-badge fill="primary" color="primary" class="badge">
|
||||
Badge
|
||||
</fluent-badge>
|
||||
</div>
|
||||
<div class="text-container">
|
||||
<h3>Example card</h3>
|
||||
<p>
|
||||
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...
|
||||
</p>
|
||||
<fluent-divider></fluent-divider>
|
||||
<div class="sample-control">
|
||||
<span class="sample-control-icon"></span>
|
||||
<span class="sample-control-text">Label</span>
|
||||
<div class="sample-control-actions">
|
||||
<fluent-button
|
||||
appearance="stealth"
|
||||
aria-label="Example 'more' button"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.25 10a1.25 1.25 0 11-2.5 0 1.25 1.25 0 012.5 0zm5 0a1.25 1.25 0 11-2.5 0 1.25 1.25 0 012.5 0zM15 11.25a1.25 1.25 0 100-2.5 1.25 1.25 0 000 2.5z"
|
||||
/>
|
||||
</svg>
|
||||
</fluent-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fluent-card>
|
||||
<div class="preview-controls">
|
||||
<fluent-progress aria-label="Example progress bar"></fluent-progress>
|
||||
<fluent-menu aria-label="Example menu">
|
||||
<fluent-menu-item role="menuitem" aria-label="Example menu item">
|
||||
Menu item 1
|
||||
</fluent-menu-item>
|
||||
<fluent-menu-item role="menuitem" aria-label="Example menu item">
|
||||
Menu item 2
|
||||
</fluent-menu-item>
|
||||
<fluent-menu-item role="menuitem" aria-label="Example menu item">
|
||||
Menu item 3
|
||||
</fluent-menu-item>
|
||||
<fluent-divider></fluent-divider>
|
||||
<fluent-menu-item role="menuitem" aria-label="Example menu item">
|
||||
Menu item 4
|
||||
</fluent-menu-item>
|
||||
</fluent-menu>
|
||||
<div class="control-container">
|
||||
<fluent-radio-group
|
||||
class="example-radios"
|
||||
name="example radio group"
|
||||
orientation="vertical"
|
||||
>
|
||||
<fluent-radio aria-label="Example radio 1">Radio 1</fluent-radio>
|
||||
<fluent-radio aria-label="Example radio 2">Radio 2</fluent-radio>
|
||||
</fluent-radio-group>
|
||||
<div class="control-container-grid">
|
||||
<fluent-switch aria-label="Example toggle">Toggle</fluent-switch>
|
||||
<fluent-checkbox class="checkbox" aria-label="Example checkbox">
|
||||
Checkbox
|
||||
</fluent-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<fluent-text-field
|
||||
placeholder="Text field"
|
||||
aria-label="Example text field"
|
||||
></fluent-text-field>
|
||||
<div class="control-container-2">
|
||||
<fluent-slider aria-label="Example slider"></fluent-slider>
|
||||
<fluent-flipper></fluent-flipper>
|
||||
<fluent-flipper disabled></fluent-flipper>
|
||||
</div>
|
||||
<div class="control-container">
|
||||
<fluent-button appearance="accent" aria-label="Example 'download' button">
|
||||
Button
|
||||
<span slot="start">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.5 17a.5.5 0 01.09 1H4.5a.5.5 0 01-.09-1H15.5zM10 2a.5.5 0 01.5.41V14.3l3.64-3.65a.5.5 0 01.64-.06l.07.06c.17.17.2.44.06.63l-.06.07-4.5 4.5a.5.5 0 01-.25.14L10 16a.5.5 0 01-.4-.2l-4.46-4.45a.5.5 0 01.64-.76l.07.06 3.65 3.64V2.5c0-.27.22-.5.5-.5z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</fluent-button>
|
||||
<fluent-button appearance="neutral" aria-label="Example 'play' button">
|
||||
Button
|
||||
<span slot="start">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M17.22 8.69a1.5 1.5 0 010 2.62l-10 5.5A1.5 1.5 0 015 15.5v-11A1.5 1.5 0 017.22 3.2l10 5.5zm-.48 1.75a.5.5 0 000-.88l-10-5.5A.5.5 0 006 4.5v11c0 .38.4.62.74.44l10-5.5z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</fluent-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import { FASTElement } from "@microsoft/fast-element";
|
||||
|
||||
export class SamplePage extends FASTElement {}
|
|
@ -1,217 +0,0 @@
|
|||
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<T extends AppSwatch>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<div
|
||||
class="icon"
|
||||
style="${x => x.iconStyle}"
|
||||
title="${x => x.contrastMessage}"
|
||||
></div>
|
||||
<code class="recipe-name">${x => x.recipeName}</code>
|
||||
<code class="hex-code">${x => x.colorValue}</code>
|
||||
`;
|
||||
}
|
||||
|
||||
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<Swatch>;
|
||||
public foregroundRecipeChanged() {
|
||||
this.updateObservables();
|
||||
}
|
||||
|
||||
@observable
|
||||
public fillRecipe?: DesignToken<Swatch>;
|
||||
public fillRecipeChanged() {
|
||||
this.updateObservables();
|
||||
}
|
||||
|
||||
@observable
|
||||
public outlineRecipe?: DesignToken<Swatch>;
|
||||
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<Swatch>): string {
|
||||
return token && typeof (token as any).createCSS === "function"
|
||||
? (token as any).createCSS()
|
||||
: "";
|
||||
}
|
||||
|
||||
private evaluateToken(token?: DesignToken<Swatch>): 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<Swatch>, b?: DesignToken<Swatch>): string {
|
||||
return a && b
|
||||
? contrastRatio(
|
||||
parseColor(this.evaluateToken(a))!,
|
||||
parseColor(this.evaluateToken(b))!
|
||||
).toFixed(2)
|
||||
: "";
|
||||
}
|
||||
|
||||
private formatBackgroundContrast(
|
||||
a?: DesignToken<Swatch>,
|
||||
b?: DesignToken<Swatch>
|
||||
): string {
|
||||
return `BG contrast: ${this.formatContrast(a, b)} : 1`;
|
||||
}
|
||||
|
||||
private formatForegroundContrast(
|
||||
a?: DesignToken<Swatch>,
|
||||
b?: DesignToken<Swatch>
|
||||
): 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();
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export * from "./components/index.js";
|
||||
export * from "./app.js";
|
||||
export * from "./component-type.js";
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"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"]
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
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: "<style>html { height: 100%; font-family: sans-serif; } body { margin: 0; height: 100%; display: flex; }</style> <app-main></app-main>",
|
||||
}),
|
||||
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()]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
# 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
|
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
extends: ["@microsoft/eslint-config-fast-dna", "prettier"],
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/typedef": "off",
|
||||
"no-unused-expressions": "off",
|
||||
},
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
package-lock=false
|
|
@ -1,18 +0,0 @@
|
|||
# FAST Figma Plugin MSFT
|
||||
A Figma Plugin for designing MSFT components and experiences.
|
||||
|
||||
|
||||
## Building
|
||||
To build the plugin, run `yarn build`. Then follow [Figma's documentation](https://help.figma.com/article/331-making-plugins) for importing the plugin into Figma.
|
||||
|
||||
You can also use `yarn watch` to watch files and rebuild the plugin when files are changed.
|
||||
|
||||
## Usage
|
||||
This plugin enables using certain parts of FAST's adaptive UI system in Figma. Currently, it supports assigning color recipes to the following:
|
||||
- background fills
|
||||
- stroke fills
|
||||
- text fills
|
||||
|
||||
With the plugin open, selecting a node in Figma will cause the available options for that node type to be reflected in the UI. Assigning a recipe to the node will apply change the color of the node and also store the assigned recipe on the node.
|
||||
|
||||
Assigning a recipe to a node opts the node into the adaptive system, and assigning a background fill recipe higher in the Figma node tree will inform what the output color the downstream recipe is. Changing upstream nodes will cause all nodes downstream that have been assigned a recipe type to be re-evaluated, allowing you to keep an entire tree of nodes in sync with their background fill.
|
Двоичные данные
packages/tooling/fast-figma-plugin-msft/cover.png
До Ширина: | Высота: | Размер: 824 KiB |
Двоичные данные
packages/tooling/fast-figma-plugin-msft/logo.png
До Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "Fluent UI WC Designer (Alpha)",
|
||||
"api": "1.0.0",
|
||||
"main": "./dist/main.js",
|
||||
"ui": "./dist/index.html",
|
||||
"id": "1081287243644186292",
|
||||
"editorType": [
|
||||
"figma"
|
||||
]
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/fast-figma-plugin-msft",
|
||||
"version": "0.8.16",
|
||||
"description": "A Figma plugin for designing MSFT experiences",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --progress --mode=production",
|
||||
"build:dev": "webpack --progress",
|
||||
"watch": "webpack --progress --watch",
|
||||
"clean:dist": "node ../../../build/clean.js dist",
|
||||
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.ts\"",
|
||||
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.ts\" --list-different",
|
||||
"test": "yarn eslint && yarn build",
|
||||
"eslint": "eslint . --ext .ts",
|
||||
"eslint:fix": "eslint . --ext .ts --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Microsoft/fast.git",
|
||||
"directory": "packages/tooling/fast-figma-plugin-msft"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/fast/issues/new/choose"
|
||||
},
|
||||
"author": {
|
||||
"name": "Microsoft",
|
||||
"url": "https://discord.gg/FcSNfg4"
|
||||
},
|
||||
"homepage": "https://www.fast.design/",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@figma/plugin-typings": "^1.47.0",
|
||||
"@types/node": "^15.0.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
|
||||
"prettier": "2.8.8",
|
||||
"resolve-typescript-plugin": "^1.2.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.7.0",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui/web-components": "^2.0.0"
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
import { AppliedDesignTokens, AppliedRecipes, RecipeEvaluations } from "./model.js";
|
||||
import { PluginNode } from "./node.js";
|
||||
import { PluginUINodeData } from "./ui/ui-controller.js";
|
||||
|
||||
export interface PluginUIState {
|
||||
selectedNodes: PluginUINodeData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller class designed to handle communication between the plugin and the design tool.
|
||||
* The controller is designed to be agnostic to the design environment,
|
||||
* relying on the abstract properties and methods to supply the implementation
|
||||
* details that might exist for the ecosystem it is being run in. (Figma, Sketch, etc).
|
||||
*/
|
||||
export abstract class Controller {
|
||||
/**
|
||||
* Track the currently selected node.
|
||||
*/
|
||||
private _selectedNodeIds: string[] = [];
|
||||
|
||||
// public static nodeCount: number = 0;
|
||||
|
||||
/**
|
||||
* Retrieve a Node from the design tool by ID.
|
||||
* @param id The ID of the node.
|
||||
* @returns Returns the PluginNode or null if no node by the provided ID exists.
|
||||
*/
|
||||
public abstract getNode(id: string): PluginNode | null;
|
||||
|
||||
/**
|
||||
* Set the selected node IDs - Setting the IDs will trigger a UI refresh.
|
||||
* @param ids The node IDs.
|
||||
*/
|
||||
public setSelectedNodes(ids: string[]): void {
|
||||
this._selectedNodeIds = ids;
|
||||
// Controller.nodeCount = 0;
|
||||
|
||||
// console.log("--------------------------------");
|
||||
// console.log("Controller.setSelectedNodes begin - selected nodes", ids);
|
||||
|
||||
// const timeStart = new Date().getTime();
|
||||
|
||||
this.setPluginUIState(this.getPluginUIState());
|
||||
|
||||
// const timeEnd = new Date().getTime();
|
||||
// const timeDiff = timeEnd - timeStart;
|
||||
// console.log("Controller.setSelectedNodes end - timing", timeDiff, "node count", Controller.nodeCount);
|
||||
// console.log("--------------------------------");
|
||||
}
|
||||
|
||||
private pluginNodesToUINodes(
|
||||
nodes: PluginNode[],
|
||||
includeInherited: boolean
|
||||
): PluginUINodeData[] {
|
||||
const convertedNodes = nodes.map(
|
||||
(node): PluginUINodeData => {
|
||||
// TODO Not all children, only nodes with design tokens or recipes.
|
||||
const children = this.pluginNodesToUINodes(node.children(), false);
|
||||
const inheritedDesignTokens = includeInherited
|
||||
? node.inheritedDesignTokens
|
||||
: new AppliedDesignTokens();
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
supports: node.supports(),
|
||||
additionalData: node.additionalData,
|
||||
children,
|
||||
inheritedDesignTokens,
|
||||
componentDesignTokens: node.componentDesignTokens,
|
||||
componentRecipes: node.componentRecipes,
|
||||
recipes: node.recipes as AppliedRecipes,
|
||||
designTokens: node.localDesignTokens as AppliedDesignTokens,
|
||||
recipeEvaluations: node.recipeEvaluations as RecipeEvaluations,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return convertedNodes;
|
||||
}
|
||||
|
||||
private getPluginUIState(): PluginUIState {
|
||||
const selectedNodes = this._selectedNodeIds
|
||||
.map(id => this.getNode(id))
|
||||
.filter((node): node is PluginNode => node !== null);
|
||||
|
||||
return {
|
||||
selectedNodes: this.pluginNodesToUINodes(selectedNodes, true),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the updated nodes that are posted from the UI.
|
||||
* @param nodes The returned from the UI.
|
||||
*/
|
||||
public handleMessage(nodes: PluginUINodeData[]): void {
|
||||
// console.log("--------------------------------");
|
||||
// console.log("Controller.handleMessage begin", nodes);
|
||||
|
||||
// const timeStart = new Date().getTime();
|
||||
|
||||
this.syncPluginNodes(nodes);
|
||||
|
||||
// const timeEnd = new Date().getTime();
|
||||
// const timeDiff = timeEnd - timeStart;
|
||||
// console.log("Controller.handleMessage end - timing", timeDiff);
|
||||
// console.log("--------------------------------");
|
||||
}
|
||||
|
||||
private syncPluginNodes(nodes: PluginUINodeData[]) {
|
||||
nodes.forEach(node => {
|
||||
const pluginNode = this.getNode(node.id);
|
||||
if (pluginNode) {
|
||||
pluginNode.handleManualDarkMode();
|
||||
pluginNode.setDesignTokens(node.designTokens);
|
||||
pluginNode.setRecipes(node.recipes);
|
||||
pluginNode.setRecipeEvaluations(node.recipeEvaluations);
|
||||
|
||||
// Paint all recipes of the node
|
||||
pluginNode.recipeEvaluations.forEach((evaluations, recipeId) => {
|
||||
// console.log("recipe eval", recipeId, evaluations);
|
||||
|
||||
evaluations.forEach(evaluation => {
|
||||
pluginNode.paint(evaluation);
|
||||
});
|
||||
});
|
||||
|
||||
this.syncPluginNodes(node.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the state object to the UI component and updates the UI
|
||||
* @param state the UI state object
|
||||
*/
|
||||
protected abstract setPluginUIState(state: PluginUIState): void;
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
import { DesignTokenType } from "./ui/design-token-registry.js";
|
||||
|
||||
/**
|
||||
* A key for passing the fill color from the tool to the plugin. Keeping it out of main design tokens to avoid a lo more special handling.
|
||||
*/
|
||||
export const TOOL_FILL_COLOR_TOKEN = "fillColor";
|
||||
|
||||
/**
|
||||
* Optional scoping for a design token value. For instance, apply a token to a particular component or all "rest" states of any component.
|
||||
*/
|
||||
export class DesignTokenScope {
|
||||
public component?: string;
|
||||
public part?: string;
|
||||
public state?: string;
|
||||
public appearance?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A design token value applied to a node and optionally scoped.
|
||||
*/
|
||||
export class AppliedDesignToken extends DesignTokenScope {
|
||||
public value?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A recipe applied to a node and optionally scoped.
|
||||
*/
|
||||
export class AppliedRecipe extends DesignTokenScope {}
|
||||
|
||||
/**
|
||||
* An attribute + value pair from an evaluated recipe.
|
||||
*/
|
||||
export class RecipeEvaluation {
|
||||
public type: DesignTokenType;
|
||||
public value: string;
|
||||
|
||||
constructor(type: DesignTokenType, value: string) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
function mapReplacer(key: string, value: any) {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
dataType: "Map",
|
||||
value: [...value],
|
||||
};
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function mapReviver(key: string, value: any) {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (value.dataType === "Map") {
|
||||
return new Map(value.value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Map that can be serialized to JSON and deserialized with correct typing.
|
||||
*/
|
||||
export class SerializableMap<K, V> extends Map<K, V> {
|
||||
public deserialize(json: string | undefined): void {
|
||||
if (this.size > 0) {
|
||||
throw "There are already entries in this Map. Expected empty Map.";
|
||||
}
|
||||
|
||||
if (json) {
|
||||
try {
|
||||
const map = JSON.parse(json as string, mapReviver) as Map<K, V>;
|
||||
map.forEach((v, k) => {
|
||||
this.set(k, v);
|
||||
});
|
||||
} catch (e) {
|
||||
// console.warn(e);
|
||||
// Ignore, empty string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public serialize(): string {
|
||||
const json = JSON.stringify(this, mapReplacer);
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of design tokens applied to a node. The key is the design token ID.
|
||||
*/
|
||||
export class AppliedDesignTokens extends SerializableMap<string, AppliedDesignToken> {}
|
||||
|
||||
/**
|
||||
* Readonly Map of design tokens applied to a node. The key is the design token ID.
|
||||
*/
|
||||
export type ReadonlyAppliedDesignTokens = ReadonlyMap<string, AppliedDesignToken>;
|
||||
|
||||
/**
|
||||
* Map of recipes applied to a node. The key is the recipe ID.
|
||||
*/
|
||||
export class AppliedRecipes extends SerializableMap<string, AppliedRecipe> {}
|
||||
|
||||
/**
|
||||
* Readonly Map of recipes applied to a node. The key is the recipe ID.
|
||||
*/
|
||||
export type ReadonlyAppliedRecipes = ReadonlyMap<string, AppliedRecipe>;
|
||||
|
||||
/**
|
||||
* Map of recipe evaluations applied to a node. The key is the recipe ID.
|
||||
*/
|
||||
export class RecipeEvaluations extends SerializableMap<string, Array<RecipeEvaluation>> {}
|
||||
|
||||
/**
|
||||
* Readonly Map of recipe evaluations applied to a node. The key is the recipe ID.
|
||||
*/
|
||||
export type ReadonlyRecipeEvaluations = ReadonlyMap<string, Array<RecipeEvaluation>>;
|
||||
|
||||
/**
|
||||
* Map of additional data exchanged between the design tool and plugin. Not persisted.
|
||||
*/
|
||||
export class AdditionalData extends SerializableMap<string, any> {}
|
||||
|
||||
/**
|
||||
* Defines the data stored by the plugin on a node instance.
|
||||
*/
|
||||
export interface PluginNodeData {
|
||||
/**
|
||||
* Design token overrides applied directly to the node.
|
||||
*/
|
||||
designTokens: AppliedDesignTokens;
|
||||
|
||||
/**
|
||||
* Recipes applied directly to the node.
|
||||
*/
|
||||
recipes: AppliedRecipes;
|
||||
|
||||
/**
|
||||
* Attribute and value pairs applied to the node evaluated from local and inherited recipes.
|
||||
*/
|
||||
recipeEvaluations: RecipeEvaluations;
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
import { ColorRGBA64 } from "@microsoft/fast-colors";
|
||||
import {
|
||||
AdditionalData,
|
||||
AppliedDesignTokens,
|
||||
AppliedRecipes,
|
||||
PluginNodeData,
|
||||
ReadonlyAppliedDesignTokens,
|
||||
ReadonlyAppliedRecipes,
|
||||
ReadonlyRecipeEvaluations,
|
||||
RecipeEvaluation,
|
||||
RecipeEvaluations,
|
||||
TOOL_FILL_COLOR_TOKEN,
|
||||
} from "./model.js";
|
||||
import { DesignTokenType } from "./ui/design-token-registry.js";
|
||||
|
||||
const DesignTokenCache: Map<string, ReadonlyAppliedDesignTokens> = new Map();
|
||||
|
||||
/**
|
||||
* The abstract class the plugin Controller interacts with.
|
||||
* Acts as a basic intermediary for node structure and data storage only.
|
||||
* Implementation details of this class will need to be created
|
||||
* for each design tool.
|
||||
*/
|
||||
export abstract class PluginNode {
|
||||
protected _recipes: AppliedRecipes = new AppliedRecipes();
|
||||
protected _componentDesignTokens?: AppliedDesignTokens;
|
||||
protected _componentRecipes?: AppliedRecipes;
|
||||
protected _localDesignTokens: AppliedDesignTokens = new AppliedDesignTokens();
|
||||
protected _recipeEvaluations: RecipeEvaluations = new RecipeEvaluations();
|
||||
protected _additionalData: AdditionalData = new AdditionalData();
|
||||
|
||||
/**
|
||||
* Retrieves the design token overrides on ancestor nodes.
|
||||
*/
|
||||
public get inheritedDesignTokens(): ReadonlyAppliedDesignTokens {
|
||||
// Return value from the cache if we have it
|
||||
if (DesignTokenCache.has(this.id)) {
|
||||
return DesignTokenCache.get(this.id) as ReadonlyAppliedDesignTokens;
|
||||
}
|
||||
|
||||
let designTokens = new AppliedDesignTokens();
|
||||
const parent = this.parent();
|
||||
if (parent !== null) {
|
||||
designTokens = new AppliedDesignTokens([
|
||||
...parent.inheritedDesignTokens,
|
||||
...(parent.componentDesignTokens
|
||||
? parent.componentDesignTokens
|
||||
: new AppliedDesignTokens()),
|
||||
...parent.localDesignTokens,
|
||||
]);
|
||||
}
|
||||
|
||||
// console.log(" PluginNode.inheritedDesignTokens", this.id, this.type, designTokens.entries());
|
||||
|
||||
DesignTokenCache.set(this.id, designTokens);
|
||||
|
||||
return designTokens;
|
||||
}
|
||||
|
||||
protected loadRecipes(): void {
|
||||
const json = this.getPluginData("recipes");
|
||||
// console.log(" loadRecipes", this.id, this.type, json);
|
||||
this._recipes.deserialize(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a readonly map of recipes applied to this node.
|
||||
*/
|
||||
public get recipes(): ReadonlyAppliedRecipes {
|
||||
return this._recipes as ReadonlyAppliedRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recipes to the node and design tool.
|
||||
* @param recipes The complete applied recipes map.
|
||||
*/
|
||||
public setRecipes(recipes: AppliedRecipes) {
|
||||
this._recipes = recipes;
|
||||
if (recipes.size) {
|
||||
const json = recipes.serialize();
|
||||
this.setPluginData("recipes", json);
|
||||
} else {
|
||||
this.deletePluginData("recipes");
|
||||
}
|
||||
}
|
||||
|
||||
public get componentRecipes(): ReadonlyAppliedRecipes | undefined {
|
||||
return this._componentRecipes as ReadonlyAppliedRecipes;
|
||||
}
|
||||
|
||||
protected loadLocalDesignTokens(): void {
|
||||
const json = this.getPluginData("designTokens");
|
||||
// console.log(" loadLocalDesignTokens", this.id, this.type, json);
|
||||
this._localDesignTokens.deserialize(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a readonly map of design token overrides applied to this node.
|
||||
*/
|
||||
public get localDesignTokens(): ReadonlyAppliedDesignTokens {
|
||||
return this._localDesignTokens;
|
||||
}
|
||||
|
||||
public get componentDesignTokens(): ReadonlyAppliedDesignTokens {
|
||||
return this._componentDesignTokens as ReadonlyAppliedDesignTokens;
|
||||
}
|
||||
|
||||
public abstract id: string;
|
||||
public abstract type: string;
|
||||
public abstract canHaveChildren(): boolean;
|
||||
public abstract children(): PluginNode[];
|
||||
public abstract parent(): PluginNode | null;
|
||||
public abstract supports(): Array<DesignTokenType>;
|
||||
|
||||
/**
|
||||
* Sets the design tokens to the node and design tool.
|
||||
* @param tokens The complete design tokens override map.
|
||||
*/
|
||||
public setDesignTokens(tokens: AppliedDesignTokens) {
|
||||
this._localDesignTokens = tokens;
|
||||
if (tokens.size) {
|
||||
const json = tokens.serialize();
|
||||
this.setPluginData("designTokens", json);
|
||||
} else {
|
||||
this.deletePluginData("designTokens");
|
||||
}
|
||||
|
||||
this.invalidateDesignTokenCache();
|
||||
}
|
||||
|
||||
protected loadRecipeEvaluations(): void {
|
||||
const json = this.getPluginData("recipeEvaluations");
|
||||
this._recipeEvaluations.deserialize(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a readonly map of recipe evaluations applied to this node.
|
||||
*/
|
||||
public get recipeEvaluations(): ReadonlyRecipeEvaluations {
|
||||
return this._recipeEvaluations;
|
||||
}
|
||||
|
||||
public setRecipeEvaluations(evaluations: RecipeEvaluations) {
|
||||
this._recipeEvaluations = evaluations;
|
||||
if (evaluations.size) {
|
||||
const json = evaluations.serialize();
|
||||
this.setPluginData("recipeEvaluations", json);
|
||||
} else {
|
||||
this.deletePluginData("recipeEvaluations");
|
||||
}
|
||||
}
|
||||
|
||||
public get additionalData(): AdditionalData {
|
||||
return this._additionalData;
|
||||
}
|
||||
|
||||
public abstract paint(data: RecipeEvaluation): void;
|
||||
|
||||
/**
|
||||
* Retrieve the effective fill color for the node.
|
||||
* This color is communicated to color recipes as the fillColor context for a node.
|
||||
*/
|
||||
public abstract getEffectiveFillColor(): ColorRGBA64 | null;
|
||||
|
||||
/**
|
||||
* Handle components that have custom dark mode configuration, like logos or illustration.
|
||||
*/
|
||||
public abstract handleManualDarkMode(): boolean;
|
||||
|
||||
/**
|
||||
* Setup special handling for fill color. It should either be a recipe or a fixed color applied in the design tool.
|
||||
* Must be called after design tokens and recipe evaluations are loaded.
|
||||
*/
|
||||
protected setupFillColor(): void {
|
||||
if (this.canHaveChildren()) {
|
||||
// console.log(" PluginNode.setupFillColor - checking", this.id, this.type);
|
||||
// If the fill color comes from a recipe, don't pass it again.
|
||||
let foundFill = false;
|
||||
this._recipeEvaluations.forEach(evaluations => {
|
||||
evaluations.forEach(evaluation => {
|
||||
// console.log(" evaluation", evaluation.type, "value", evaluation.value);
|
||||
if (
|
||||
evaluation.type === DesignTokenType.backgroundFill ||
|
||||
evaluation.type === DesignTokenType.layerFill
|
||||
) {
|
||||
foundFill = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!foundFill) {
|
||||
const nodeFillColor = this.getEffectiveFillColor();
|
||||
// console.log(" fill not found - effective color", nodeFillColor?.toStringHexRGB());
|
||||
if (nodeFillColor) {
|
||||
// console.log(" PluginNode.setupFillColor - setting", TOOL_FILL_COLOR_TOKEN, this.id, this.type, nodeFillColor.toStringHexRGB());
|
||||
this._additionalData.set(
|
||||
TOOL_FILL_COLOR_TOKEN,
|
||||
nodeFillColor.toStringHexRGB()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete entries in the design token cache for this node and any child nodes.
|
||||
*/
|
||||
private invalidateDesignTokenCache(): void {
|
||||
function getIds(node: PluginNode): string[] {
|
||||
let found = [node.id];
|
||||
|
||||
node.children().forEach((child: PluginNode) => {
|
||||
found = found.concat(getIds(child));
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
getIds(this).forEach((id: string) => DesignTokenCache.delete(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets custom data from the design tool storage.
|
||||
* @param key The data storage key.
|
||||
*/
|
||||
protected abstract getPluginData<K extends keyof PluginNodeData>(
|
||||
key: K
|
||||
): string | undefined;
|
||||
|
||||
/**
|
||||
* Sets custom data to the design tool storage.
|
||||
* @param key The data storage key.
|
||||
* @param value The new serialized value.
|
||||
*/
|
||||
protected abstract setPluginData<K extends keyof PluginNodeData>(
|
||||
key: K,
|
||||
value: string
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Deletes custom data from the design tool storage.
|
||||
* @param key The data storage key.
|
||||
*/
|
||||
protected abstract deletePluginData<K extends keyof PluginNodeData>(key: K): void;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M3.37 10.17a.5.5 0 0 0-.74.66l4 4.5c.19.22.52.23.72.02l10.5-10.5a.5.5 0 0 0-.7-.7L7.02 14.27l-3.65-4.1Z"/></svg>
|
До Ширина: | Высота: | Размер: 205 B |
|
@ -1 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 1v3H5V1h1Zm7.41 1.62a2 2 0 0 0-2.82 0L8.2 5l2.86 2.8 2.34-2.35a2 2 0 0 0 0-2.83Zm-3.53-.7a3 3 0 0 1 4.24 4.24L11.07 9.2 6.8 5l3.09-3.09ZM1 5h3v1H1V5Zm4 1.8L9.28 11l-3.16 3.16A3 3 0 0 1 1.88 9.9L5 6.8Zm0 1.4L2.6 10.62a2 2 0 1 0 2.82 2.83L7.86 11 5 8.2Zm7 1.8h3v1h-3v-1Zm-2 5v-3h1v3h-1Z"/></svg>
|
До Ширина: | Высота: | Размер: 429 B |
|
@ -1 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 15a7.24 7.24 0 0 0 6.94-5.15l.95.3a7.88 7.88 0 0 1-1.16 2.37 8.61 8.61 0 0 1-1.82 1.86c-.69.52-1.45.91-2.3 1.19A8.2 8.2 0 0 1 1 12.1V14H.01v-4h4v1H1.53a7.15 7.15 0 0 0 4.48 3.71c.63.19 1.3.28 1.99.29Zm8-13v4h-4V5h2.47a7.16 7.16 0 0 0-4.48-3.71 7.2 7.2 0 0 0-8.93 4.86l-.95-.3a8 8 0 0 1 2.98-4.22A8.17 8.17 0 0 1 8 0a8.2 8.2 0 0 1 7 3.9V2h1Z"/></svg>
|
До Ширина: | Высота: | Размер: 445 B |
|
@ -1 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M15 9.49a6.3 6.3 0 0 1-.9 3.28 6.72 6.72 0 0 1-2.33 2.34c-.48.27-1 .5-1.56.66a6.34 6.34 0 0 1-5.02-.66 6.73 6.73 0 0 1-2.34-2.33 7 7 0 0 1-.66-1.56 5.98 5.98 0 0 1-.23-1.73v-.5h1v.5a5.43 5.43 0 0 0 2.74 4.76 5.23 5.23 0 0 0 2.78.75 5.46 5.46 0 0 0 4.76-2.73A5.2 5.2 0 0 0 14 9.49a5.43 5.43 0 0 0-2.74-4.76 5.22 5.22 0 0 0-2.78-.75H1.9L4.2 6.26l-.7.7L0 3.48 3.49 0l.7.7-2.28 2.28h6.57a6.34 6.34 0 0 1 3.28.9 6.73 6.73 0 0 1 2.35 2.32c.27.48.5 1 .65 1.56.17.56.25 1.14.24 1.73Z"/></svg>
|
До Ширина: | Высота: | Размер: 577 B |
|
@ -1 +0,0 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="9.5" width="14" height="1" rx=".5"/></svg>
|
До Ширина: | Высота: | Размер: 141 B |
|
@ -1,87 +0,0 @@
|
|||
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
|
||||
import { neutralFillStealthHover, neutralForegroundHint } from "@fluentui/web-components";
|
||||
|
||||
const template = html<CornerRadius>`
|
||||
<template
|
||||
class="${x => x.orientation} ${x => (x.interactive ? "interactive" : "")} ${x =>
|
||||
x.selected ? "selected" : ""}"
|
||||
style="--radius: ${x => x.value}"
|
||||
tabindex="${x => (x.interactive ? "interactive" : null)}"
|
||||
aria-selected="${x => x.selected}"
|
||||
>
|
||||
<div class="indicator"></div>
|
||||
<slot></slot>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: ${neutralForegroundHint};
|
||||
}
|
||||
|
||||
.indicator {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border: 4px solid black;
|
||||
border-radius: calc(var(--radius) * 3px);
|
||||
}
|
||||
|
||||
:host(.vertical) {
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
:host(.vertical) .indicator {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
:host(.horizontal) .indicator {
|
||||
margin-inline-end: 12px;
|
||||
}
|
||||
|
||||
:host(.interactive) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host(.interactive:hover) {
|
||||
cursor: pointer;
|
||||
background: ${neutralFillStealthHover};
|
||||
}
|
||||
|
||||
:host(.selected),
|
||||
:host(.selected:hover) {
|
||||
background: #daebf7;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-corner-radius",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class CornerRadius extends FASTElement {
|
||||
@attr
|
||||
public value: string = "0";
|
||||
|
||||
@attr
|
||||
public orientation: "vertical" | "horizontal" = "vertical";
|
||||
|
||||
@attr({ mode: "boolean" })
|
||||
public interactive: boolean = false;
|
||||
|
||||
@attr({ mode: "boolean" })
|
||||
public selected: boolean = false;
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
import {
|
||||
css,
|
||||
customElement,
|
||||
ExecutionContext,
|
||||
FASTElement,
|
||||
html,
|
||||
observable,
|
||||
ref,
|
||||
repeat,
|
||||
when,
|
||||
} from "@microsoft/fast-element";
|
||||
import CheckmarkIcon from "../../assets/checkmark.svg";
|
||||
import { DesignTokenDefinition } from "../../design-token-registry.js";
|
||||
import { DesignTokenField } from "../design-token-field/index.js";
|
||||
|
||||
DesignTokenField;
|
||||
|
||||
const template = html<DesignTokenAdd>`
|
||||
<select @change="${(x, c) => x.selectHandler(c)}">
|
||||
<option selected value="-">Add design token override...</option>
|
||||
${repeat(
|
||||
x => x.designTokens,
|
||||
html<DesignTokenDefinition, DesignTokenAdd>`
|
||||
<option value="${x => x.id}">
|
||||
${x => x.name} (${x => x.groupTitle})
|
||||
</option>
|
||||
`
|
||||
)}
|
||||
</select>
|
||||
<div class="add-form" ?hidden="${x => !x.selectedDesignToken}">
|
||||
<td-design-token-field
|
||||
:designToken=${x => x.selectedDesignToken}
|
||||
${ref("field")}
|
||||
></td-design-token-field>
|
||||
<plugin-button
|
||||
appearance="stealth"
|
||||
aria-label="Add"
|
||||
title="Add"
|
||||
@click="${(x, c) => x.addHandler()}"
|
||||
>
|
||||
${CheckmarkIcon}
|
||||
</plugin-button>
|
||||
</div>
|
||||
${when(
|
||||
x => x.showMessageTemporary,
|
||||
html<DesignTokenAdd>`
|
||||
<p>
|
||||
Design token added. Please select another layer then come back to this to
|
||||
modify.
|
||||
</p>
|
||||
<p>This will be fixed asap.</p>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
select {
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add-form {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.add-form[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-design-token-add",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class DesignTokenAdd extends FASTElement {
|
||||
@observable
|
||||
designTokens: DesignTokenDefinition[] = [];
|
||||
|
||||
@observable
|
||||
selectedDesignToken?: DesignTokenDefinition;
|
||||
|
||||
selectedDesignTokenIndex: number;
|
||||
|
||||
field: DesignTokenField;
|
||||
|
||||
@observable
|
||||
showMessageTemporary: boolean;
|
||||
|
||||
designTokensChanged() {
|
||||
this.showMessageTemporary = false;
|
||||
}
|
||||
|
||||
selectHandler(c: ExecutionContext) {
|
||||
const selectedTokenId = (c.event.target as HTMLSelectElement).value;
|
||||
|
||||
if (selectedTokenId !== "-") {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.selectedDesignToken = this.designTokens.find((token, index) => {
|
||||
this.selectedDesignTokenIndex = index;
|
||||
return token.id === selectedTokenId;
|
||||
})!;
|
||||
if (this.field) {
|
||||
this.field.value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addHandler() {
|
||||
if (this.field.value) {
|
||||
// Remove the item from the list
|
||||
this.designTokens.splice(this.selectedDesignTokenIndex, 1);
|
||||
|
||||
this.$emit("add", {
|
||||
definition: this.selectedDesignToken,
|
||||
value: this.field.value,
|
||||
});
|
||||
|
||||
this.field.value = undefined;
|
||||
this.selectedDesignToken = undefined;
|
||||
|
||||
// Hack until rebuilt in web components to show a message to refresh selection.
|
||||
this.showMessageTemporary = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
import { StandardLuminance } from "@fluentui/web-components";
|
||||
import { ColorRGBA64, parseColorHexRGB } from "@microsoft/fast-colors";
|
||||
import {
|
||||
css,
|
||||
customElement,
|
||||
ExecutionContext,
|
||||
FASTElement,
|
||||
html,
|
||||
observable,
|
||||
} from "@microsoft/fast-element";
|
||||
import { DesignTokenDefinition, FormControlId } from "../../design-token-registry.js";
|
||||
|
||||
const defaultTokenTemplate = html<DesignTokenField>`
|
||||
<input
|
||||
value="${x => x.value}"
|
||||
@change="${(x, c) => {
|
||||
x.updateValue((c.event.target as HTMLInputElement).value);
|
||||
}}"
|
||||
/>
|
||||
`;
|
||||
|
||||
const colorInputChangeHandler = (x: DesignTokenField, c: ExecutionContext) => {
|
||||
const hex: string = (c.event.target as HTMLInputElement).value;
|
||||
const parsed = parseColorHexRGB(hex);
|
||||
if (parsed instanceof ColorRGBA64) {
|
||||
x.updateValue(hex);
|
||||
}
|
||||
};
|
||||
|
||||
const tokenTemplatesByType = {
|
||||
color: html<DesignTokenField>`
|
||||
<input
|
||||
type="color"
|
||||
id="${x => x.designToken?.id}"
|
||||
value="${x => x.value}"
|
||||
@change="${colorInputChangeHandler}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="${x => x.designToken?.id}Hex"
|
||||
class="hex"
|
||||
value="${x => x.value}"
|
||||
@change="${colorInputChangeHandler}"
|
||||
/>
|
||||
`,
|
||||
luminance: html<DesignTokenField>`
|
||||
<select
|
||||
@change="${(x, c) => {
|
||||
x.updateValue((c.event.target as HTMLSelectElement).value);
|
||||
}}"
|
||||
>
|
||||
<option></option>
|
||||
<option
|
||||
value="${StandardLuminance.LightMode}"
|
||||
?selected="${x =>
|
||||
Number.parseFloat(x.value) === StandardLuminance.LightMode}"
|
||||
>
|
||||
Light mode
|
||||
</option>
|
||||
<option
|
||||
value="${StandardLuminance.DarkMode}"
|
||||
?selected="${x =>
|
||||
Number.parseFloat(x.value) === StandardLuminance.DarkMode}"
|
||||
>
|
||||
Dark mode
|
||||
</option>
|
||||
</select>
|
||||
`,
|
||||
};
|
||||
|
||||
const template = html<DesignTokenField>`
|
||||
<label>
|
||||
<span>${x => x.designToken?.name}</span>
|
||||
${x => x.selectTemplate()}
|
||||
</label>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
input.hex {
|
||||
width: 80px;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-design-token-field",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class DesignTokenField extends FASTElement {
|
||||
@observable
|
||||
designToken?: DesignTokenDefinition;
|
||||
|
||||
@observable
|
||||
value?: any;
|
||||
|
||||
updateValue(value: any) {
|
||||
this.value = value;
|
||||
this.$emit("change", this.value);
|
||||
}
|
||||
|
||||
selectTemplate() {
|
||||
if (this.designToken) {
|
||||
if (this.designToken.formControlId === FormControlId.color) {
|
||||
return tokenTemplatesByType["color"];
|
||||
} else if (this.designToken.id === "baseLayerLuminance") {
|
||||
return tokenTemplatesByType["luminance"];
|
||||
}
|
||||
}
|
||||
return defaultTokenTemplate;
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
import {
|
||||
css,
|
||||
customElement,
|
||||
FASTElement,
|
||||
html,
|
||||
observable,
|
||||
repeat,
|
||||
when,
|
||||
} from "@microsoft/fast-element";
|
||||
import SubtractIcon from "../../assets/subtract.svg";
|
||||
import { UIDesignTokenValue } from "../../ui-controller.js";
|
||||
import { DesignTokenField } from "../design-token-field/index.js";
|
||||
|
||||
DesignTokenField;
|
||||
|
||||
const template = html<DesignTokensForm>`
|
||||
<ul>
|
||||
${repeat(
|
||||
x => x.designTokens,
|
||||
html<UIDesignTokenValue, DesignTokensForm>`
|
||||
<li>
|
||||
<td-design-token-field
|
||||
:designToken=${x => x.definition}
|
||||
:value=${x => x.value}
|
||||
@change="${(x, c) =>
|
||||
c.parent.changeHandler(x, c.event as CustomEvent)}"
|
||||
></td-design-token-field>
|
||||
<plugin-button
|
||||
appearance="stealth"
|
||||
aria-label="Remove design token"
|
||||
title="Remove design token"
|
||||
@click="${(x, c) => c.parent.detachHandler(x)}"
|
||||
>
|
||||
${SubtractIcon}
|
||||
</plugin-button>
|
||||
${when(
|
||||
x => x.multipleValues,
|
||||
html<UIDesignTokenValue>`
|
||||
<span class="values">${x => x.multipleValues}</span>
|
||||
`
|
||||
)}
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-design-tokens-form",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class DesignTokensForm extends FASTElement {
|
||||
@observable
|
||||
designTokens: UIDesignTokenValue[] = [];
|
||||
|
||||
changeHandler(token: UIDesignTokenValue, e: CustomEvent) {
|
||||
token.value = e.detail;
|
||||
this.$emit("tokenChange", token);
|
||||
}
|
||||
|
||||
detachHandler(token: UIDesignTokenValue) {
|
||||
// Remove the item from the list
|
||||
let detachIndex: number = -1;
|
||||
this.designTokens.find((curToken, index) => {
|
||||
detachIndex = index;
|
||||
return curToken === token;
|
||||
});
|
||||
this.designTokens.splice(detachIndex, 1);
|
||||
|
||||
this.$emit("detach", token.definition);
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
|
||||
import { StealthButton } from "../stealth-button/index.js";
|
||||
|
||||
StealthButton;
|
||||
|
||||
const template = html`
|
||||
<template class="${x => (x.expanded ? "expanded" : "collapsed")}">
|
||||
<div class="header">
|
||||
${x => x.name}
|
||||
<td-stealth-button
|
||||
class="expand-button"
|
||||
aria-label="Expand region"
|
||||
aria-controls="expanded-content collapsed-content"
|
||||
aria-expanded="${x => x.expanded.toString()}"
|
||||
@click="${(x, c) => x.handleExpandButtonClick(c.event)}"
|
||||
>
|
||||
<svg
|
||||
width="9"
|
||||
height="9"
|
||||
viewBox="0 0 9 9"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M0 1C0 0.447715 0.447715 0 1 0H2C2.55228 0 3 0.447715 3 1V2C3 2.55228 2.55228 3 2 3H1C0.447715 3 0 2.55228 0 2V1Z"
|
||||
/>
|
||||
<path
|
||||
d="M0 7C0 6.44772 0.447715 6 1 6H2C2.55228 6 3 6.44772 3 7V8C3 8.55228 2.55228 9 2 9H1C0.447715 9 0 8.55228 0 8V7Z"
|
||||
/>
|
||||
<path
|
||||
d="M6 1C6 0.447715 6.44772 0 7 0H8C8.55228 0 9 0.447715 9 1V2C9 2.55228 8.55228 3 8 3H7C6.44772 3 6 2.55228 6 2V1Z"
|
||||
/>
|
||||
<path
|
||||
d="M6 7C6 6.44772 6.44772 6 7 6H8C8.55228 6 9 6.44772 9 7V8C9 8.55228 8.55228 9 8 9H7C6.44772 9 6 8.55228 6 8V7Z"
|
||||
/>
|
||||
</svg>
|
||||
</td-stealth-button>
|
||||
</div>
|
||||
<div class="expanded-content" role="region" id="expanded-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="collapsed-content" role="region" id="collapsed-content">
|
||||
<slot name="collapsed-content"></slot>
|
||||
</div>
|
||||
<template></template>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
height: 48px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:host(.collapsed) .expanded-content,
|
||||
:host(.expanded) .collapsed-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host(.expanded) .expanded-content,
|
||||
:host(.collapsed) .collapsed-content {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-drawer",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class Drawer extends FASTElement {
|
||||
@attr({ mode: "boolean" })
|
||||
public expanded: boolean = false;
|
||||
|
||||
@attr
|
||||
public name: string = "";
|
||||
|
||||
private handleExpandButtonClick(): void {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export * from "./corner-radius/index.js";
|
||||
export * from "./design-token-add/index.js";
|
||||
export * from "./design-token-field/index.js";
|
||||
export * from "./design-tokens-form/index.js";
|
||||
export * from "./drawer/index.js";
|
||||
export * from "./stealth-button/index.js";
|
||||
export * from "./token-glyph/index.js";
|
|
@ -1,114 +0,0 @@
|
|||
import {
|
||||
controlCornerRadius,
|
||||
foregroundOnAccentRest,
|
||||
neutralFillStealthActive,
|
||||
neutralFillStealthHover,
|
||||
neutralFillStealthRest,
|
||||
neutralForegroundRest,
|
||||
neutralStrokeRest,
|
||||
} from "@fluentui/web-components";
|
||||
import { css, customElement, FASTElement, html } from "@microsoft/fast-element";
|
||||
|
||||
const template = html`
|
||||
<button>
|
||||
<slot id="glyph" name="glyph"></slot>
|
||||
<span><slot id="content"></slot></span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host(.has-glyph.has-content) span {
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: calc(${controlCornerRadius} * 1px);
|
||||
background: ${neutralFillStealthRest};
|
||||
fill: ${neutralForegroundRest};
|
||||
outline: none;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
border-color: ${neutralStrokeRest};
|
||||
}
|
||||
button:hover {
|
||||
background: ${neutralFillStealthHover};
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: ${neutralFillStealthActive};
|
||||
}
|
||||
|
||||
:host([aria-expanded="true"]) button {
|
||||
background: #18a0fb;
|
||||
fill: ${foregroundOnAccentRest};
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-stealth-button",
|
||||
template,
|
||||
styles,
|
||||
shadowOptions: {
|
||||
mode: "open",
|
||||
delegatesFocus: true,
|
||||
},
|
||||
})
|
||||
export class StealthButton extends FASTElement {
|
||||
public glyph?: HTMLSlotElement;
|
||||
public content?: HTMLSlotElement;
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.hasGlyph()) {
|
||||
this.classList.add("has-glyph");
|
||||
}
|
||||
|
||||
if (this.hasContent()) {
|
||||
this.classList.add("has-content");
|
||||
}
|
||||
}
|
||||
|
||||
private hasGlyph(): boolean {
|
||||
return this.slotHasContent(
|
||||
(this.shadowRoot as ShadowRoot).querySelector(
|
||||
"[id='glyph']"
|
||||
) as HTMLSlotElement
|
||||
);
|
||||
}
|
||||
|
||||
private hasContent(): boolean {
|
||||
return this.slotHasContent(
|
||||
(this.shadowRoot as ShadowRoot).querySelector(
|
||||
"[id='content']"
|
||||
) as HTMLSlotElement
|
||||
);
|
||||
}
|
||||
|
||||
private slotHasContent(slot: HTMLSlotElement): boolean {
|
||||
return (
|
||||
Array.from(slot.assignedNodes()).filter(node => {
|
||||
if (
|
||||
node.nodeType === 3 &&
|
||||
node.nodeValue &&
|
||||
node.nodeValue.trim().length === 0
|
||||
) {
|
||||
// Text node
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).length !== 0
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
|
||||
import {
|
||||
controlCornerRadius,
|
||||
neutralFillStealthHover,
|
||||
neutralForegroundHint,
|
||||
} from "@fluentui/web-components";
|
||||
|
||||
const template = html<TokenGlyph>`
|
||||
<template
|
||||
class="${x => x.orientation}
|
||||
${x => x.type}
|
||||
${x => (x.circular ? " circular" : "")}
|
||||
${x => (x.value === "none" ? "none" : "")}
|
||||
${x => (x.interactive ? "interactive" : "")}
|
||||
${x => (x.selected ? "selected" : "")}"
|
||||
style="--swatch-value: ${x => (x.value === "none" ? "transparent" : x.value)}"
|
||||
tabindex="${x => (x.interactive ? "0" : null)}"
|
||||
role="${x => (x.interactive ? "button" : null)}"
|
||||
aria-selected="${x => x.selected}"
|
||||
>
|
||||
<div class="${x => (x.type === TokenGlyphType.icon ? "icon" : "swatch")}">
|
||||
${x => x.icon || ""}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</template>
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: ${neutralForegroundHint};
|
||||
}
|
||||
|
||||
.swatch {
|
||||
box-sizing: border-box;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: calc(${controlCornerRadius} * 2px);
|
||||
background: var(--swatch-value);
|
||||
border: 1px solid var(--swatch-border-color, #e8e8e8);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.swatch:before {
|
||||
content: "";
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
box-sizing: border-box;
|
||||
background: var(--fill-color);
|
||||
border: 1px solid var(--swatch-border-color, #e8e8e8);
|
||||
border-radius: calc(${controlCornerRadius} * 2px);
|
||||
}
|
||||
|
||||
.swatch::after {
|
||||
content: "";
|
||||
display: none;
|
||||
width: 1px;
|
||||
height: 32px;
|
||||
background: #ff3366;
|
||||
left: calc(50% - 0.5px);
|
||||
position: relative;
|
||||
transform-origin: center;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host(.vertical) {
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
:host(.vertical) .swatch {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
:host(.horizontal) .swatch {
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
|
||||
:host(.circular) .swatch,
|
||||
:host(.circular) .swatch::before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:host(.border) .swatch::before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host(.none) .swatch::after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host(.interactive) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host(.interactive:hover) {
|
||||
cursor: pointer;
|
||||
background: ${neutralFillStealthHover};
|
||||
}
|
||||
|
||||
:host(.selected),
|
||||
:host(.selected:hover) {
|
||||
background: #daebf7;
|
||||
}
|
||||
`;
|
||||
|
||||
export enum TokenGlyphType {
|
||||
backgroundSwatch = "background",
|
||||
borderSwatch = "border",
|
||||
icon = "icon",
|
||||
}
|
||||
|
||||
@customElement({
|
||||
name: "td-token-glyph",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class TokenGlyph extends FASTElement {
|
||||
@attr
|
||||
public type: TokenGlyphType = TokenGlyphType.backgroundSwatch;
|
||||
|
||||
@attr({ mode: "boolean" })
|
||||
public circular: boolean = false;
|
||||
|
||||
@attr
|
||||
public value: string | "none" = "none";
|
||||
|
||||
@attr
|
||||
public icon?: string;
|
||||
|
||||
@attr
|
||||
public orientation: "horizontal" | "vertical" = "vertical";
|
||||
|
||||
@attr
|
||||
public label?: string;
|
||||
|
||||
@attr({ mode: "boolean" })
|
||||
public interactive: boolean = false;
|
||||
|
||||
@attr({ mode: "boolean" })
|
||||
public selected: boolean = false;
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
import {
|
||||
accentFillActiveDelta,
|
||||
accentFillFocusDelta,
|
||||
accentFillHoverDelta,
|
||||
accentFillRestDelta,
|
||||
accentForegroundActiveDelta,
|
||||
accentForegroundFocusDelta,
|
||||
accentForegroundHoverDelta,
|
||||
accentForegroundRestDelta,
|
||||
fillColor,
|
||||
InteractiveColorRecipe,
|
||||
InteractiveSwatchSet,
|
||||
Palette,
|
||||
PaletteRGB,
|
||||
Swatch,
|
||||
SwatchRGB,
|
||||
} from "@fluentui/web-components";
|
||||
import { DesignToken } from "@microsoft/fast-foundation";
|
||||
import { parseColorHexRGB } from "@microsoft/fast-colors";
|
||||
|
||||
// Local recipes for use in documentation files.
|
||||
|
||||
const target = (-0.1 + Math.sqrt(0.21)) / 2;
|
||||
|
||||
function isDark(color: Swatch): boolean {
|
||||
return color.relativeLuminance <= target;
|
||||
}
|
||||
|
||||
function directionByIsDark(color: Swatch): 1 | -1 {
|
||||
return isDark(color) ? -1 : 1;
|
||||
}
|
||||
|
||||
function contrastAndDeltaSwatchSet(
|
||||
palette: Palette,
|
||||
reference: Swatch,
|
||||
baseContrast: number,
|
||||
restDelta: number,
|
||||
hoverDelta: number,
|
||||
activeDelta: number,
|
||||
focusDelta: number,
|
||||
direction?: -1 | 1 | null
|
||||
): InteractiveSwatchSet {
|
||||
if (direction === null || direction === void 0) {
|
||||
direction = directionByIsDark(reference);
|
||||
}
|
||||
const baseIndex = palette.closestIndexOf(
|
||||
palette.colorContrast(reference, baseContrast)
|
||||
);
|
||||
|
||||
return {
|
||||
rest: palette.get(baseIndex + direction * restDelta),
|
||||
hover: palette.get(baseIndex + direction * hoverDelta),
|
||||
active: palette.get(baseIndex + direction * activeDelta),
|
||||
focus: palette.get(baseIndex + direction * focusDelta),
|
||||
};
|
||||
}
|
||||
|
||||
export const docBaseColor = DesignToken.create<Swatch>("doc-base-color").withDefault(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
SwatchRGB.from(parseColorHexRGB("#E1477E")!)
|
||||
);
|
||||
|
||||
export const docPalette = DesignToken.create<Palette>(
|
||||
"doc-palette"
|
||||
).withDefault((element: HTMLElement) =>
|
||||
PaletteRGB.from(docBaseColor.getValueFor(element) as SwatchRGB)
|
||||
);
|
||||
|
||||
export const docForegroundRecipe = DesignToken.create<InteractiveColorRecipe>(
|
||||
"doc-foreground-recipe"
|
||||
).withDefault({
|
||||
evaluate: (element: HTMLElement, reference?: Swatch): InteractiveSwatchSet =>
|
||||
contrastAndDeltaSwatchSet(
|
||||
docPalette.getValueFor(element),
|
||||
reference || fillColor.getValueFor(element),
|
||||
4.5,
|
||||
accentForegroundRestDelta.getValueFor(element),
|
||||
accentForegroundHoverDelta.getValueFor(element),
|
||||
accentForegroundActiveDelta.getValueFor(element),
|
||||
accentForegroundFocusDelta.getValueFor(element)
|
||||
),
|
||||
});
|
||||
|
||||
export const docForeground = DesignToken.create<Swatch>("doc-foreground").withDefault(
|
||||
(element: HTMLElement) =>
|
||||
docForegroundRecipe.getValueFor(element).evaluate(element).rest
|
||||
);
|
||||
|
||||
export const docFillRecipe = DesignToken.create<InteractiveColorRecipe>(
|
||||
"doc-fill-recipe"
|
||||
).withDefault({
|
||||
evaluate: (element: HTMLElement, reference?: Swatch): InteractiveSwatchSet =>
|
||||
contrastAndDeltaSwatchSet(
|
||||
docPalette.getValueFor(element),
|
||||
reference || fillColor.getValueFor(element),
|
||||
5,
|
||||
accentFillRestDelta.getValueFor(element),
|
||||
accentFillHoverDelta.getValueFor(element),
|
||||
accentFillActiveDelta.getValueFor(element),
|
||||
accentFillFocusDelta.getValueFor(element)
|
||||
),
|
||||
});
|
||||
|
||||
export const docFillRest = DesignToken.create<Swatch>("doc-fill-rest").withDefault(
|
||||
(element: HTMLElement) => {
|
||||
return docFillRecipe.getValueFor(element).evaluate(element).rest;
|
||||
}
|
||||
);
|
|
@ -1,119 +0,0 @@
|
|||
import { DesignToken } from "@microsoft/fast-foundation";
|
||||
|
||||
export enum DesignTokenType {
|
||||
designToken = "designToken",
|
||||
layerFill = "layerFill",
|
||||
backgroundFill = "backgroundFill",
|
||||
foregroundFill = "foregroundFill",
|
||||
strokeFill = "strokeFill",
|
||||
strokeWidth = "strokeWidth",
|
||||
cornerRadius = "cornerRadius",
|
||||
fontName = "fontName",
|
||||
fontSize = "fontSize",
|
||||
lineHeight = "lineHeight",
|
||||
}
|
||||
|
||||
export enum FormControlId {
|
||||
text = "text",
|
||||
color = "color",
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface where all keys of DesignTokenTypes map to a type
|
||||
*/
|
||||
export type MappedDesignTokenTypes<T> = { [K in keyof typeof DesignTokenType]: T };
|
||||
|
||||
/**
|
||||
* Defines a generic design token
|
||||
*/
|
||||
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
||||
export interface DesignTokenDefinition {
|
||||
/**
|
||||
* A title for organizing recipe sets
|
||||
*/
|
||||
groupTitle: string;
|
||||
|
||||
/**
|
||||
* The name of the design token
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Unique ID for the design token
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The type of design token
|
||||
*/
|
||||
type: DesignTokenType;
|
||||
|
||||
/**
|
||||
* The type of form control to edit this value. Following convention from fast-tooling.
|
||||
*/
|
||||
formControlId?: string;
|
||||
|
||||
/**
|
||||
* The underlying DesignToken for the plugin definition
|
||||
*/
|
||||
token: DesignToken<any>;
|
||||
}
|
||||
|
||||
export class DesignTokenRegistry {
|
||||
private entries: { [id: string]: DesignTokenDefinition } = {};
|
||||
|
||||
/**
|
||||
* Register a new design token
|
||||
* @param designToken the design token to register
|
||||
*/
|
||||
public register(designToken: DesignTokenDefinition): void {
|
||||
const { id } = designToken;
|
||||
|
||||
if (this.isRegistered(id)) {
|
||||
throw new Error(
|
||||
`Design token of id ${id} has already been registered. You must unregister the registered design token before registering with that ID.`
|
||||
);
|
||||
} else {
|
||||
this.entries[id] = designToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a design token
|
||||
* @param id - the ID of the design token to unregister
|
||||
*/
|
||||
public unregister(id: string): void {
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/* @ts-ignore-next-line no-implicit-any */
|
||||
delete this.register[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a design token definition by ID
|
||||
* @param id the id of the design token
|
||||
*/
|
||||
public get(id: string): DesignTokenDefinition | null {
|
||||
if (this.isRegistered(id)) {
|
||||
return this.entries[id];
|
||||
}
|
||||
|
||||
return null;
|
||||
// throw new Error(`Design token of id ${id} does not exist`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the design token has been registered
|
||||
* @param id - the id of the design token
|
||||
*/
|
||||
public isRegistered(id: string): boolean {
|
||||
return this.entries.hasOwnProperty(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all entries of a given design token type
|
||||
* @param type the design token type to return entries of
|
||||
*/
|
||||
public find(type: DesignTokenType): DesignTokenDefinition[] {
|
||||
return Object.values(this.entries).filter(value => value.type === type);
|
||||
}
|
||||
}
|
|
@ -1,551 +0,0 @@
|
|||
import {
|
||||
css,
|
||||
customElement,
|
||||
FASTElement,
|
||||
html,
|
||||
observable,
|
||||
repeat,
|
||||
when,
|
||||
} from "@microsoft/fast-element";
|
||||
import {
|
||||
DesignTokenAdd,
|
||||
DesignTokensForm,
|
||||
Drawer,
|
||||
TokenGlyph,
|
||||
TokenGlyphType,
|
||||
} from "./components/index.js";
|
||||
import { DesignTokenDefinition, DesignTokenType } from "./design-token-registry.js";
|
||||
import { PluginUINodeData, UIController, UIDesignTokenValue } from "./ui-controller.js";
|
||||
|
||||
TokenGlyph;
|
||||
Drawer;
|
||||
DesignTokenAdd;
|
||||
DesignTokensForm;
|
||||
|
||||
const assignedTokensTemplate = (
|
||||
tokens: DesignTokenDefinition[] | null,
|
||||
title: string | null,
|
||||
glyphType: TokenGlyphType = TokenGlyphType.backgroundSwatch
|
||||
) => html<App>`
|
||||
${when(
|
||||
x => tokens?.length,
|
||||
html<App>`
|
||||
${when(
|
||||
x => title,
|
||||
html`
|
||||
<p class="title inset">${x => title}</p>
|
||||
`
|
||||
)}
|
||||
${repeat(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
x => tokens!,
|
||||
html<DesignTokenDefinition, App>`
|
||||
<div class="applied-recipe">
|
||||
<td-token-glyph
|
||||
circular
|
||||
value=${(x, c) =>
|
||||
c.parent.controller.getDefaultDesignTokenValue(x.token)}
|
||||
orientation="horizontal"
|
||||
type="${x => glyphType}"
|
||||
>
|
||||
${x => x.name}
|
||||
</td-token-glyph>
|
||||
<span>
|
||||
${(x, c) =>
|
||||
c.parent.controller.getDefaultDesignTokenValue(x.token)}
|
||||
</span>
|
||||
<plugin-button
|
||||
appearance="stealth"
|
||||
aria-label="Detach"
|
||||
@click=${(x, c) => c.parent.controller.removeRecipe(x)}
|
||||
>
|
||||
Detach
|
||||
</plugin-button>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
const availableTokensTemplate = (
|
||||
tokenType: DesignTokenType,
|
||||
title: string | null,
|
||||
tokenLayout: "stack" | "grid" = "stack",
|
||||
glyphType: TokenGlyphType = TokenGlyphType.backgroundSwatch
|
||||
) => html<App>`
|
||||
${when(
|
||||
x => x.selectedNodes?.some(node => node.supports.includes(tokenType)),
|
||||
html<App>`
|
||||
${when(
|
||||
x => title,
|
||||
html`
|
||||
<p class="title inset">${x => title}</p>
|
||||
`
|
||||
)}
|
||||
<div class="swatch-${tokenLayout}">
|
||||
${repeat(
|
||||
x => x.controller.recipeOptionsByType(tokenType),
|
||||
html<DesignTokenDefinition, App>`
|
||||
<td-token-glyph
|
||||
circular
|
||||
value=${(x, c) =>
|
||||
c.parent.controller.getDefaultDesignTokenValue(x.token)}
|
||||
orientation="horizontal"
|
||||
type="${x => glyphType}"
|
||||
interactive
|
||||
?selected=${(x, c) =>
|
||||
c.parent.controller.recipeIsAssigned(x.id).length > 0}
|
||||
@click=${(x, c) => c.parent.controller.assignRecipe(x)}
|
||||
>
|
||||
${x => x.name}
|
||||
</td-token-glyph>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
const syncLabel =
|
||||
"Evaluate and apply all design tokens and recipes to the current selection.";
|
||||
const revertLabel = "Remove all plugin data from the current selection.";
|
||||
|
||||
const footerTemplate = html<App>`
|
||||
<footer>
|
||||
<p class="selection-label">
|
||||
${x =>
|
||||
x.selectedNodes?.map(node => `${node.type}`).join(" | ") ||
|
||||
"No selection"}
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<plugin-button
|
||||
appearance="accent"
|
||||
aria-label=${syncLabel}
|
||||
style="display: ${x => (x.controller.autoRefresh ? "none" : "")};"
|
||||
@click=${x => x.controller.refreshSelectedNodes()}
|
||||
>
|
||||
Sync
|
||||
</plugin-button>
|
||||
<plugin-button
|
||||
appearance="stealth"
|
||||
aria-label=${revertLabel}
|
||||
@click=${x => x.controller.resetNodes()}
|
||||
>
|
||||
Reset
|
||||
</plugin-button>
|
||||
</div>
|
||||
</footer>
|
||||
`;
|
||||
|
||||
const template = html<App>`
|
||||
<plugin-tabs activeid="recipes">
|
||||
<plugin-tab id="recipes">Recipes</plugin-tab>
|
||||
<plugin-tab id="tokens">Design Tokens</plugin-tab>
|
||||
<plugin-tab-panel id="recipesPanel">
|
||||
<div style="overflow-y: overlay;">
|
||||
${when(
|
||||
x => x.supportsColor,
|
||||
html<App>`
|
||||
<td-drawer name="Color">
|
||||
<div slot="collapsed-content">
|
||||
${x => assignedTokensTemplate(x.layerRecipes, "Layer")}
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.backgroundRecipes,
|
||||
"Background"
|
||||
)}
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.foregroundRecipes,
|
||||
"Foreground"
|
||||
)}
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.strokeRecipes,
|
||||
"Stroke",
|
||||
TokenGlyphType.borderSwatch
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.layerFill,
|
||||
"Layer backgrounds"
|
||||
)}
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.backgroundFill,
|
||||
"Fills"
|
||||
)}
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.strokeFill,
|
||||
"Strokes",
|
||||
"stack",
|
||||
TokenGlyphType.borderSwatch
|
||||
)}
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.foregroundFill,
|
||||
"Foregrounds"
|
||||
)}
|
||||
</div>
|
||||
</td-drawer>
|
||||
`
|
||||
)}
|
||||
${when(
|
||||
x => x.supportsStrokeWidth,
|
||||
html<App>`
|
||||
<td-drawer name="Stroke width">
|
||||
<div slot="collapsed-content">
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.strokeWidthRecipes,
|
||||
null,
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.strokeWidth,
|
||||
null,
|
||||
"stack",
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
</td-drawer>
|
||||
`
|
||||
)}
|
||||
${when(
|
||||
x => x.supportsCornerRadius,
|
||||
html<App>`
|
||||
<td-drawer name="Corner radius">
|
||||
<div slot="collapsed-content">
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.cornerRadiusRecipes,
|
||||
null,
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.cornerRadius,
|
||||
null,
|
||||
"stack",
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
</td-drawer>
|
||||
`
|
||||
)}
|
||||
${when(
|
||||
x => x.supportsText,
|
||||
html<App>`
|
||||
<td-drawer name="Text">
|
||||
<div slot="collapsed-content">
|
||||
${x =>
|
||||
assignedTokensTemplate(
|
||||
x.textRecipes,
|
||||
null,
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.fontName,
|
||||
"font name",
|
||||
"stack",
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.fontSize,
|
||||
"font size",
|
||||
"stack",
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
${x =>
|
||||
availableTokensTemplate(
|
||||
DesignTokenType.lineHeight,
|
||||
"Line height",
|
||||
"stack",
|
||||
TokenGlyphType.icon
|
||||
)}
|
||||
</div>
|
||||
</td-drawer>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</plugin-tab-panel>
|
||||
<plugin-tab-panel id="tokensPanel">
|
||||
${when(
|
||||
x => x.supportsDesignSystem,
|
||||
html<App>`
|
||||
<div class="tokens-panel-content">
|
||||
<div>
|
||||
<td-design-token-add
|
||||
:designTokens=${x => x.availableDesignTokens}
|
||||
@add=${(x, c) =>
|
||||
x.controller.assignDesignToken(
|
||||
(c.event as CustomEvent).detail.definition,
|
||||
(c.event as CustomEvent).detail.value
|
||||
)}
|
||||
></td-design-token-add>
|
||||
<plugin-divider></plugin-divider>
|
||||
</div>
|
||||
<div style="overflow-y: overlay;">
|
||||
<td-design-tokens-form
|
||||
:designTokens=${x => x.appliedDesignTokens}
|
||||
@tokenChange=${(x, c) =>
|
||||
x.controller.assignDesignToken(
|
||||
(c.event as CustomEvent).detail.definition,
|
||||
(c.event as CustomEvent).detail.value
|
||||
)}
|
||||
@detach=${(x, c) =>
|
||||
x.controller.removeDesignToken(
|
||||
(c.event as CustomEvent).detail
|
||||
)}
|
||||
></td-design-tokens-form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
${when(
|
||||
x => !x.supportsDesignSystem,
|
||||
html`
|
||||
<div>Selected layers don't support design tokens</div>
|
||||
`
|
||||
)}
|
||||
</plugin-tab-panel>
|
||||
</plugin-tabs>
|
||||
${x => footerTemplate}
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
plugin-tabs::part(tablist) {
|
||||
border-bottom: calc(var(--stroke-width) * 1px) solid
|
||||
var(--neutral-stroke-divider-rest);
|
||||
}
|
||||
|
||||
plugin-tab-panel {
|
||||
height: calc(556px - 40px);
|
||||
overflow-y: overlay;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
plugin-tab-panel::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
plugin-tab-panel::-webkit-scrollbar-track {
|
||||
margin: 1px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
plugin-tab-panel::-webkit-scrollbar-thumb {
|
||||
border: 2px solid #fff;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
plugin-tab-panel:hover::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.13);
|
||||
}
|
||||
|
||||
plugin-tab-panel::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(0, 0, 0, 0.13);
|
||||
}
|
||||
|
||||
.swatch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
justify-items: stretch;
|
||||
margin-inline-end: calc(var(--design-unit) * 2px);
|
||||
}
|
||||
|
||||
.swatch-grid > * {
|
||||
margin-bottom: calc(var(--design-unit) * 3px);
|
||||
}
|
||||
|
||||
.swatch-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.swatch-stack > * {
|
||||
padding: calc(var(--design-unit) * 2px) 0;
|
||||
padding-inline-start: calc(var(--design-unit) * 4px);
|
||||
padding-inline-end: calc(var(--design-unit) * 1px);
|
||||
}
|
||||
|
||||
.inset {
|
||||
padding: 0 calc(var(--design-unit) * 2px);
|
||||
}
|
||||
|
||||
.applied-recipe {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
padding-inline-start: calc(var(--design-unit) * 2px);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.applied-recipe > span {
|
||||
margin: 0 8px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tokens-panel-content {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 4px calc(var(--design-unit) * 2px) 4px calc(var(--design-unit) * 4px);
|
||||
}
|
||||
|
||||
footer .selection-label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
footer .buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
@customElement({
|
||||
name: "td-app",
|
||||
template,
|
||||
styles,
|
||||
})
|
||||
export class App extends FASTElement {
|
||||
@observable
|
||||
readonly controller: UIController;
|
||||
|
||||
@observable
|
||||
supportsColor: boolean;
|
||||
|
||||
@observable
|
||||
supportsStrokeWidth: boolean;
|
||||
|
||||
@observable
|
||||
supportsCornerRadius: boolean;
|
||||
|
||||
@observable
|
||||
supportsText: boolean;
|
||||
|
||||
@observable
|
||||
layerRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
backgroundRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
foregroundRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
strokeRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
strokeWidthRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
cornerRadiusRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
textRecipes: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
supportsDesignSystem: boolean;
|
||||
|
||||
@observable
|
||||
appliedDesignTokens: UIDesignTokenValue[] | null;
|
||||
|
||||
@observable
|
||||
availableDesignTokens: DesignTokenDefinition[] | null;
|
||||
|
||||
@observable
|
||||
selectedNodes: PluginUINodeData[] | null;
|
||||
selectedNodesChanged(prev: PluginUINodeData[], next: PluginUINodeData[]) {
|
||||
this.controller.setSelectedNodes(next);
|
||||
|
||||
this.supportsColor =
|
||||
this.selectedNodes?.some(
|
||||
node =>
|
||||
node.supports.includes(DesignTokenType.backgroundFill) ||
|
||||
node.supports.includes(DesignTokenType.foregroundFill) ||
|
||||
node.supports.includes(DesignTokenType.strokeFill)
|
||||
) || false;
|
||||
this.supportsStrokeWidth = this.controller.supports(DesignTokenType.strokeWidth);
|
||||
this.supportsCornerRadius = this.controller.supports(
|
||||
DesignTokenType.cornerRadius
|
||||
);
|
||||
this.supportsText = this.controller.supports(DesignTokenType.fontName);
|
||||
|
||||
this.layerRecipes = this.controller.appliedRecipes(DesignTokenType.layerFill);
|
||||
this.backgroundRecipes = this.controller.appliedRecipes(
|
||||
DesignTokenType.backgroundFill
|
||||
);
|
||||
this.foregroundRecipes = this.controller.appliedRecipes(
|
||||
DesignTokenType.foregroundFill
|
||||
);
|
||||
this.strokeRecipes = this.controller.appliedRecipes(DesignTokenType.strokeFill);
|
||||
this.strokeWidthRecipes = this.controller.appliedRecipes(
|
||||
DesignTokenType.strokeWidth
|
||||
);
|
||||
this.cornerRadiusRecipes = this.controller.appliedRecipes(
|
||||
DesignTokenType.cornerRadius
|
||||
);
|
||||
this.textRecipes = [
|
||||
...this.controller.appliedRecipes(DesignTokenType.fontName),
|
||||
...this.controller.appliedRecipes(DesignTokenType.fontSize),
|
||||
...this.controller.appliedRecipes(DesignTokenType.lineHeight),
|
||||
];
|
||||
|
||||
this.supportsDesignSystem = this.controller.supports(DesignTokenType.designToken);
|
||||
|
||||
// Get all applied design tokens except fillColor because it's handled through a recipe or plain color from the design tool.
|
||||
this.appliedDesignTokens = this.controller.appliedDesignTokens();
|
||||
//.filter(token => token.definition.id !== "fillColor");
|
||||
|
||||
// Get all design tokens that can be added, which is the full list except any already applied /* or fillColor (see above). */
|
||||
this.availableDesignTokens = this.controller.getDesignTokenDefinitions().filter(
|
||||
definition =>
|
||||
this.appliedDesignTokens!.find(
|
||||
appliedToken => appliedToken.definition.id === definition.id
|
||||
) || true //&& definition.id !== "fillColor"
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.controller = new UIController(nodes => this.dispatchState(nodes));
|
||||
}
|
||||
|
||||
private dispatchState(selectedNodes: PluginUINodeData[]): void {
|
||||
this.$emit("dispatch", selectedNodes);
|
||||
}
|
||||
}
|
|
@ -1,330 +0,0 @@
|
|||
import { DesignToken } from "@microsoft/fast-foundation";
|
||||
import {
|
||||
accentBaseColor,
|
||||
accentFillRest,
|
||||
accentForegroundRest,
|
||||
accentStrokeControlRest,
|
||||
baseLayerLuminance,
|
||||
bodyFont,
|
||||
controlCornerRadius,
|
||||
fillColor,
|
||||
focusStrokeInner,
|
||||
focusStrokeOuter,
|
||||
focusStrokeWidth,
|
||||
foregroundOnAccentRest,
|
||||
layerCornerRadius,
|
||||
neutralBaseColor,
|
||||
neutralFillInputAltRest,
|
||||
neutralFillInputRest,
|
||||
neutralFillLayerAltRest,
|
||||
neutralFillLayerRest,
|
||||
neutralFillRest,
|
||||
neutralFillSecondaryRest,
|
||||
neutralFillStealthRest,
|
||||
neutralFillStrongRest,
|
||||
neutralForegroundHint,
|
||||
neutralForegroundRest,
|
||||
neutralLayer1,
|
||||
neutralLayer2,
|
||||
neutralLayer3,
|
||||
neutralLayer4,
|
||||
neutralLayerCardContainer,
|
||||
neutralLayerFloating,
|
||||
neutralStrokeControlRest,
|
||||
neutralStrokeDividerRest,
|
||||
neutralStrokeInputRest,
|
||||
neutralStrokeLayerRest,
|
||||
neutralStrokeRest,
|
||||
neutralStrokeStrongRest,
|
||||
strokeWidth,
|
||||
typeRampBaseFontSize,
|
||||
typeRampBaseLineHeight,
|
||||
typeRampMinus1FontSize,
|
||||
typeRampMinus1LineHeight,
|
||||
typeRampMinus2FontSize,
|
||||
typeRampMinus2LineHeight,
|
||||
typeRampPlus1FontSize,
|
||||
typeRampPlus1LineHeight,
|
||||
typeRampPlus2FontSize,
|
||||
typeRampPlus2LineHeight,
|
||||
typeRampPlus3FontSize,
|
||||
typeRampPlus3LineHeight,
|
||||
typeRampPlus4FontSize,
|
||||
typeRampPlus4LineHeight,
|
||||
typeRampPlus5FontSize,
|
||||
typeRampPlus5LineHeight,
|
||||
typeRampPlus6FontSize,
|
||||
typeRampPlus6LineHeight,
|
||||
} from "@fluentui/web-components";
|
||||
import {
|
||||
DesignTokenDefinition,
|
||||
DesignTokenRegistry,
|
||||
DesignTokenType,
|
||||
FormControlId,
|
||||
} from "./design-token-registry.js";
|
||||
import { docBaseColor, docFillRest, docForeground } from "./custom-recipes.js";
|
||||
|
||||
interface DesignTokenStore {
|
||||
[key: string]: {
|
||||
name: string;
|
||||
token: DesignToken<any>;
|
||||
type?: DesignTokenType;
|
||||
formControlId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const designTokens: DesignTokenStore = {
|
||||
accentBaseColor: {
|
||||
token: accentBaseColor,
|
||||
name: "Accent color",
|
||||
formControlId: FormControlId.color,
|
||||
},
|
||||
neutralBaseColor: {
|
||||
token: neutralBaseColor,
|
||||
name: "Neutral color",
|
||||
formControlId: FormControlId.color,
|
||||
},
|
||||
baseLayerLuminance: { token: baseLayerLuminance, name: "Base layer luminance" },
|
||||
fillColor: {
|
||||
token: fillColor,
|
||||
name: "Fill color",
|
||||
formControlId: FormControlId.color,
|
||||
},
|
||||
docBaseColor: {
|
||||
token: docBaseColor,
|
||||
name: "Doc color",
|
||||
formControlId: FormControlId.color,
|
||||
},
|
||||
};
|
||||
|
||||
const layerRecipes: DesignTokenStore = {
|
||||
neutralLayerCardContainer: {
|
||||
token: neutralLayerCardContainer,
|
||||
name: "Card Container",
|
||||
},
|
||||
neutralLayerFloating: { token: neutralLayerFloating, name: "Floating" },
|
||||
neutralLayer1: { token: neutralLayer1, name: "Layer 1 (Primary)" },
|
||||
neutralLayer2: { token: neutralLayer2, name: "Layer 2" },
|
||||
neutralLayer3: { token: neutralLayer3, name: "Layer 3" },
|
||||
neutralLayer4: { token: neutralLayer4, name: "Layer 4" },
|
||||
};
|
||||
|
||||
const fillRecipes: DesignTokenStore = {
|
||||
accentFillRest: { token: accentFillRest, name: "Accent" },
|
||||
neutralFillRest: { token: neutralFillRest, name: "Neutral" },
|
||||
neutralFillLayerRest: { token: neutralFillLayerRest, name: "Neutral Layer" },
|
||||
neutralFillLayerAltRest: {
|
||||
token: neutralFillLayerAltRest,
|
||||
name: "Neutral Layer Alt",
|
||||
},
|
||||
neutralFillInputRest: { token: neutralFillInputRest, name: "Neutral Input" },
|
||||
neutralFillInputAltRest: {
|
||||
token: neutralFillInputAltRest,
|
||||
name: "Neutral Input Alt",
|
||||
},
|
||||
neutralFillSecondaryRest: {
|
||||
token: neutralFillSecondaryRest,
|
||||
name: "Neutral Secondary",
|
||||
},
|
||||
neutralFillStealthRest: { token: neutralFillStealthRest, name: "Neutral Stealth" },
|
||||
neutralFillStrongRest: { token: neutralFillStrongRest, name: "Neutral Strong" },
|
||||
docFillRest: { token: docFillRest, name: "Doc" },
|
||||
};
|
||||
|
||||
const strokeRecipes: DesignTokenStore = {
|
||||
accentStrokeControlRest: { token: accentStrokeControlRest, name: "Accent Control" },
|
||||
focusStrokeOuter: { token: focusStrokeOuter, name: "Focus Outer" },
|
||||
focusStrokeInner: { token: focusStrokeInner, name: "Focus Inner" },
|
||||
neutralStrokeDividerRest: { token: neutralStrokeDividerRest, name: "Divider" },
|
||||
neutralStrokeLayerRest: { token: neutralStrokeLayerRest, name: "Layer" },
|
||||
neutralStrokeRest: { token: neutralStrokeRest, name: "Neutral" },
|
||||
neutralStrokeStrongRest: { token: neutralStrokeStrongRest, name: "Neutral Strong" },
|
||||
neutralStrokeControlRest: {
|
||||
token: neutralStrokeControlRest,
|
||||
name: "Neutral Control",
|
||||
},
|
||||
neutralStrokeInputRest: { token: neutralStrokeInputRest, name: "Neutral Input" },
|
||||
};
|
||||
|
||||
const strokeWidthRecipes: DesignTokenStore = {
|
||||
strokeWidth: { token: strokeWidth, name: "Stroke width" },
|
||||
focusStrokeWidth: { token: focusStrokeWidth, name: "Focus stroke width" },
|
||||
};
|
||||
|
||||
const textFillRecipes: DesignTokenStore = {
|
||||
neutralForegroundRest: { token: neutralForegroundRest, name: "Neutral" },
|
||||
neutralForegroundHint: { token: neutralForegroundHint, name: "Hint" },
|
||||
accentForegroundRest: { token: accentForegroundRest, name: "Accent" },
|
||||
foregroundOnAccentRest: { token: foregroundOnAccentRest, name: "On Accent" },
|
||||
docForegroundRest: { token: docForeground, name: "Doc" },
|
||||
};
|
||||
|
||||
const cornerRadiusRecipes: DesignTokenStore = {
|
||||
controlCornerRadius: { token: controlCornerRadius, name: "Control" },
|
||||
layerCornerRadius: { token: layerCornerRadius, name: "Layer" },
|
||||
};
|
||||
|
||||
const textRecipes: DesignTokenStore = {
|
||||
bodyFont: { type: DesignTokenType.fontName, token: bodyFont, name: "Font" },
|
||||
typeRampPlus6FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus6FontSize,
|
||||
name: "Plus 6 font size",
|
||||
},
|
||||
typeRampPlus6LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus6LineHeight,
|
||||
name: "Plus 6 line height",
|
||||
},
|
||||
typeRampPlus5FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus5FontSize,
|
||||
name: "Plus 5 font size",
|
||||
},
|
||||
typeRampPlus5LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus5LineHeight,
|
||||
name: "Plus 5 line height",
|
||||
},
|
||||
typeRampPlus4FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus4FontSize,
|
||||
name: "Plus 4 font size",
|
||||
},
|
||||
typeRampPlus4LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus4LineHeight,
|
||||
name: "Plus 4 line height",
|
||||
},
|
||||
typeRampPlus3FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus3FontSize,
|
||||
name: "Plus 3 font size",
|
||||
},
|
||||
typeRampPlus3LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus3LineHeight,
|
||||
name: "Plus 3 line height",
|
||||
},
|
||||
typeRampPlus2FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus2FontSize,
|
||||
name: "Plus 2 font size",
|
||||
},
|
||||
typeRampPlus2LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus2LineHeight,
|
||||
name: "Plus 2 line height",
|
||||
},
|
||||
typeRampPlus1FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampPlus1FontSize,
|
||||
name: "Plus 1 font size",
|
||||
},
|
||||
typeRampPlus1LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampPlus1LineHeight,
|
||||
name: "Plus 1 line height",
|
||||
},
|
||||
typeRampBaseFontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampBaseFontSize,
|
||||
name: "Base font size",
|
||||
},
|
||||
typeRampBaseLineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampBaseLineHeight,
|
||||
name: "Base line height",
|
||||
},
|
||||
typeRampMinus1FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampMinus1FontSize,
|
||||
name: "Minus 1 font size",
|
||||
},
|
||||
typeRampMinus1LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampMinus1LineHeight,
|
||||
name: "Minus 1 line height",
|
||||
},
|
||||
typeRampMinus2FontSize: {
|
||||
type: DesignTokenType.fontSize,
|
||||
token: typeRampMinus2FontSize,
|
||||
name: "Minus 2 font size",
|
||||
},
|
||||
typeRampMinus2LineHeight: {
|
||||
type: DesignTokenType.lineHeight,
|
||||
token: typeRampMinus2LineHeight,
|
||||
name: "Minus 2 line height",
|
||||
},
|
||||
};
|
||||
|
||||
function registerStore(
|
||||
type: DesignTokenType | null,
|
||||
store: DesignTokenStore,
|
||||
title: string,
|
||||
registry: DesignTokenRegistry
|
||||
): void {
|
||||
Object.keys(store).forEach((key: string) => {
|
||||
const entry = store[key];
|
||||
|
||||
const entryType = type || entry.type;
|
||||
if (entryType === null) {
|
||||
throw `DesignTokenType not specified for ${key}`;
|
||||
}
|
||||
|
||||
const definition: DesignTokenDefinition = {
|
||||
id: key,
|
||||
name: entry.name,
|
||||
groupTitle: title,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
type: entryType!,
|
||||
formControlId: entry.formControlId,
|
||||
token: entry.token,
|
||||
};
|
||||
|
||||
registry.register(definition);
|
||||
});
|
||||
}
|
||||
|
||||
export const registerTokens = (registry: DesignTokenRegistry) => {
|
||||
registerStore(DesignTokenType.designToken, designTokens, "Global tokens", registry);
|
||||
// This could be optimized, but some tokens are intended to be modified as well as applied as recipes.
|
||||
registerStore(DesignTokenType.designToken, textRecipes, "Text", registry);
|
||||
registerStore(
|
||||
DesignTokenType.designToken,
|
||||
strokeWidthRecipes,
|
||||
"Stroke width",
|
||||
registry
|
||||
);
|
||||
registerStore(
|
||||
DesignTokenType.designToken,
|
||||
cornerRadiusRecipes,
|
||||
"Corner radius",
|
||||
registry
|
||||
);
|
||||
};
|
||||
|
||||
export const registerRecipes = (registry: DesignTokenRegistry) => {
|
||||
registerStore(DesignTokenType.layerFill, layerRecipes, "Layer fill", registry);
|
||||
registerStore(DesignTokenType.backgroundFill, fillRecipes, "Fill", registry);
|
||||
registerStore(
|
||||
DesignTokenType.foregroundFill,
|
||||
textFillRecipes,
|
||||
"Foreground",
|
||||
registry
|
||||
);
|
||||
registerStore(DesignTokenType.strokeFill, strokeRecipes, "Stroke", registry);
|
||||
registerStore(
|
||||
DesignTokenType.strokeWidth,
|
||||
strokeWidthRecipes,
|
||||
"Stroke width",
|
||||
registry
|
||||
);
|
||||
registerStore(
|
||||
DesignTokenType.cornerRadius,
|
||||
cornerRadiusRecipes,
|
||||
"Corner radius",
|
||||
registry
|
||||
);
|
||||
registerStore(null, textRecipes, "Text", registry);
|
||||
};
|
|
@ -1,541 +0,0 @@
|
|||
import { SwatchRGB } from "@fluentui/web-components";
|
||||
import { parseColorHexRGB } from "@microsoft/fast-colors";
|
||||
import { DesignToken, DesignTokenValue } from "@microsoft/fast-foundation";
|
||||
import {
|
||||
AdditionalData,
|
||||
AppliedDesignToken,
|
||||
AppliedRecipe,
|
||||
PluginNodeData,
|
||||
ReadonlyAppliedDesignTokens,
|
||||
ReadonlyAppliedRecipes,
|
||||
RecipeEvaluation,
|
||||
} from "../model.js";
|
||||
import {
|
||||
DesignTokenDefinition,
|
||||
DesignTokenRegistry,
|
||||
DesignTokenType,
|
||||
} from "./design-token-registry.js";
|
||||
import { registerRecipes, registerTokens } from "./recipes.js";
|
||||
|
||||
/**
|
||||
* Represents a Node on the UI side.
|
||||
*/
|
||||
export interface PluginUINodeData extends PluginNodeData {
|
||||
/**
|
||||
* The ID of the node
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The node type
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The recipe types that the node supports
|
||||
*/
|
||||
supports: Array<DesignTokenType>;
|
||||
|
||||
/**
|
||||
* For other transient data exchanged between the design tool and the plugin.
|
||||
*/
|
||||
additionalData: AdditionalData;
|
||||
|
||||
/**
|
||||
* The design token values inherited by this node from layer hierarchy.
|
||||
*/
|
||||
inheritedDesignTokens: ReadonlyAppliedDesignTokens;
|
||||
|
||||
/**
|
||||
* The design token values inherited by an instance node from the main component.
|
||||
*/
|
||||
componentDesignTokens?: ReadonlyAppliedDesignTokens;
|
||||
|
||||
/**
|
||||
* Recipes inherited by an instance node from the main component.
|
||||
*/
|
||||
componentRecipes?: ReadonlyAppliedRecipes;
|
||||
|
||||
/**
|
||||
* Children of this node that have design tokens or recipes applied.
|
||||
*/
|
||||
children: PluginUINodeData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple display information for representing design tokens applied to one or more Nodes.
|
||||
*/
|
||||
export interface UIDesignTokenValue {
|
||||
/**
|
||||
* The definition of the design token.
|
||||
*/
|
||||
definition: DesignTokenDefinition;
|
||||
|
||||
/**
|
||||
* Represents the design token value if all selected nodes have the same value.
|
||||
*/
|
||||
value?: string;
|
||||
|
||||
/**
|
||||
* If the selected nodes have multiple different values this will be a list for display.
|
||||
*/
|
||||
multipleValues?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Controller for the UI side of the plugin, which encapsulates the business logic of
|
||||
* applying design tokens and recipes and evaluating the changes for the selected nodes.
|
||||
*/
|
||||
export class UIController {
|
||||
private readonly _updateStateCallback: (
|
||||
selectedNodes: PluginUINodeData[]
|
||||
) => void | undefined;
|
||||
|
||||
// This is adapting the new token model to the previous plugin structure. Recipes are now just tokens,
|
||||
// but the separation is useful for now in that a token is where you set a value and a recipe you apply to some visual element.
|
||||
private readonly _designTokenRegistry: DesignTokenRegistry = new DesignTokenRegistry();
|
||||
private readonly _recipeRegistry: DesignTokenRegistry = new DesignTokenRegistry();
|
||||
|
||||
/**
|
||||
* The container for the elements created for each node so we can resolve values from the Design Token infrastructure.
|
||||
* We don't need to set values for every design token because for we'll get the token's withDefault value.
|
||||
*/
|
||||
private readonly _rootElement: HTMLElement;
|
||||
|
||||
private _selectedNodes: PluginUINodeData[] = [];
|
||||
|
||||
/**
|
||||
* Create a new UI controller.
|
||||
* @param updateStateCallback Callback function to handle updated design token and recipe application and evaluation.
|
||||
*/
|
||||
constructor(updateStateCallback: (selectedNodes: PluginUINodeData[]) => void) {
|
||||
this._updateStateCallback = updateStateCallback;
|
||||
|
||||
registerTokens(this._designTokenRegistry);
|
||||
registerRecipes(this._recipeRegistry);
|
||||
|
||||
this._rootElement = document.createElement("div");
|
||||
this._rootElement.id = "designTokenRoot";
|
||||
document.body.appendChild(this._rootElement);
|
||||
}
|
||||
|
||||
public get autoRefresh(): boolean {
|
||||
return !(
|
||||
this._selectedNodes.length === 1 && this._selectedNodes[0].type === "PAGE"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected nodes, which sets up the UI and immediately refreshes all recipe evaluations.
|
||||
* @param nodes The selected nodes.
|
||||
*/
|
||||
public setSelectedNodes(nodes: PluginUINodeData[]) {
|
||||
// console.log("--------------------------------");
|
||||
// console.log("UIController.setSelectedNodes", nodes);
|
||||
|
||||
this._selectedNodes = nodes;
|
||||
|
||||
this._rootElement.childNodes.forEach(child =>
|
||||
this._rootElement.removeChild(child)
|
||||
);
|
||||
nodes.forEach(node => this.setupDesignTokenElement(this._rootElement, node));
|
||||
|
||||
if (this.autoRefresh) {
|
||||
this.refreshSelectedNodes("setSelectedNodes");
|
||||
}
|
||||
}
|
||||
|
||||
public refreshSelectedNodes(reason: string = "refreshSelectedNodes"): void {
|
||||
this.evaluateRecipes(this._selectedNodes);
|
||||
|
||||
this.dispatchState(reason);
|
||||
}
|
||||
|
||||
public supports(type: DesignTokenType) {
|
||||
return this._selectedNodes.some(node => node.supports.includes(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a display representation of design tokens applied to the selected nodes.
|
||||
* @returns Applied design tokens.
|
||||
*/
|
||||
public appliedDesignTokens(): UIDesignTokenValue[] {
|
||||
const tokenValues = new Map<string, Set<string>>();
|
||||
const designTokens: UIDesignTokenValue[] = [];
|
||||
|
||||
this._selectedNodes.forEach(node =>
|
||||
node.designTokens.forEach((designToken, designTokenId) => {
|
||||
if (designToken.value) {
|
||||
const values = tokenValues.get(designTokenId) || new Set<string>();
|
||||
values.add(designToken.value);
|
||||
tokenValues.set(designTokenId, values);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const allDesignTokens = this._designTokenRegistry.find(
|
||||
DesignTokenType.designToken
|
||||
);
|
||||
|
||||
allDesignTokens.forEach(designToken => {
|
||||
if (tokenValues.has(designToken.id)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const set = tokenValues.get(designToken.id)!;
|
||||
designTokens.push({
|
||||
definition: designToken,
|
||||
value: set.size === 1 ? set.values().next().value : undefined,
|
||||
multipleValues: set.size > 1 ? [...set].join(", ") : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return designTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a display representation of recipes applied to the selected nodes.
|
||||
* @param type Recipe type.
|
||||
* @returns Applied recipes.
|
||||
*/
|
||||
public appliedRecipes(type: DesignTokenType): DesignTokenDefinition[] {
|
||||
const ids = new Set<string>();
|
||||
const recipes: DesignTokenDefinition[] = [];
|
||||
|
||||
this._selectedNodes.forEach(node =>
|
||||
node.recipes.forEach((recipe, recipeId) => ids.add(recipeId))
|
||||
);
|
||||
|
||||
const typeRecipes = this._recipeRegistry.find(type);
|
||||
|
||||
typeRecipes.forEach(recipe => {
|
||||
if (ids.has(recipe.id)) {
|
||||
recipes.push(recipe);
|
||||
}
|
||||
});
|
||||
|
||||
return recipes.filter(recipe => recipe.type === type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of recipes for the recipe type.
|
||||
* @param type Recipe type.
|
||||
* @returns List of available recipes.
|
||||
*/
|
||||
public recipeOptionsByType(type: DesignTokenType): DesignTokenDefinition[] {
|
||||
const val = this._recipeRegistry.find(type);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node IDs to which the recipe is assigned.
|
||||
* @param id - The recipe ID.
|
||||
*/
|
||||
public recipeIsAssigned(id: string): string[] {
|
||||
return this._selectedNodes
|
||||
.filter(node => {
|
||||
return Object.keys(node.recipes).includes(id);
|
||||
})
|
||||
.map(node => node.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all design tokens and recipes for the selected nodes.
|
||||
*/
|
||||
public resetNodes(): void {
|
||||
this._selectedNodes.forEach(node => {
|
||||
// console.log("--------------------------------");
|
||||
// console.log("reset", node);
|
||||
|
||||
node.designTokens.clear();
|
||||
node.recipes.clear();
|
||||
node.recipeEvaluations.clear();
|
||||
});
|
||||
|
||||
this.dispatchState("resetNodes");
|
||||
}
|
||||
|
||||
private evaluateRecipes(nodes: PluginUINodeData[]) {
|
||||
// console.log(" evaluateRecipes");
|
||||
nodes.forEach(node => {
|
||||
const allRecipeIds = [
|
||||
...(node.componentRecipes
|
||||
? node.componentRecipes.keys()
|
||||
: new Array<string>()),
|
||||
...node.recipes.keys(),
|
||||
];
|
||||
|
||||
allRecipeIds.reduceRight<Array<DesignTokenDefinition>>(
|
||||
(previousRecipes, currentId, index, array) => {
|
||||
// console.log(previousRecipes, currentId, index, array);
|
||||
const currentRecipe = this._recipeRegistry.get(currentId);
|
||||
if (
|
||||
currentRecipe &&
|
||||
!previousRecipes.find(value => value.type === currentRecipe.type)
|
||||
) {
|
||||
// console.log("adding", currentRecipe);
|
||||
this.evaluateRecipe(currentRecipe, node);
|
||||
previousRecipes.push(currentRecipe);
|
||||
}
|
||||
return previousRecipes;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
this.evaluateRecipes(node.children);
|
||||
});
|
||||
}
|
||||
|
||||
private evaluateRecipe(recipe: DesignTokenDefinition, node: PluginUINodeData): any {
|
||||
// console.log(" evaluateRecipe", recipe);
|
||||
let value: any = this.getDesignTokenValue(node, recipe.token);
|
||||
if (typeof (value as any).toColorString === "function") {
|
||||
value = (value as any).toColorString();
|
||||
}
|
||||
node.recipeEvaluations.set(recipe.id, [
|
||||
new RecipeEvaluation(recipe.type, (value as unknown) as string),
|
||||
]);
|
||||
// console.log(" evaluations", node.recipeEvaluations);
|
||||
|
||||
if (recipe.type === DesignTokenType.layerFill) {
|
||||
// console.log(" Fill recipe, setting fillColor design token");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const def = this._designTokenRegistry.get("fillColor")!;
|
||||
const element = this.getElementForNode(node);
|
||||
this.setDesignTokenForElement(element, def.token, value);
|
||||
|
||||
this.evaluateRecipes(node.children);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public removeRecipe(recipe: DesignTokenDefinition): void {
|
||||
this._selectedNodes.forEach(node => {
|
||||
node.recipes.delete(recipe.id);
|
||||
|
||||
node.recipeEvaluations.delete(recipe.id);
|
||||
|
||||
// console.log("--------------------------------");
|
||||
// console.log("removed recipe from node", recipe.id, node);
|
||||
});
|
||||
|
||||
this.evaluateRecipes(this._selectedNodes);
|
||||
|
||||
this.dispatchState("removeRecipe");
|
||||
}
|
||||
|
||||
public assignRecipe(recipe: DesignTokenDefinition): void {
|
||||
this._selectedNodes.forEach(node => {
|
||||
// console.log("--------------------------------");
|
||||
// console.log("assigning recipe to node", recipe);
|
||||
|
||||
// There should only be able to be one recipe for a type, but maybe add safety check.
|
||||
let recipeToRemove: string | null = null;
|
||||
node.recipeEvaluations.forEach((evaluationAttrs, evaluationRecipeId) => {
|
||||
const found = evaluationAttrs.find(
|
||||
evaluation => evaluation.type === recipe.type
|
||||
);
|
||||
if (found) {
|
||||
recipeToRemove = evaluationRecipeId;
|
||||
}
|
||||
});
|
||||
|
||||
if (recipeToRemove) {
|
||||
// console.log(" Removing recipe and evaluations for", recipeToRemove);
|
||||
node.recipes.delete(recipeToRemove);
|
||||
node.recipeEvaluations.delete(recipeToRemove);
|
||||
}
|
||||
|
||||
node.recipes.set(recipe.id, new AppliedRecipe());
|
||||
|
||||
this.evaluateRecipe(recipe, node);
|
||||
// console.log(" node", node);
|
||||
});
|
||||
|
||||
this.dispatchState("assignRecipe");
|
||||
}
|
||||
|
||||
private setDesignTokenForElement<T>(
|
||||
nodeElement: HTMLElement,
|
||||
token: DesignToken<any>,
|
||||
value: T | null
|
||||
) {
|
||||
try {
|
||||
if (value) {
|
||||
// TODO figure out a better way to handle storage data types
|
||||
const color = parseColorHexRGB((value as unknown) as string);
|
||||
if (color) {
|
||||
// console.log(" setting DesignToken value (color)", token.name, value);
|
||||
token.setValueFor(
|
||||
nodeElement,
|
||||
(SwatchRGB.from(color) as unknown) as DesignTokenValue<T>
|
||||
);
|
||||
} else {
|
||||
const num = Number.parseFloat((value as unknown) as string);
|
||||
if (!Number.isNaN(num)) {
|
||||
// console.log(" setting DesignToken value (number)", token.name, value);
|
||||
token.setValueFor(
|
||||
nodeElement,
|
||||
(num as unknown) as DesignTokenValue<T>
|
||||
);
|
||||
} else {
|
||||
// console.log(" setting DesignToken value (unconverted)", token.name, value);
|
||||
token.setValueFor(nodeElement, value as DesignTokenValue<T>);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
token.deleteValueFor(nodeElement);
|
||||
}
|
||||
} catch (e) {
|
||||
// console.warn(" token error", e);
|
||||
// Ignore, token not found
|
||||
}
|
||||
}
|
||||
|
||||
private getElementForNode(node: PluginUINodeData): HTMLElement {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const element = this._rootElement.querySelector(
|
||||
`#${CSS.escape(node.id)}`
|
||||
)! as HTMLElement;
|
||||
return element;
|
||||
}
|
||||
|
||||
private appliedDesignTokensHandler(
|
||||
nodeElement: HTMLElement
|
||||
): (value: AppliedDesignToken, key: string) => void {
|
||||
return (value: AppliedDesignToken, key: string): void => {
|
||||
const def = this._designTokenRegistry.get(key);
|
||||
if (def) {
|
||||
this.setDesignTokenForElement(
|
||||
nodeElement,
|
||||
def.token,
|
||||
value.value as string
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private setupDesignTokenElement(element: HTMLElement, node: PluginUINodeData) {
|
||||
// console.log(" setupDesignTokenElement - node", node, "parent", element.id);
|
||||
|
||||
// Create an element representing this node in our local dom.
|
||||
const nodeElement = document.createElement("div");
|
||||
nodeElement.id = node.id;
|
||||
element.appendChild(nodeElement);
|
||||
|
||||
// Set all the inherited design token values for the local element.
|
||||
// console.log(" setting inherited tokens");
|
||||
node.inheritedDesignTokens.forEach(
|
||||
this.appliedDesignTokensHandler(nodeElement),
|
||||
this
|
||||
);
|
||||
|
||||
// Set all design token values from the main component for the local element (an instance component).
|
||||
// console.log(" setting main component tokens", node.componentDesignTokens);
|
||||
node.componentDesignTokens?.forEach(
|
||||
this.appliedDesignTokensHandler(nodeElement),
|
||||
this
|
||||
);
|
||||
|
||||
// Set all the design token override values for the local element.
|
||||
// console.log(" setting local tokens");
|
||||
node.designTokens.forEach(this.appliedDesignTokensHandler(nodeElement), this);
|
||||
|
||||
// Handle any additional data. Keys are provided as design token ids.
|
||||
node.additionalData.forEach((value, key) => {
|
||||
const def = this._designTokenRegistry.get(key);
|
||||
if (def) {
|
||||
// console.log(" setting token value on element", def, "value", value);
|
||||
this.setDesignTokenForElement(nodeElement, def.token, value);
|
||||
}
|
||||
}, this);
|
||||
|
||||
node.children.forEach(child => this.setupDesignTokenElement(nodeElement, child));
|
||||
}
|
||||
|
||||
private valueToString(value: any): string {
|
||||
// TODO figure out a better way to handle storage data types
|
||||
if (typeof value.toColorString === "function") {
|
||||
return value.toColorString();
|
||||
} else {
|
||||
return "" + value;
|
||||
}
|
||||
}
|
||||
|
||||
public getDesignTokenDefinitions(): DesignTokenDefinition[] {
|
||||
return this._designTokenRegistry.find(DesignTokenType.designToken);
|
||||
}
|
||||
|
||||
public getDesignTokenDefinition(id: string): DesignTokenDefinition | null {
|
||||
return this._designTokenRegistry.get(id);
|
||||
}
|
||||
|
||||
public getDefaultDesignTokenValue(token: DesignToken<any>): string {
|
||||
const val = this.valueToString(token.getValueFor(this._rootElement));
|
||||
// console.log("getDefaultDesignTokenValue", "token", token, "value", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
public getDesignTokenValue(node: PluginUINodeData, token: DesignToken<any>): any {
|
||||
// Evaluate the token based on the tokens provided to the element.
|
||||
const element = this.getElementForNode(node);
|
||||
const val = token.getValueFor(element);
|
||||
// console.log(" getDesignTokenValue", node.id, node.type, token.name, "value", this.valueToString(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
private setDesignTokenForNode(
|
||||
node: PluginUINodeData,
|
||||
definition: DesignTokenDefinition,
|
||||
value: any
|
||||
): void {
|
||||
if (value) {
|
||||
const designToken = new AppliedDesignToken();
|
||||
designToken.value = this.valueToString(value);
|
||||
node.designTokens.set(definition.id, designToken);
|
||||
} else {
|
||||
node.designTokens.delete(definition.id);
|
||||
}
|
||||
// console.log(" after set designTokens", node.id, node.type, node.designTokens);
|
||||
|
||||
const element = this.getElementForNode(node);
|
||||
this.setDesignTokenForElement(element, definition.token, value);
|
||||
}
|
||||
|
||||
public assignDesignToken<T>(definition: DesignTokenDefinition, value: T): void {
|
||||
const nodes = this._selectedNodes.filter(node =>
|
||||
node.supports.includes(DesignTokenType.designToken)
|
||||
);
|
||||
|
||||
// console.log("--------------------------------");
|
||||
// console.log("UIController.assignDesignToken", definition, value, typeof value, nodes);
|
||||
|
||||
nodes.forEach(node => this.setDesignTokenForNode(node, definition, value));
|
||||
|
||||
// console.log(" Evaluating all recipes for all selected nodes");
|
||||
this.evaluateRecipes(this._selectedNodes);
|
||||
|
||||
this.dispatchState("assignDesignToken " + definition.id);
|
||||
}
|
||||
|
||||
public removeDesignToken(definition: DesignTokenDefinition): void {
|
||||
const nodes = this._selectedNodes.filter(node =>
|
||||
node.supports.includes(DesignTokenType.designToken)
|
||||
);
|
||||
|
||||
// console.log("--------------------------------");
|
||||
// console.log("UIController.removeDesignToken", definition.id, nodes);
|
||||
|
||||
nodes.forEach(node => this.setDesignTokenForNode(node, definition, null));
|
||||
|
||||
// console.log(" Evaluating all recipes for all selected nodes");
|
||||
this.evaluateRecipes(this._selectedNodes);
|
||||
|
||||
this.dispatchState("removeDesignToken");
|
||||
}
|
||||
|
||||
private dispatchState(reason: string): void {
|
||||
// console.log("UIController.dispatchState", reason);
|
||||
this._updateStateCallback(this._selectedNodes);
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
declare module "*.svg" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
import { Controller, PluginUIState } from "../core/controller.js";
|
||||
import {
|
||||
AdditionalData,
|
||||
AppliedDesignTokens,
|
||||
AppliedRecipes,
|
||||
RecipeEvaluations,
|
||||
} from "../core/model.js";
|
||||
import { DesignTokenType } from "../core/ui/design-token-registry.js";
|
||||
import { PluginUINodeData } from "../core/ui/ui-controller.js";
|
||||
import { FigmaPluginNode } from "./node.js";
|
||||
|
||||
/**
|
||||
* Serializable version of PluginUINodeData that works across Figma's iframe sandbox setup.
|
||||
*/
|
||||
export interface PluginUISerializableNodeData {
|
||||
id: string;
|
||||
type: string;
|
||||
supports: Array<DesignTokenType>;
|
||||
children: PluginUISerializableNodeData[];
|
||||
inheritedDesignTokens: string;
|
||||
componentDesignTokens?: string;
|
||||
designTokens: string;
|
||||
componentRecipes?: string;
|
||||
recipes: string;
|
||||
recipeEvaluations: string;
|
||||
additionalData: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts node data from the UI to serializable format.
|
||||
* @param nodes Node data in the UI format.
|
||||
* @returns Node data in the serializable format.
|
||||
*/
|
||||
export function serializeUINodes(
|
||||
nodes: PluginUINodeData[]
|
||||
): PluginUISerializableNodeData[] {
|
||||
const serializedNodes = nodes.map(
|
||||
(node): PluginUISerializableNodeData => {
|
||||
return {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
supports: node.supports,
|
||||
children: serializeUINodes(node.children),
|
||||
inheritedDesignTokens: (node.inheritedDesignTokens as AppliedDesignTokens).serialize(),
|
||||
componentDesignTokens: (node.componentDesignTokens as AppliedDesignTokens)?.serialize(),
|
||||
designTokens: node.designTokens.serialize(),
|
||||
componentRecipes: (node.componentRecipes as AppliedRecipes)?.serialize(),
|
||||
recipes: node.recipes.serialize(),
|
||||
recipeEvaluations: node.recipeEvaluations.serialize(),
|
||||
additionalData: node.additionalData.serialize(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return serializedNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts node data from the serializable to UI format.
|
||||
* @param nodes Node data in the serializable format.
|
||||
* @returns Node data in the UI format.
|
||||
*/
|
||||
export function deserializeUINodes(
|
||||
nodes: PluginUISerializableNodeData[]
|
||||
): PluginUINodeData[] {
|
||||
const deserializedNodes = nodes.map(
|
||||
(node): PluginUINodeData => {
|
||||
const inheritedDesignTokens = new AppliedDesignTokens();
|
||||
inheritedDesignTokens.deserialize(node.inheritedDesignTokens);
|
||||
const componentDesignTokens = new AppliedDesignTokens();
|
||||
componentDesignTokens.deserialize(node.componentDesignTokens);
|
||||
const designTokens = new AppliedDesignTokens();
|
||||
designTokens.deserialize(node.designTokens);
|
||||
const recipes = new AppliedRecipes();
|
||||
recipes.deserialize(node.recipes);
|
||||
const componentRecipes = new AppliedRecipes();
|
||||
componentRecipes.deserialize(node.componentRecipes);
|
||||
const recipeEvaluations = new RecipeEvaluations();
|
||||
recipeEvaluations.deserialize(node.recipeEvaluations);
|
||||
const additionalData = new AdditionalData();
|
||||
additionalData.deserialize(node.additionalData);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
supports: node.supports,
|
||||
children: deserializeUINodes(node.children),
|
||||
inheritedDesignTokens,
|
||||
componentDesignTokens,
|
||||
designTokens,
|
||||
componentRecipes,
|
||||
recipes,
|
||||
recipeEvaluations,
|
||||
additionalData,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return deserializedNodes;
|
||||
}
|
||||
|
||||
export class FigmaController extends Controller {
|
||||
public getNode(id: string): FigmaPluginNode | null {
|
||||
const node = figma.getNodeById(id);
|
||||
if (node) {
|
||||
return new FigmaPluginNode(node);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public setPluginUIState(state: PluginUIState): void {
|
||||
const message = {
|
||||
selectedNodes: serializeUINodes(state.selectedNodes),
|
||||
};
|
||||
|
||||
figma.ui.postMessage(message);
|
||||
}
|
||||
}
|
|
@ -1,465 +0,0 @@
|
|||
import { isDark, SwatchRGB } from "@fluentui/web-components";
|
||||
import { ColorRGBA64, parseColor } from "@microsoft/fast-colors";
|
||||
import {
|
||||
AppliedDesignTokens,
|
||||
AppliedRecipes,
|
||||
PluginNodeData,
|
||||
RecipeEvaluation,
|
||||
} from "../core/model.js";
|
||||
import { PluginNode } from "../core/node.js";
|
||||
import { DesignTokenType } from "../core/ui/design-token-registry.js";
|
||||
import { variantBooleanHelper } from "./utility.js";
|
||||
|
||||
function isNodeType<T extends BaseNode>(type: NodeType): (node: BaseNode) => node is T {
|
||||
return (node: BaseNode): node is T => node.type === type;
|
||||
}
|
||||
|
||||
export const isDocumentNode = isNodeType<DocumentNode>("DOCUMENT");
|
||||
export const isPageNode = isNodeType<PageNode>("PAGE");
|
||||
export const isSliceNode = isNodeType<SliceNode>("SLICE");
|
||||
export const isFrameNode = isNodeType<FrameNode>("FRAME");
|
||||
export const isGroupNode = isNodeType<GroupNode>("GROUP");
|
||||
export const isComponentNode = isNodeType<ComponentNode>("COMPONENT");
|
||||
export const isComponentSetNode = isNodeType<ComponentNode>("COMPONENT_SET");
|
||||
export const isInstanceNode = isNodeType<InstanceNode>("INSTANCE");
|
||||
export const isBooleanOperationNode = isNodeType<BooleanOperationNode>(
|
||||
"BOOLEAN_OPERATION"
|
||||
);
|
||||
export const isVectorNode = isNodeType<VectorNode>("VECTOR");
|
||||
export const isStarNode = isNodeType<StarNode>("STAR");
|
||||
export const isLineNode = isNodeType<LineNode>("LINE");
|
||||
export const isEllipseNode = isNodeType<EllipseNode>("ELLIPSE");
|
||||
export const isPolygonNode = isNodeType<PolygonNode>("POLYGON");
|
||||
export const isRectangleNode = isNodeType<RectangleNode>("RECTANGLE");
|
||||
export const isTextNode = isNodeType<TextNode>("TEXT");
|
||||
|
||||
export function isSceneNode(node: BaseNode): node is SceneNode {
|
||||
return [
|
||||
isSliceNode,
|
||||
isFrameNode,
|
||||
isGroupNode,
|
||||
isComponentNode,
|
||||
isComponentSetNode,
|
||||
isInstanceNode,
|
||||
isBooleanOperationNode,
|
||||
isVectorNode,
|
||||
isStarNode,
|
||||
isLineNode,
|
||||
isEllipseNode,
|
||||
isPolygonNode,
|
||||
isRectangleNode,
|
||||
isTextNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(node));
|
||||
}
|
||||
|
||||
export function canHaveChildren(
|
||||
node: BaseNode
|
||||
): node is
|
||||
| DocumentNode
|
||||
| PageNode
|
||||
| FrameNode
|
||||
| GroupNode
|
||||
| BooleanOperationNode
|
||||
| InstanceNode
|
||||
| ComponentNode
|
||||
| ComponentSetNode {
|
||||
return [
|
||||
isDocumentNode,
|
||||
isPageNode,
|
||||
isFrameNode,
|
||||
isGroupNode,
|
||||
isBooleanOperationNode,
|
||||
isInstanceNode,
|
||||
isComponentNode,
|
||||
isComponentSetNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(node));
|
||||
}
|
||||
|
||||
export class FigmaPluginNode extends PluginNode {
|
||||
public id: string;
|
||||
public type: string;
|
||||
private node: BaseNode;
|
||||
|
||||
constructor(node: BaseNode) {
|
||||
super();
|
||||
|
||||
// Controller.nodeCount++;
|
||||
|
||||
// console.log(" new FigmaPluginNode", node.id, node.name, node);
|
||||
|
||||
this.node = node;
|
||||
this.id = node.id;
|
||||
this.type = node.type;
|
||||
|
||||
this.loadLocalDesignTokens();
|
||||
this.loadRecipes();
|
||||
this.loadRecipeEvaluations();
|
||||
|
||||
// If it's an instance node, the `recipes` may also include main component settings. Deduplicate them.
|
||||
if (isInstanceNode(this.node)) {
|
||||
const mainComponentNode = (this.node as InstanceNode).mainComponent;
|
||||
if (mainComponentNode) {
|
||||
this.deduplicateComponentDesignTokens(mainComponentNode);
|
||||
this.deduplicateComponentRecipes(mainComponentNode);
|
||||
}
|
||||
}
|
||||
|
||||
// if (this._recipes.size) {
|
||||
// console.log(" final recipes", this._recipes.serialize());
|
||||
// }
|
||||
|
||||
// TODO This isn't working and is causing a lot of token evaluation issues. It would be nice if _some_ layers
|
||||
// in the design tool could have a fixed color and provide that to the tokens, but the logic for _which_
|
||||
// layers turns out to be pretty complicated.
|
||||
// For now the requirement is basing the adaptive design with a "layer" recipe.
|
||||
// this.setupFillColor();
|
||||
}
|
||||
|
||||
private deduplicateComponentDesignTokens(node: BaseNode) {
|
||||
this._componentDesignTokens = new AppliedDesignTokens();
|
||||
const componentDesignTokensJson = node.getSharedPluginData(
|
||||
"fast",
|
||||
"designTokens"
|
||||
);
|
||||
this._componentDesignTokens.deserialize(componentDesignTokensJson);
|
||||
|
||||
this._componentDesignTokens.forEach((token, tokenId) => {
|
||||
this._localDesignTokens.delete(tokenId);
|
||||
});
|
||||
}
|
||||
|
||||
private deduplicateComponentRecipes(node: BaseNode) {
|
||||
this._componentRecipes = new AppliedRecipes();
|
||||
const componentRecipesJson = node.getSharedPluginData("fast", "recipes");
|
||||
this._componentRecipes.deserialize(componentRecipesJson);
|
||||
|
||||
this._componentRecipes.forEach((recipe, recipeId) => {
|
||||
this._recipes.delete(recipeId);
|
||||
});
|
||||
}
|
||||
|
||||
public canHaveChildren(): boolean {
|
||||
return canHaveChildren(this.node);
|
||||
}
|
||||
|
||||
public children(): FigmaPluginNode[] {
|
||||
if (canHaveChildren(this.node)) {
|
||||
const children: FigmaPluginNode[] = [];
|
||||
|
||||
// console.log(" get children");
|
||||
for (const child of this.node.children) {
|
||||
children.push(new FigmaPluginNode(child));
|
||||
}
|
||||
|
||||
return children;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public supports(): Array<DesignTokenType> {
|
||||
return Object.keys(DesignTokenType).filter((key: string) => {
|
||||
switch (key) {
|
||||
case DesignTokenType.layerFill:
|
||||
case DesignTokenType.backgroundFill:
|
||||
return [
|
||||
isDocumentNode,
|
||||
isPageNode,
|
||||
isFrameNode,
|
||||
isRectangleNode,
|
||||
isEllipseNode,
|
||||
isPolygonNode,
|
||||
isStarNode,
|
||||
isBooleanOperationNode,
|
||||
isVectorNode,
|
||||
isComponentNode,
|
||||
isInstanceNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(this.node));
|
||||
case DesignTokenType.strokeFill:
|
||||
case DesignTokenType.strokeWidth:
|
||||
return [
|
||||
isFrameNode,
|
||||
isRectangleNode,
|
||||
isEllipseNode,
|
||||
isPolygonNode,
|
||||
isStarNode,
|
||||
isLineNode,
|
||||
isVectorNode,
|
||||
isComponentNode,
|
||||
isInstanceNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(this.node));
|
||||
case DesignTokenType.cornerRadius:
|
||||
return [
|
||||
isFrameNode,
|
||||
isRectangleNode,
|
||||
isEllipseNode,
|
||||
isPolygonNode,
|
||||
isStarNode,
|
||||
isComponentNode,
|
||||
isInstanceNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(this.node));
|
||||
case DesignTokenType.foregroundFill:
|
||||
return [
|
||||
isFrameNode,
|
||||
isRectangleNode,
|
||||
isEllipseNode,
|
||||
isPolygonNode,
|
||||
isLineNode,
|
||||
isStarNode,
|
||||
isBooleanOperationNode,
|
||||
isVectorNode,
|
||||
isComponentNode,
|
||||
isInstanceNode,
|
||||
isTextNode,
|
||||
].some((test: (node: BaseNode) => boolean) => test(this.node));
|
||||
case DesignTokenType.fontName:
|
||||
case DesignTokenType.fontSize:
|
||||
case DesignTokenType.lineHeight:
|
||||
return isTextNode(this.node);
|
||||
case DesignTokenType.designToken:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}) as Array<DesignTokenType>;
|
||||
}
|
||||
|
||||
public paint(data: RecipeEvaluation): void {
|
||||
switch (data.type) {
|
||||
case DesignTokenType.strokeFill:
|
||||
case DesignTokenType.layerFill:
|
||||
case DesignTokenType.backgroundFill:
|
||||
case DesignTokenType.foregroundFill:
|
||||
this.paintColor(data);
|
||||
break;
|
||||
case DesignTokenType.strokeWidth:
|
||||
this.paintStrokeWidth(data);
|
||||
break;
|
||||
case DesignTokenType.cornerRadius:
|
||||
this.paintCornerRadius(data);
|
||||
break;
|
||||
case DesignTokenType.fontName:
|
||||
{
|
||||
// TODO Handle font list better and font weight
|
||||
const families = data.value.split(",");
|
||||
const fontName = { family: families[0], style: "Regular" };
|
||||
figma.loadFontAsync(fontName).then(x => {
|
||||
(this.node as TextNode).fontName = fontName;
|
||||
});
|
||||
}
|
||||
break;
|
||||
case DesignTokenType.fontSize:
|
||||
{
|
||||
const textNode = this.node as TextNode;
|
||||
figma.loadFontAsync(textNode.fontName as FontName).then(x => {
|
||||
textNode.fontSize = Number.parseFloat(data.value);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case DesignTokenType.lineHeight:
|
||||
{
|
||||
const textNode = this.node as TextNode;
|
||||
figma.loadFontAsync(textNode.fontName as FontName).then(x => {
|
||||
textNode.lineHeight = {
|
||||
value: Number.parseFloat(data.value),
|
||||
unit: "PIXELS",
|
||||
};
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Recipe could not be painted ${JSON.stringify(data)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public parent(): FigmaPluginNode | null {
|
||||
const parent = this.node.parent;
|
||||
|
||||
if (parent === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// console.log(" get parent");
|
||||
return new FigmaPluginNode(parent);
|
||||
}
|
||||
|
||||
public getEffectiveFillColor(): ColorRGBA64 | null {
|
||||
let node: BaseNode | null = this.node;
|
||||
|
||||
while (node !== null) {
|
||||
if ((node as GeometryMixin).fills) {
|
||||
const fills = (node as GeometryMixin).fills;
|
||||
|
||||
if (Array.isArray(fills)) {
|
||||
const paints: SolidPaint[] = fills.filter(
|
||||
(fill: Paint) => fill.type === "SOLID" && fill.visible
|
||||
);
|
||||
|
||||
// TODO: how do we process multiple paints?
|
||||
if (paints.length === 1) {
|
||||
const parsed = ColorRGBA64.fromObject(paints[0].color);
|
||||
if (parsed instanceof ColorRGBA64) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public handleManualDarkMode(): boolean {
|
||||
if (isInstanceNode(this.node)) {
|
||||
if (this.node.variantProperties) {
|
||||
const currentDarkMode = this.node.variantProperties["Dark mode"];
|
||||
if (currentDarkMode) {
|
||||
const color = this.getEffectiveFillColor();
|
||||
if (color) {
|
||||
const containerIsDark = isDark(SwatchRGB.from(color));
|
||||
// console.log("handleManualDarkMode", this.node.variantProperties['Dark mode'], "color", color.toStringHexRGB(), "dark", containerIsDark);
|
||||
this.node.setProperties({
|
||||
"Dark mode": variantBooleanHelper(currentDarkMode)(
|
||||
containerIsDark
|
||||
),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected getPluginData<K extends keyof PluginNodeData>(key: K): string | undefined {
|
||||
let value: string | undefined = this.node.getSharedPluginData(
|
||||
"fast",
|
||||
key as string
|
||||
);
|
||||
if (value === "") {
|
||||
value = undefined;
|
||||
}
|
||||
// console.log(" getPluginData", this.node.id, this.node.type, key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected setPluginData<K extends keyof PluginNodeData>(key: K, value: string): void {
|
||||
// console.log(" setPluginData", this.node.id, this.node.type, key, value);
|
||||
this.node.setSharedPluginData("fast", key, value);
|
||||
}
|
||||
|
||||
protected deletePluginData<K extends keyof PluginNodeData>(key: K): void {
|
||||
// console.log(" deletePluginData", this.node.id, this.node.type, key);
|
||||
this.node.setSharedPluginData("fast", key, "");
|
||||
}
|
||||
|
||||
private paintColor(data: RecipeEvaluation): void {
|
||||
let paint: Paint | null = null;
|
||||
|
||||
if (data.value.startsWith("linear-gradient")) {
|
||||
const linearMatch = /linear-gradient\((?<params>.+)\)/;
|
||||
const matches = data.value.match(linearMatch);
|
||||
if (matches && matches.groups) {
|
||||
const array = matches.groups.params.split(",").map(p => p.trim());
|
||||
|
||||
let degrees: number = 90;
|
||||
if (array[0].endsWith("deg")) {
|
||||
const angle = array.shift()?.replace("deg", "") || "90";
|
||||
degrees = Number.parseFloat(angle);
|
||||
}
|
||||
const radians: number = degrees * (Math.PI / 180);
|
||||
|
||||
const paramMatch = /(?<color>#[\w\d]+)( (?<pos>.+))?/;
|
||||
const stops = array.map((p, index, array) => {
|
||||
const paramMatches = p.match(paramMatch);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const color = parseColor(paramMatches?.groups?.color || "FF00FF")!;
|
||||
let position: number = 0;
|
||||
if (paramMatches?.groups && paramMatches?.groups?.pos) {
|
||||
if (paramMatches.groups.pos.endsWith("%")) {
|
||||
position = Number.parseFloat(paramMatches.groups.pos) / 100;
|
||||
} else if (paramMatches.groups.pos.startsWith("calc(100% - ")) {
|
||||
const px = Number.parseFloat(
|
||||
paramMatches.groups.pos
|
||||
.replace("calc(100% - ", "")
|
||||
.replace("px)", "")
|
||||
);
|
||||
const size =
|
||||
degrees === 90 || degrees === 270
|
||||
? (this.node as LayoutMixin).height
|
||||
: (this.node as LayoutMixin).width;
|
||||
position = (size - px) / size;
|
||||
}
|
||||
} else if (index === array.length - 1) {
|
||||
position = 1;
|
||||
}
|
||||
const stop: ColorStop = {
|
||||
position,
|
||||
color: {
|
||||
r: color.r,
|
||||
g: color.g,
|
||||
b: color.b,
|
||||
a: color.a,
|
||||
},
|
||||
};
|
||||
return stop;
|
||||
});
|
||||
|
||||
const gradientPaint: GradientPaint = {
|
||||
type: "GRADIENT_LINEAR",
|
||||
gradientStops: stops,
|
||||
gradientTransform: [
|
||||
[Math.cos(radians), Math.sin(radians), 0],
|
||||
[Math.sin(radians) * -1, Math.cos(radians), 1],
|
||||
],
|
||||
};
|
||||
paint = gradientPaint;
|
||||
}
|
||||
} else {
|
||||
// Assume it's solid
|
||||
const color = parseColor(data.value);
|
||||
|
||||
if (color === null) {
|
||||
throw new Error(
|
||||
`The value "${data.value}" could not be converted to a ColorRGBA64`
|
||||
);
|
||||
}
|
||||
|
||||
const colorObject = color.toObject();
|
||||
const solidPaint: SolidPaint = {
|
||||
type: "SOLID",
|
||||
visible: true,
|
||||
opacity: colorObject.a,
|
||||
blendMode: "NORMAL",
|
||||
color: {
|
||||
r: colorObject.r,
|
||||
g: colorObject.g,
|
||||
b: colorObject.b,
|
||||
},
|
||||
};
|
||||
paint = solidPaint;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case DesignTokenType.layerFill:
|
||||
case DesignTokenType.backgroundFill:
|
||||
case DesignTokenType.foregroundFill:
|
||||
(this.node as any).fills = [paint];
|
||||
break;
|
||||
case DesignTokenType.strokeFill:
|
||||
(this.node as any).strokes = [paint];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private paintStrokeWidth(data: RecipeEvaluation): void {
|
||||
(this.node as any).strokeWeight = Number.parseFloat(data.value);
|
||||
}
|
||||
|
||||
private paintCornerRadius(data: RecipeEvaluation): void {
|
||||
(this.node as any).cornerRadius = data.value;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* Gets a string representation of `isTrue` based on the format of `booleanString`.
|
||||
*
|
||||
* Figma supports component properties like "true", "True", "yes", and "Yes", but doesn't doi the work to interpret it.
|
||||
*
|
||||
* Assumes the value is paired with the same case and set (i.e. "Yes" / "No", "true" / "false", etc.).
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
export const variantBooleanHelper = (booleanString: string) => (isTrue: boolean) =>
|
||||
[
|
||||
["No", "Yes"],
|
||||
["False", "True"],
|
||||
]
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
.find(bools => bools.find(b => b.toLowerCase() === booleanString.toLowerCase())!)!
|
||||
.map(b => (booleanString.match(/^[nytf]/) ? b.toLowerCase() : b))[
|
||||
!isTrue ? 0 : 1
|
||||
];
|
|
@ -1,22 +0,0 @@
|
|||
/* Import Inter font to match Figma UI
|
||||
* https://rsms.me/inter/
|
||||
* https://github.com/rsms/inter/blob/master/LICENSE.txt
|
||||
*/
|
||||
@import url("https://rsms.me/inter/inter.css");
|
||||
html {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 11px;
|
||||
}
|
||||
@supports (font-variation-settings: normal) {
|
||||
html {
|
||||
font-family: "Inter var", sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export const css: string;
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<plugin-design-system-provider base-layer-luminance="1">
|
||||
<td-app></td-app>
|
||||
</plugin-design-system-provider>
|
||||
</body>
|
||||
</html>
|
|
@ -1,71 +0,0 @@
|
|||
import {
|
||||
deserializeUINodes,
|
||||
FigmaController,
|
||||
PluginUISerializableNodeData,
|
||||
} from "./figma/controller.js";
|
||||
|
||||
const controller = new FigmaController();
|
||||
|
||||
// Ignore invisible nodes for performance, which means if someone turns them back to visible they may need to run the plugin again.
|
||||
figma.skipInvisibleInstanceChildren = true;
|
||||
|
||||
figma.showUI(__html__, {
|
||||
height: 600,
|
||||
width: 356,
|
||||
});
|
||||
|
||||
/**
|
||||
* Displays a notification when running a function that takes some time.
|
||||
* @param callback The function to call
|
||||
*/
|
||||
function notifyProcessing(callback: () => void) {
|
||||
const notify = figma.notify("Processing design tokens", { timeout: Infinity });
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
figma.notify(e.message, { error: true });
|
||||
}
|
||||
|
||||
notify.cancel();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function handleSelection() {
|
||||
const nodes: readonly BaseNode[] = figma.currentPage.selection.length
|
||||
? figma.currentPage.selection
|
||||
: Object.freeze([figma.currentPage]);
|
||||
|
||||
notifyProcessing(() =>
|
||||
controller.setSelectedNodes(nodes.map((node: BaseNode): string => node.id))
|
||||
);
|
||||
}
|
||||
|
||||
let lastSelectionTimeout: number = Number.NaN;
|
||||
|
||||
/**
|
||||
* Avoid extra processing when the selection is still changing.
|
||||
*/
|
||||
function debounceSelection() {
|
||||
if (!Number.isNaN(lastSelectionTimeout)) {
|
||||
clearTimeout(lastSelectionTimeout);
|
||||
}
|
||||
|
||||
lastSelectionTimeout = setTimeout(() => {
|
||||
lastSelectionTimeout = Number.NaN;
|
||||
handleSelection();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
figma.on("selectionchange", debounceSelection);
|
||||
|
||||
figma.ui.onmessage = (nodes: PluginUISerializableNodeData[]): void => {
|
||||
notifyProcessing(() => {
|
||||
const pluginNodes = deserializeUINodes(nodes);
|
||||
controller.handleMessage(pluginNodes);
|
||||
});
|
||||
};
|
||||
|
||||
handleSelection();
|
|
@ -1,49 +0,0 @@
|
|||
import { DesignSystem } from "@microsoft/fast-foundation";
|
||||
import {
|
||||
fluentButton,
|
||||
fluentDesignSystemProvider,
|
||||
fluentDivider,
|
||||
fluentTab,
|
||||
fluentTabPanel,
|
||||
fluentTabs,
|
||||
} from "@fluentui/web-components";
|
||||
import { App } from "./core/ui/index.js";
|
||||
import {
|
||||
deserializeUINodes,
|
||||
PluginUISerializableNodeData,
|
||||
serializeUINodes,
|
||||
} from "./figma/controller.js";
|
||||
|
||||
DesignSystem.getOrCreate()
|
||||
.withPrefix("plugin")
|
||||
.register(
|
||||
fluentButton(),
|
||||
fluentDesignSystemProvider(),
|
||||
fluentDivider(),
|
||||
fluentTab(),
|
||||
fluentTabs(),
|
||||
fluentTabPanel()
|
||||
);
|
||||
|
||||
App;
|
||||
|
||||
/* eslint-disable */
|
||||
const styles = require("./global.css");
|
||||
/* eslint-enable */
|
||||
|
||||
window.onload = (e: Event): void => {
|
||||
const app: App = document.querySelector("td-app") as App;
|
||||
|
||||
app.addEventListener("dispatch", (e: CustomEvent) => {
|
||||
parent.postMessage({ pluginMessage: serializeUINodes(e.detail) }, "*");
|
||||
});
|
||||
|
||||
// Update UI from Controller's message
|
||||
window.onmessage = (e: any): void => {
|
||||
const nodes = e.data.pluginMessage.selectedNodes as Array<
|
||||
PluginUISerializableNodeData
|
||||
>;
|
||||
const deserializedNodes = deserializeUINodes(nodes);
|
||||
app.selectedNodes = deserializedNodes;
|
||||
};
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"target": "ES2015",
|
||||
"module": "ES2015",
|
||||
"moduleResolution": "Node16",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@figma"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/custom.d.ts"
|
||||
]
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin");
|
||||
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
||||
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
const ResolveTypeScriptPlugin = require("resolve-typescript-plugin");
|
||||
|
||||
|
||||
const appDir = path.resolve(__dirname, "./src");
|
||||
const outDir = path.resolve(__dirname, "./dist");
|
||||
|
||||
module.exports = (env, args) => {
|
||||
const isProduction = args.mode === "production";
|
||||
|
||||
return {
|
||||
devtool: isProduction ? undefined : "inline-source-map",
|
||||
entry: {
|
||||
main: path.resolve(appDir, "main.ts"),
|
||||
ui: path.resolve(appDir, "ui.ts"),
|
||||
},
|
||||
output: {
|
||||
path: outDir,
|
||||
publicPath: "/",
|
||||
filename: "[name].js",
|
||||
},
|
||||
mode: args.mode || "development",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /.ts$/,
|
||||
loader: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
loader: "raw-loader",
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
global: {}, // Fix missing symbol error when running in developer VM
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
title: "Fluent UI Design Tokens",
|
||||
contentBase: outDir,
|
||||
chunks: ["ui"],
|
||||
inlineSource: "(js)$",
|
||||
template: path.resolve(appDir, "index.html"),
|
||||
}),
|
||||
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
|
||||
new BundleAnalyzerPlugin({
|
||||
// Remove this to inspect bundle sizes.
|
||||
analyzerMode: "disabled",
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: [".js", ".svg"],
|
||||
plugins: [new ResolveTypeScriptPlugin()],
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
# 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__
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"extends": ["@microsoft/eslint-config-fast-dna", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-non-null-assertion": "off"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
tsdoc-metadata.json
|
||||
temp
|
||||
test
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"colors": true,
|
||||
"recursive": true,
|
||||
"timeout": 5000,
|
||||
"spec": "dist/**/*.spec.js"
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
# Tests
|
||||
__test__/
|
||||
*.spec.*
|
||||
*.test.*
|
||||
|
||||
# Source files
|
||||
src/
|
||||
|
||||
# babel config
|
||||
babel.config.js
|
|
@ -1 +0,0 @@
|
|||
package-lock=false
|
|
@ -1,3 +0,0 @@
|
|||
dist/*
|
||||
test/*
|
||||
src/__test__/*
|
|
@ -1,527 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/adaptive-ui",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Fri, 16 Jun 2023 18:17:14 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.28",
|
||||
"version": "1.0.0-alpha.28",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "chhol@microsoft.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "ca0e62ee8d05f72d1d8c1ad66bd6eea8e3f0a4eb",
|
||||
"comment": "update prettier and eslint-config-prettier versions"
|
||||
}
|
||||
],
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.28",
|
||||
"commit": "8250aa8352940584ff443b550ee756d49f01c478"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 28 Mar 2023 22:14:10 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.27",
|
||||
"version": "1.0.0-alpha.27",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.27",
|
||||
"commit": "032285c2bf0311f9f44cbc875b40696fc8f62857"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sat, 11 Mar 2023 00:09:48 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.26",
|
||||
"version": "1.0.0-alpha.26",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "c0ae1948ddddbb1c279a4adecb4fd590422f7791",
|
||||
"comment": "Marked @microsoft/adaptive-ui package for deprecation"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.26",
|
||||
"commit": "dcf64d0383f613fd0c79b6f4f5ef010fccdbc11f"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 14 Feb 2023 04:02:36 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.25",
|
||||
"version": "1.0.0-alpha.25",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.25",
|
||||
"commit": "e504d092f81623e6332fa8cdf72ce9114e6c842e"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 11 Jan 2023 22:07:48 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.24",
|
||||
"version": "1.0.0-alpha.24",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.24",
|
||||
"commit": "2bb85c931a84d4de3a8b61d6f26f89f2d9a525f6"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 02 Dec 2022 01:18:22 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.23",
|
||||
"version": "1.0.0-alpha.23",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.23",
|
||||
"commit": "040867e9a2a9e0ae25b78dfb3265fbe8f60fe023"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 15 Nov 2022 02:40:35 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.22",
|
||||
"version": "1.0.0-alpha.22",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.22",
|
||||
"commit": "416dc9167e9d41e6ffe11d87ed79b2f455357923"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 04 Nov 2022 22:28:49 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.21",
|
||||
"version": "1.0.0-alpha.21",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.21",
|
||||
"commit": "e60decc0627e763249e74937e00fab61c5c748e6"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 01 Nov 2022 23:26:26 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.20",
|
||||
"version": "1.0.0-alpha.20",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.20",
|
||||
"commit": "32d18d0900047899d1dadfe94f7923b6164b0cf3"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 28 Oct 2022 20:44:44 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.19",
|
||||
"version": "1.0.0-alpha.19",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.19",
|
||||
"commit": "5b745b1fb3bf249a7a2d172b7fb69cb08694aadd"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 25 Oct 2022 20:24:32 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.18",
|
||||
"version": "1.0.0-alpha.18",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.18",
|
||||
"commit": "220cc7e0e0de490e51cc8b6f42ff46b03228beaa"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 14 Oct 2022 18:26:11 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.17",
|
||||
"version": "1.0.0-alpha.17",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.17",
|
||||
"commit": "bcfbcb5be4433fb1fb0fab1697f13d345980db87"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Mon, 10 Oct 2022 20:28:02 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.16",
|
||||
"version": "1.0.0-alpha.16",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.16",
|
||||
"commit": "ed2977ef6556964e68ea3ab0e4951320a1527baf"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 06 Oct 2022 23:21:20 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.15",
|
||||
"version": "1.0.0-alpha.15",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.15",
|
||||
"commit": "a69c2e3c0700918fef5ba934909781cc8ab71384"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 05 Oct 2022 23:26:01 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.14",
|
||||
"version": "1.0.0-alpha.14",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.14",
|
||||
"commit": "21d1de94be2b9399ddc43a2b93c6483fa7304591"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Mon, 03 Oct 2022 23:44:38 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.13",
|
||||
"version": "1.0.0-alpha.13",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "9ae34c9d83d7dc54a91db5ae22f4f861530b4789",
|
||||
"comment": "Fix neutral color overlay mode default value"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.13",
|
||||
"commit": "e23f4375f183f0672f9aeafd0e58411adb66ae90"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 28 Sep 2022 20:45:51 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.12",
|
||||
"version": "1.0.0-alpha.12",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.12",
|
||||
"commit": "08fab33015853b7f820fdfdac2aecddfeb31843f"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 27 Sep 2022 22:31:52 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.11",
|
||||
"version": "1.0.0-alpha.11",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.11",
|
||||
"commit": "8834c6732c727d39f92f72b197388453a9c17f9b"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 23 Sep 2022 22:53:27 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.10",
|
||||
"version": "1.0.0-alpha.10",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "e4df319f20f647ac5437e684d3be460e98308f5b",
|
||||
"comment": "Add layer recipes and design tokens"
|
||||
},
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "ebf424d4aee7575a4e8c20e76e92fcbe7e0af8d7",
|
||||
"comment": "Initial support for opacity in neutral recipes"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.10",
|
||||
"commit": "1646b26450a08a77c8bd6302560fe12cc6989ae1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 01 Sep 2022 21:53:34 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.9",
|
||||
"version": "1.0.0-alpha.9",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.9",
|
||||
"commit": "6b0f08574475d8b28cc2df67bf72b9041c2ad285"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 26 Aug 2022 18:06:44 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.8",
|
||||
"version": "1.0.0-alpha.8",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.8",
|
||||
"commit": "4fe1bc5f1be71e2e0ea3bb46871c597fc993dc5f"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 18 Aug 2022 20:46:10 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.7",
|
||||
"version": "1.0.0-alpha.7",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "218cfc014f5c76464493725f83bd883e8b07defa",
|
||||
"comment": "Update design tokens to use new resolve function"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.7",
|
||||
"commit": "0b57f1bc812e8e6371b7d27bb625f99a25bfaa66"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 27 Jul 2022 17:36:33 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.6",
|
||||
"version": "1.0.0-alpha.6",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "349a30c989da19b377e627fdb6e64974345a4b1b",
|
||||
"comment": "Fixed missing export and incorrect min-contrast token values"
|
||||
},
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "0bc551cbe2db1a31adc31b3f69db08184da77dfa",
|
||||
"comment": "Fix a bug in Swatch where blue and green values were being flipped"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.6",
|
||||
"commit": "7df4548954621d6247ab7fe3fae1a1ba0d33294b"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Mon, 18 Jul 2022 21:10:01 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.5",
|
||||
"version": "1.0.0-alpha.5",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"commit": "b7caa90b2d77f5e5d56ef5303ff7647a12e17802",
|
||||
"comment": "Added elevation design tokens"
|
||||
},
|
||||
{
|
||||
"author": "beachball",
|
||||
"package": "@microsoft/adaptive-ui",
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.5",
|
||||
"commit": "2ff46a32cdd9af12b236d0e2f3436a4de782e8ee"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 22 Jun 2022 20:17:50 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.4",
|
||||
"version": "1.0.0-alpha.4",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"comment": "Fixed a cyclical reference",
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"commit": "1a4a389e284caaacb9ee0e45cf8da08d364df3d9",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
],
|
||||
"none": [
|
||||
{
|
||||
"comment": "adaptive-ui: A few test and doc updates",
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"commit": "b79a4e8d20576535d643716d5e71fd1b9bdcc447",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 15 Jun 2022 17:41:10 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.3",
|
||||
"version": "1.0.0-alpha.3",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.3",
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"commit": "a6b2a570c1cb592bc92b4c9d8366d197658819ae",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 01 Jun 2022 22:21:24 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.2",
|
||||
"version": "1.0.0-alpha.2",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.2",
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"commit": "e4740b8f077b77954ae82ff698f144181e1504a7",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 01 Jun 2022 17:53:14 GMT",
|
||||
"tag": "@microsoft/adaptive-ui_v1.0.0-alpha.1",
|
||||
"version": "1.0.0-alpha.1",
|
||||
"comments": {
|
||||
"prerelease": [
|
||||
{
|
||||
"comment": "Add adaptive-ui package",
|
||||
"author": "47367562+bheston@users.noreply.github.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
},
|
||||
{
|
||||
"comment": "Set prerelease version",
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"commit": "5b313a3262b977acbee3c2efac87d0d14a82f792",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
],
|
||||
"none": [
|
||||
{
|
||||
"comment": "chore: fix adaptive-ui build and test",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
},
|
||||
{
|
||||
"comment": "update api extractor and typescript to use the latest versions",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
},
|
||||
{
|
||||
"comment": "Bump @microsoft/fast-foundation to v3.0.0-alpha.1",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
},
|
||||
{
|
||||
"comment": "Bump @microsoft/eslint-config-fast-dna to v2.1.0",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
},
|
||||
{
|
||||
"comment": "Bump @microsoft/fast-colors to v5.3.1",
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/adaptive-ui"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,238 +0,0 @@
|
|||
# Change Log - @microsoft/adaptive-ui
|
||||
|
||||
This log was last generated on Fri, 16 Jun 2023 18:17:14 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0-alpha.28
|
||||
|
||||
Fri, 16 Jun 2023 18:17:14 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.28
|
||||
|
||||
## 1.0.0-alpha.27
|
||||
|
||||
Tue, 28 Mar 2023 22:14:10 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.27
|
||||
|
||||
## 1.0.0-alpha.26
|
||||
|
||||
Sat, 11 Mar 2023 00:09:48 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Marked @microsoft/adaptive-ui package for deprecation (47367562+bheston@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.26
|
||||
|
||||
## 1.0.0-alpha.25
|
||||
|
||||
Tue, 14 Feb 2023 04:02:36 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.25
|
||||
|
||||
## 1.0.0-alpha.24
|
||||
|
||||
Wed, 11 Jan 2023 22:07:48 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.24
|
||||
|
||||
## 1.0.0-alpha.23
|
||||
|
||||
Fri, 02 Dec 2022 01:18:22 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.23
|
||||
|
||||
## 1.0.0-alpha.22
|
||||
|
||||
Tue, 15 Nov 2022 02:40:35 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.22
|
||||
|
||||
## 1.0.0-alpha.21
|
||||
|
||||
Fri, 04 Nov 2022 22:28:49 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.21
|
||||
|
||||
## 1.0.0-alpha.20
|
||||
|
||||
Tue, 01 Nov 2022 23:26:26 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.20
|
||||
|
||||
## 1.0.0-alpha.19
|
||||
|
||||
Fri, 28 Oct 2022 20:44:44 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.19
|
||||
|
||||
## 1.0.0-alpha.18
|
||||
|
||||
Tue, 25 Oct 2022 20:24:32 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.18
|
||||
|
||||
## 1.0.0-alpha.17
|
||||
|
||||
Fri, 14 Oct 2022 18:26:11 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.17
|
||||
|
||||
## 1.0.0-alpha.16
|
||||
|
||||
Mon, 10 Oct 2022 20:28:02 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.16
|
||||
|
||||
## 1.0.0-alpha.15
|
||||
|
||||
Thu, 06 Oct 2022 23:21:20 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.15
|
||||
|
||||
## 1.0.0-alpha.14
|
||||
|
||||
Wed, 05 Oct 2022 23:26:01 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.14
|
||||
|
||||
## 1.0.0-alpha.13
|
||||
|
||||
Mon, 03 Oct 2022 23:44:38 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Fix neutral color overlay mode default value (47367562+bheston@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.13
|
||||
|
||||
## 1.0.0-alpha.12
|
||||
|
||||
Wed, 28 Sep 2022 20:45:51 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.12
|
||||
|
||||
## 1.0.0-alpha.11
|
||||
|
||||
Tue, 27 Sep 2022 22:31:52 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.11
|
||||
|
||||
## 1.0.0-alpha.10
|
||||
|
||||
Fri, 23 Sep 2022 22:53:27 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Add layer recipes and design tokens (47367562+bheston@users.noreply.github.com)
|
||||
- Initial support for opacity in neutral recipes (47367562+bheston@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.10
|
||||
|
||||
## 1.0.0-alpha.9
|
||||
|
||||
Thu, 01 Sep 2022 21:53:34 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.9
|
||||
|
||||
## 1.0.0-alpha.8
|
||||
|
||||
Fri, 26 Aug 2022 18:06:44 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.8
|
||||
|
||||
## 1.0.0-alpha.7
|
||||
|
||||
Thu, 18 Aug 2022 20:46:10 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Update design tokens to use new resolve function (nicholasrice@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.7
|
||||
|
||||
## 1.0.0-alpha.6
|
||||
|
||||
Wed, 27 Jul 2022 17:36:33 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Fixed missing export and incorrect min-contrast token values (47367562+bheston@users.noreply.github.com)
|
||||
- Fix a bug in Swatch where blue and green values were being flipped (47367562+bheston@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.6
|
||||
|
||||
## 1.0.0-alpha.5
|
||||
|
||||
Mon, 18 Jul 2022 21:10:01 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Added elevation design tokens (47367562+bheston@users.noreply.github.com)
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.5
|
||||
|
||||
## 1.0.0-alpha.4
|
||||
|
||||
Wed, 22 Jun 2022 20:17:50 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Fixed a cyclical reference (7559015+janechu@users.noreply.github.com)
|
||||
|
||||
## 1.0.0-alpha.3
|
||||
|
||||
Wed, 15 Jun 2022 17:41:10 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.3 (7559015+janechu@users.noreply.github.com)
|
||||
|
||||
## 1.0.0-alpha.2
|
||||
|
||||
Wed, 01 Jun 2022 22:21:24 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Bump @microsoft/fast-foundation to v3.0.0-alpha.2 (nicholasrice@users.noreply.github.com)
|
||||
|
||||
## 1.0.0-alpha.1
|
||||
|
||||
Wed, 01 Jun 2022 17:53:14 GMT
|
||||
|
||||
### Changes
|
||||
|
||||
- Add adaptive-ui package (47367562+bheston@users.noreply.github.com)
|
||||
- Set prerelease version (nicholasrice@users.noreply.github.com)
|
|
@ -1,69 +0,0 @@
|
|||
# Adaptive UI
|
||||
|
||||
> This package is no longer maintained. This effort has migrated to @adaptive-web/adaptive-ui.
|
||||
|
||||
> @adaptive-web version 0.0.1 is compatible with @microsoft version 1.0.0-alpha.2 with the following changes:
|
||||
> - The default value for "accent-base-color" has changed from "#0078D4" to "#F26C0D" (Microsoft blue to a custom orange)
|
||||
> - The default value for "accent-fill-min-contrast" has changed from 5.5 to 4.5
|
||||
> - The default value for "body-font" has changed from '"Segoe UI Variable", "Segoe UI", sans-serif' to 'Arial, Helvetica, sans-serif'
|
||||
> - Font Variations have been removed for all steps in the type ramp (ex: "type-ramp-base-font-variations")
|
||||
> - Layer recipes (ex: "layer-fill-fixed-base") have been fixed so they go the same direction in dark mode as they did in light mode
|
||||
> - The "direction" design token has been removed
|
||||
|
||||
Adaptive UI is a library for building highly-consistent design systems based around your visual decisions. This represents an evolution of many token implementations that provide end-result values without the means to track how they were derived or easily adjust them.
|
||||
|
||||
This is a core feature of [FAST](https://fast.design) and is incorporated into the [Fluent UI Web Components](https://aka.ms/fluentwebcomponents) and other design systems.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/adaptive-ui
|
||||
```
|
||||
|
||||
```shell
|
||||
yarn add @microsoft/adaptive-ui
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
The most common way to use this library is through the `DesignToken`s.
|
||||
|
||||
For example, in a FAST style sheet:
|
||||
|
||||
```ts
|
||||
css`
|
||||
:host {
|
||||
background: ${neutralFillRest};
|
||||
}
|
||||
`
|
||||
```
|
||||
|
||||
This will evaluate the `neutralFillRest` recipe when the component loads and apply the correct color appropriate for the current context within design.
|
||||
|
||||
Most of the color recipes are contextually aware of where they're used so they produce accessible colors based on the configuration of the design, for instance between light mode and dark mode, or when personalization is applied, like a blue tint to the neutral colors or orange accent color.
|
||||
|
||||
To tint the neutral palette, and thus all color recipes derived from it:
|
||||
|
||||
```ts
|
||||
neutralBaseColor.withDefault("#73818C");
|
||||
```
|
||||
|
||||
To switch to dark mode:
|
||||
|
||||
```ts
|
||||
fillColor.withDefault("#232323");
|
||||
```
|
||||
|
||||
See more about the [adaptive color system](./src/color/README.md).
|
||||
|
||||
## Road map
|
||||
|
||||
The layer system for setting content area background colors and improved handling of light and dark mode is evolving and will be added soon.
|
||||
|
||||
The latest version of adaptive density, currently in RFC and PR, will come here soon as well.
|
||||
|
||||
The color system is being updated to support opacity in colors, which will enable at least the neutral palette to overlay images or background blur effects like in Windows 11.
|
||||
|
||||
### Further afield
|
||||
|
||||
The fixed design tokens will evolve into a more configurable definition aligning with industry standards in design tokens.
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"extends": "../../../api-extractor.json",
|
||||
"mainEntryPointFilePath": "./dist/dts/index.d.ts",
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/adaptive-ui.untrimmed.d.ts",
|
||||
"betaTrimmedFilePath": "./dist/adaptive-ui.d.ts"
|
||||
}
|
||||
}
|
|
@ -1,819 +0,0 @@
|
|||
## API Report File for "@microsoft/adaptive-ui"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
|
||||
import { ColorRGBA64 } from '@microsoft/fast-colors';
|
||||
import { CSSDesignToken } from '@microsoft/fast-foundation';
|
||||
import { CSSDirective } from '@microsoft/fast-element';
|
||||
import { DesignToken } from '@microsoft/fast-foundation';
|
||||
import { DesignTokenResolver } from '@microsoft/fast-foundation';
|
||||
import { Direction } from '@microsoft/fast-web-utilities';
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentBaseColor: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentBaseSwatch: DesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillMinContrast: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentFillRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundMinContrast: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentForegroundRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const accentPalette: DesignToken<Palette<Swatch>>;
|
||||
|
||||
// @public
|
||||
export class BasePalette<T extends Swatch> implements Palette<T> {
|
||||
constructor(source: T, swatches: ReadonlyArray<T>);
|
||||
readonly closestIndexCache: Map<number, number>;
|
||||
closestIndexOf(reference: RelativeLuminance): number;
|
||||
colorContrast(reference: RelativeLuminance, contrastTarget: number, initialSearchIndex?: number, direction?: PaletteDirection): T;
|
||||
delta(reference: RelativeLuminance, delta: number, direction: PaletteDirection): T;
|
||||
get(index: number): T;
|
||||
readonly lastIndex: number;
|
||||
readonly reversedSwatches: ReadonlyArray<T>;
|
||||
readonly source: T;
|
||||
readonly swatches: ReadonlyArray<T>;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export const _black: SwatchRGB;
|
||||
|
||||
// @public
|
||||
export function blackOrWhiteByContrast(reference: Swatch, minContrast: number, defaultBlack: boolean): Swatch;
|
||||
|
||||
// @public
|
||||
export function blackOrWhiteByContrastSet(restReference: Swatch, hoverReference: Swatch, activeReference: Swatch, focusReference: Swatch, minContrast: number, defaultBlack: boolean): {
|
||||
rest: Swatch;
|
||||
hover: Swatch;
|
||||
active: Swatch;
|
||||
focus: Swatch;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const bodyFont: CSSDesignToken<string>;
|
||||
|
||||
// @public
|
||||
export interface ColorRecipe {
|
||||
evaluate(resolver: DesignTokenResolver, reference?: Swatch): Swatch;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function contrast(a: RelativeLuminance, b: RelativeLuminance): number;
|
||||
|
||||
// @public
|
||||
export function contrastAndDeltaSwatchSet(palette: Palette, reference: Swatch, minContrast: number, restDelta: number, hoverDelta: number, activeDelta: number, focusDelta: number, direction?: PaletteDirection): InteractiveSwatchSet;
|
||||
|
||||
// @public
|
||||
export function contrastSwatch(palette: Palette, reference: Swatch, minContrast: number, direction?: PaletteDirection): Swatch;
|
||||
|
||||
// @public
|
||||
export const ContrastTarget: Readonly<{
|
||||
readonly NormalText: 4.5;
|
||||
readonly LargeText: 3;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const controlCornerRadius: CSSDesignToken<number>;
|
||||
|
||||
// @public
|
||||
export function deltaSwatch(palette: Palette, reference: Swatch, delta: number, direction?: PaletteDirection): Swatch;
|
||||
|
||||
// @public
|
||||
export function deltaSwatchSet(palette: Palette, reference: Swatch, restDelta: number, hoverDelta: number, activeDelta: number, focusDelta: number, direction?: PaletteDirection): InteractiveSwatchSet;
|
||||
|
||||
// @public (undocumented)
|
||||
export const designUnit: CSSDesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const direction: CSSDesignToken<Direction>;
|
||||
|
||||
// @public
|
||||
export function directionByIsDark(color: RelativeLuminance): PaletteDirectionValue;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardActive: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardActiveSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardFocus: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardFocusSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardHover: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardHoverSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardRest: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationCardRestSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationDialog: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationDialogSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationFlyout: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationFlyoutSize: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export interface ElevationRecipe {
|
||||
evaluate(resolver: DesignTokenResolver, size: number): string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationRecipe: DesignToken<ElevationRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationTooltip: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const elevationTooltipSize: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const fillColor: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const focusStrokeInner: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const focusStrokeInnerRecipe: DesignToken<ColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const focusStrokeOuter: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const focusStrokeOuterRecipe: DesignToken<ColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const focusStrokeWidth: CSSDesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const fontWeight: CSSDesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const foregroundOnAccentActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const foregroundOnAccentFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const foregroundOnAccentHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const foregroundOnAccentRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const foregroundOnAccentRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export function idealColorDeltaSwatchSet(palette: Palette, reference: Swatch, minContrast: number, idealColor: Swatch, restDelta: number, hoverDelta: number, activeDelta: number, focusDelta: number, direction?: PaletteDirection): InteractiveSwatchSet;
|
||||
|
||||
// @public
|
||||
export interface InteractiveColorRecipe {
|
||||
evaluate(resolver: DesignTokenResolver, reference?: Swatch): InteractiveSwatchSet;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface InteractiveSwatchSet {
|
||||
active: Swatch;
|
||||
focus: Swatch;
|
||||
hover: Swatch;
|
||||
rest: Swatch;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function interactiveSwatchSetAsOverlay(set: InteractiveSwatchSet, reference: Swatch, asOverlay: boolean): InteractiveSwatchSet;
|
||||
|
||||
// @public
|
||||
export function isDark(color: RelativeLuminance): boolean;
|
||||
|
||||
// @public
|
||||
export const LayerBaseLuminance: Readonly<{
|
||||
readonly LightMode: 0.95;
|
||||
readonly DarkMode: 0.13;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const layerCornerRadius: CSSDesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillBaseLuminance: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillDelta: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedBase: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedMinus1: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedMinus2: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedMinus3: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedMinus4: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedPlus1: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedPlus2: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedPlus3: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedPlus4: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillFixedRecipe: DesignToken<LayerRecipe>;
|
||||
|
||||
// @public
|
||||
export const layerFillFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export const layerFillInteractiveActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillInteractiveFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillInteractiveHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerFillInteractiveRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public
|
||||
export const layerFillInteractiveRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public
|
||||
export const layerPalette: DesignToken<Palette<Swatch>>;
|
||||
|
||||
// @public
|
||||
export interface LayerRecipe {
|
||||
evaluate(resolve: DesignTokenResolver, index: number): Swatch;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function luminanceSwatch(luminance: number): Swatch;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralAsOverlay: DesignToken<boolean>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralBaseColor: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralBaseSwatch: DesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillInputRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillSecondaryRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStealthRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongMinContrast: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralFillStrongRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundHint: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundHintRecipe: DesignToken<ColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundMinContrast: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralForegroundRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralPalette: DesignToken<Palette<Swatch>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeDividerRecipe: DesignToken<ColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeDividerRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeDividerRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeInputRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeRestDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongActive: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongActiveDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongFocus: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongFocusDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongHover: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongHoverDelta: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongMinContrast: DesignToken<number>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongRecipe: DesignToken<InteractiveColorRecipe>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongRest: CSSDesignToken<Swatch>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const neutralStrokeStrongRestDelta: DesignToken<number>;
|
||||
|
||||
// @public
|
||||
export interface Palette<T extends Swatch = Swatch> {
|
||||
closestIndexOf(reference: RelativeLuminance): number;
|
||||
colorContrast(reference: RelativeLuminance, minContrast: number, initialIndex?: number, direction?: PaletteDirection): T;
|
||||
delta(reference: RelativeLuminance, delta: number, direction: PaletteDirection): T;
|
||||
get(index: number): T;
|
||||
readonly source: T;
|
||||
readonly swatches: ReadonlyArray<T>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type PaletteDirection = PaletteDirectionValue | (() => PaletteDirectionValue);
|
||||
|
||||
// @public
|
||||
export const PaletteDirectionValue: Readonly<{
|
||||
readonly darker: 1;
|
||||
readonly lighter: -1;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type PaletteDirectionValue = (typeof PaletteDirectionValue)[keyof typeof PaletteDirectionValue];
|
||||
|
||||
// @public
|
||||
export class PaletteRGB extends BasePalette<SwatchRGB> {
|
||||
static from(source: SwatchRGB | string, options?: Partial<PaletteRGBOptions>): PaletteRGB;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface PaletteRGBOptions {
|
||||
preserveSource: boolean;
|
||||
stepContrast: number;
|
||||
stepContrastRamp: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface RelativeLuminance {
|
||||
readonly relativeLuminance: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function resolvePaletteDirection(direction: PaletteDirection): PaletteDirectionValue;
|
||||
|
||||
// @public
|
||||
export const StandardFontWeight: {
|
||||
readonly Thin: 100;
|
||||
readonly ExtraLight: 200;
|
||||
readonly Light: 300;
|
||||
readonly Normal: 400;
|
||||
readonly Medium: 500;
|
||||
readonly SemiBold: 600;
|
||||
readonly Bold: 700;
|
||||
readonly ExtraBold: 800;
|
||||
readonly Black: 900;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const strokeWidth: CSSDesignToken<number>;
|
||||
|
||||
// @public
|
||||
export interface Swatch extends RelativeLuminance {
|
||||
contrast(target: RelativeLuminance): number;
|
||||
toColorString(): string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function swatchAsOverlay(swatch: Swatch, reference: Swatch, asOverlay: boolean): Swatch;
|
||||
|
||||
// @public
|
||||
export class SwatchRGB implements Swatch {
|
||||
constructor(red: number, green: number, blue: number, alpha?: number, intendedColor?: SwatchRGB);
|
||||
static asOverlay(intendedColor: SwatchRGB, reference: SwatchRGB): SwatchRGB;
|
||||
readonly b: number;
|
||||
readonly color: ColorRGBA64;
|
||||
contrast: any;
|
||||
createCSS: () => string;
|
||||
static from(obj: {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}): SwatchRGB;
|
||||
readonly g: number;
|
||||
readonly intendedColor?: SwatchRGB;
|
||||
readonly r: number;
|
||||
readonly relativeLuminance: number;
|
||||
toColorString(): string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampBase: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampBaseFontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampBaseFontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampBaseLineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus1: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus1FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus1FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus1LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus2: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus2FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus2FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampMinus2LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus1: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus1FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus1FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus1LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus2: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus2FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus2FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus2LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus3: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus3FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus3FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus3LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus4: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus4FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus4FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus4LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus5: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus5FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus5FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus5LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus6: CSSDirective;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus6FontSize: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus6FontVariations: CSSDesignToken<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const typeRampPlus6LineHeight: CSSDesignToken<string>;
|
||||
|
||||
// @internal
|
||||
export const _white: SwatchRGB;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
```
|
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/adaptive-ui",
|
||||
"version": "1.0.0-alpha.28",
|
||||
"description": "A collection of design utilities supporting basic styling and Adaptive UI",
|
||||
"type": "module",
|
||||
"main": "dist/esm/index.js",
|
||||
"types": "dist/adaptive-ui.d.ts",
|
||||
"unpkg": "dist/esm/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/adaptive-ui.d.ts",
|
||||
"default": "./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 && yarn doc",
|
||||
"build:tests": "tsc -p ./tsconfig.json",
|
||||
"clean:dist": "node ../../../build/clean.js dist",
|
||||
"clean:test": "node ../../../build/clean.js test",
|
||||
"doc": "api-extractor run --local",
|
||||
"doc:ci": "api-extractor run",
|
||||
"prepare": "yarn clean:dist && yarn build",
|
||||
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.ts\"",
|
||||
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.ts\" --list-different",
|
||||
"test": "yarn build:tests && yarn eslint && yarn unit-tests && yarn doc",
|
||||
"eslint": "eslint . --ext .ts",
|
||||
"eslint:fix": "eslint . --ext .ts --fix",
|
||||
"unit-tests": "mocha",
|
||||
"unit-tests:watch": "mocha --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/fast-colors": "^5.3.1",
|
||||
"@microsoft/fast-foundation": "3.0.0-alpha.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.24.2",
|
||||
"@microsoft/eslint-config-fast-dna": "^2.1.0",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"chai": "^4.2.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"mocha": "^7.1.2",
|
||||
"prettier": "2.8.8",
|
||||
"typescript": "^4.7.0"
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
# Adaptive UI Color Recipes
|
||||
|
||||
Color recipes are algorithmic patterns that produce individual or sets of colors from a variety of inputs. Components can apply these recipes to achieve expressive theming options while maintaining color accessability targets.
|
||||
|
||||
## Swatch
|
||||
A Swatch is a representation of a color that has a `relativeLuminance` value and a method to convert the swatch to a color string. It is used by recipes to determine which colors to use for UI.
|
||||
|
||||
### SwatchRGB
|
||||
A concrete implementation of `Swatch`, it is a swatch with red, green, and blue 64bit color channels.
|
||||
|
||||
**Example: Creating a SwatchRGB**
|
||||
```ts
|
||||
import { SwatchRGB } from "@microsoft/adaptive-ui";
|
||||
|
||||
const red = new SwatchRGB(1, 0, 0);
|
||||
```
|
||||
|
||||
## Palette
|
||||
A palette is a collection `Swatch` instances, ordered by relative luminance, and provides mechanisms to safely retrieve swatches by index and by target contrast ratios. It also contains a `source` color, which is the color from which the palette is derived.
|
||||
|
||||
### PaletteRGB
|
||||
An implementation of `Palette` of `SwatchRGB` instances.
|
||||
|
||||
```ts
|
||||
// Create a PaletteRGB from a SwatchRGB
|
||||
const redPalette = PaletteRGB.from(red):
|
||||
```
|
|
@ -1,6 +0,0 @@
|
|||
export * from "./recipes/index.js";
|
||||
export * from "./utilities/index.js";
|
||||
export * from "./palette-rgb.js";
|
||||
export * from "./palette.js";
|
||||
export * from "./recipe.js";
|
||||
export * from "./swatch.js";
|
|
@ -1,52 +0,0 @@
|
|||
import { parseColorHexRGB } from "@microsoft/fast-colors";
|
||||
import chai from "chai";
|
||||
import { PaletteRGB, PaletteRGBOptions } from "./palette-rgb.js";
|
||||
import { SwatchRGB } from "./swatch.js";
|
||||
import { contrast } from "./utilities/relative-luminance.js";
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
const greyHex = "#808080";
|
||||
const greySwatch: SwatchRGB = SwatchRGB.from(parseColorHexRGB(greyHex)!);
|
||||
|
||||
describe("PaletteRGB.from", () => {
|
||||
it("should create a palette from the provided swatch", () => {
|
||||
const palette = PaletteRGB.from(greySwatch);
|
||||
|
||||
expect(palette.source).to.equal(greySwatch);
|
||||
});
|
||||
|
||||
it("should create a palette from the provided hex color", () => {
|
||||
const palette = PaletteRGB.from(greyHex);
|
||||
|
||||
expect(palette.source.toColorString()).to.equal(greyHex);
|
||||
});
|
||||
|
||||
it("should create a palette with increased contrast", () => {
|
||||
const options: Partial<PaletteRGBOptions> = {
|
||||
stepContrast: 1.07,
|
||||
stepContrastRamp: 0,
|
||||
};
|
||||
const palette = PaletteRGB.from(greySwatch, options);
|
||||
|
||||
expect(
|
||||
contrast(palette.swatches[0], palette.swatches[1]),
|
||||
"at least 1.07:1 between 0 and 1"
|
||||
).to.be.gte(1.07);
|
||||
expect(
|
||||
contrast(palette.swatches[20], palette.swatches[21]),
|
||||
"at least 1.07:1 between 20 and 21"
|
||||
).to.be.gte(1.07);
|
||||
});
|
||||
|
||||
// TODO: Fix with https://github.com/microsoft/fast/issues/5852
|
||||
// it("should create a palette with preserved source", () => {
|
||||
// const options: Partial<PaletteRGBOptions> = {
|
||||
// preserveSource: true,
|
||||
// };
|
||||
// const palette = PaletteRGB.from(greySwatch, options);
|
||||
|
||||
// expect(contrast(palette.swatches[0], palette.swatches[1]), "at least 1.05:1 between 0 and 1").to.be.gte(1.05);
|
||||
// expect(contrast(palette.swatches[20], palette.swatches[21]), "at least 1.05:1 between 0 and 1").to.be.gte(1.05);
|
||||
// });
|
||||
});
|
|
@ -1,315 +0,0 @@
|
|||
import {
|
||||
ColorHSL,
|
||||
ColorLAB,
|
||||
ColorRGBA64,
|
||||
hslToRGB,
|
||||
interpolateRGB,
|
||||
labToRGB,
|
||||
parseColorHexRGB,
|
||||
rgbToHSL,
|
||||
rgbToLAB,
|
||||
roundToPrecisionSmall,
|
||||
} from "@microsoft/fast-colors";
|
||||
import { BasePalette } from "./palette.js";
|
||||
import { SwatchRGB } from "./swatch.js";
|
||||
import { contrast } from "./utilities/relative-luminance.js";
|
||||
|
||||
/**
|
||||
* A utility Palette that generates many Swatches used for selection in the actual Palette.
|
||||
* The algorithm uses the LAB color space and keeps the saturation from the source color throughout.
|
||||
*/
|
||||
class HighResolutionPaletteRGB extends BasePalette<SwatchRGB> {
|
||||
/**
|
||||
* Bump the saturation if it falls below the reference color saturation.
|
||||
*
|
||||
* @param reference - Color with target saturation
|
||||
* @param color - Color to check and bump if below target saturation
|
||||
* @returns Original or adjusted color
|
||||
*/
|
||||
private static saturationBump(
|
||||
reference: ColorRGBA64,
|
||||
color: ColorRGBA64
|
||||
): ColorRGBA64 {
|
||||
const hslReference = rgbToHSL(reference);
|
||||
const saturationTarget = hslReference.s;
|
||||
const hslColor = rgbToHSL(color);
|
||||
if (hslColor.s < saturationTarget) {
|
||||
const hslNew = new ColorHSL(hslColor.h, saturationTarget, hslColor.l);
|
||||
return hslToRGB(hslNew);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales input from 0 to 100 to 0 to 0.5.
|
||||
*
|
||||
* @param l - Input number, 0 to 100
|
||||
* @returns Output number, 0 to 0.5
|
||||
*/
|
||||
private static ramp(l: number) {
|
||||
const inputval = l / 100;
|
||||
if (inputval > 0.5) return (inputval - 0.5) / 0.5; //from 0.500001in = 0.00000001out to 1.0in = 1.0out
|
||||
return 2 * inputval; //from 0in = 0out to 0.5in = 1.0out
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new high-resolution Palette.
|
||||
*
|
||||
* @param source - The source color
|
||||
* @returns The Palette based on the `source` color
|
||||
*/
|
||||
static from(source: SwatchRGB): HighResolutionPaletteRGB {
|
||||
const swatches: SwatchRGB[] = [];
|
||||
|
||||
const labSource = rgbToLAB(ColorRGBA64.fromObject(source)!.roundToPrecision(4));
|
||||
const lab0 = labToRGB(new ColorLAB(0, labSource.a, labSource.b))
|
||||
.clamp()
|
||||
.roundToPrecision(4);
|
||||
const lab50 = labToRGB(new ColorLAB(50, labSource.a, labSource.b))
|
||||
.clamp()
|
||||
.roundToPrecision(4);
|
||||
const lab100 = labToRGB(new ColorLAB(100, labSource.a, labSource.b))
|
||||
.clamp()
|
||||
.roundToPrecision(4);
|
||||
const rgbMin = new ColorRGBA64(0, 0, 0);
|
||||
const rgbMax = new ColorRGBA64(1, 1, 1);
|
||||
|
||||
const lAbove = lab100.equalValue(rgbMax) ? 0 : 14;
|
||||
const lBelow = lab0.equalValue(rgbMin) ? 0 : 14;
|
||||
|
||||
// 257 levels max, depending on whether lab0 or lab100 are black or white respectively.
|
||||
for (let l = 100 + lAbove; l >= 0 - lBelow; l -= 0.5) {
|
||||
let rgb: ColorRGBA64;
|
||||
|
||||
if (l < 0) {
|
||||
// For L less than 0, scale from black to L=0
|
||||
const percentFromRgbMinToLab0 = l / lBelow + 1;
|
||||
rgb = interpolateRGB(percentFromRgbMinToLab0, rgbMin, lab0);
|
||||
} else if (l <= 50) {
|
||||
// For L less than 50, we scale from L=0 to the base color
|
||||
rgb = interpolateRGB(HighResolutionPaletteRGB.ramp(l), lab0, lab50);
|
||||
} else if (l <= 100) {
|
||||
// For L less than 100, scale from the base color to L=100
|
||||
rgb = interpolateRGB(HighResolutionPaletteRGB.ramp(l), lab50, lab100);
|
||||
} else {
|
||||
// For L greater than 100, scale from L=100 to white
|
||||
const percentFromLab100ToRgbMax = (l - 100.0) / lAbove;
|
||||
rgb = interpolateRGB(percentFromLab100ToRgbMax, lab100, rgbMax);
|
||||
}
|
||||
|
||||
rgb = HighResolutionPaletteRGB.saturationBump(lab50, rgb).roundToPrecision(4);
|
||||
|
||||
swatches.push(SwatchRGB.from(rgb));
|
||||
}
|
||||
|
||||
return new HighResolutionPaletteRGB(source, swatches);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to tailor the generation of PaletteRGB.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PaletteRGBOptions {
|
||||
/**
|
||||
* The minimum amount of contrast between steps in the palette.
|
||||
* - Default 1.05
|
||||
* - Greater than 1
|
||||
* - Recommended increments by hundredths
|
||||
*/
|
||||
stepContrast: number;
|
||||
|
||||
/**
|
||||
* Multiplier for increasing step contrast as the swatches darken.
|
||||
* - Default 0.
|
||||
* - Greater than or equal to 0
|
||||
* - Recommended increments by hundredths
|
||||
*/
|
||||
stepContrastRamp: number;
|
||||
|
||||
/**
|
||||
* Whether to keep the exact source color in the target palette.
|
||||
* - Default false
|
||||
*
|
||||
* Only recommended when the exact color is required and used in stateful interaction recipes like hover.
|
||||
*
|
||||
* Note that custom recipes can still access the source color even if it's not in the ramp,
|
||||
* but turning this on will potentially increase the contrast between steps toward the ends of the palette.
|
||||
*/
|
||||
preserveSource: boolean;
|
||||
}
|
||||
|
||||
const defaultPaletteRGBOptions: PaletteRGBOptions = {
|
||||
stepContrast: 1.05,
|
||||
stepContrastRamp: 0,
|
||||
preserveSource: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation of a {@link Palette} that has a consistent minimum contrast value between Swatches.
|
||||
* This is useful for UI as it means the perception of the difference between colors the same distance
|
||||
* apart in the Palette will be consistent whether the colors are light yellow or dark red.
|
||||
* It generates its curve using the LAB color space and maintains the saturation of the source color throughout.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class PaletteRGB extends BasePalette<SwatchRGB> {
|
||||
/**
|
||||
* Adjust one end of the contrast-based palette so it doesn't abruptly fall to black (or white).
|
||||
*
|
||||
* @param swatchContrast - Function to get the target contrast for the next swatch
|
||||
* @param referencePalette - The high resolution palette
|
||||
* @param targetPalette - The contrast-based palette to adjust
|
||||
* @param direction - The end to adjust
|
||||
*/
|
||||
private static adjustEnd(
|
||||
swatchContrast: (swatch: SwatchRGB) => number,
|
||||
referencePalette: HighResolutionPaletteRGB,
|
||||
targetPalette: SwatchRGB[],
|
||||
direction: 1 | -1
|
||||
) {
|
||||
// Careful with the use of referencePalette as only the refSwatches is reversed.
|
||||
const refSwatches =
|
||||
direction === -1
|
||||
? referencePalette.swatches
|
||||
: referencePalette.reversedSwatches;
|
||||
const refIndex = (swatch: SwatchRGB) => {
|
||||
const index = referencePalette.closestIndexOf(swatch);
|
||||
return direction === 1 ? referencePalette.lastIndex - index : index;
|
||||
};
|
||||
|
||||
// Only operates on the 'end' end of the array, so flip if we're adjusting the 'beginning'
|
||||
if (direction === 1) {
|
||||
targetPalette.reverse();
|
||||
}
|
||||
|
||||
const targetContrast = swatchContrast(targetPalette[targetPalette.length - 2]);
|
||||
const actualContrast = roundToPrecisionSmall(
|
||||
contrast(
|
||||
targetPalette[targetPalette.length - 1],
|
||||
targetPalette[targetPalette.length - 2]
|
||||
),
|
||||
2
|
||||
);
|
||||
if (actualContrast < targetContrast) {
|
||||
// Remove last swatch if not sufficient contrast
|
||||
targetPalette.pop();
|
||||
|
||||
// Distribute to the last swatch
|
||||
const safeSecondSwatch = referencePalette.colorContrast(
|
||||
refSwatches[referencePalette.lastIndex],
|
||||
targetContrast,
|
||||
undefined,
|
||||
direction
|
||||
);
|
||||
const safeSecondRefIndex = refIndex(safeSecondSwatch);
|
||||
const targetSwatchCurrentRefIndex = refIndex(
|
||||
targetPalette[targetPalette.length - 2]
|
||||
);
|
||||
const swatchesToSpace = safeSecondRefIndex - targetSwatchCurrentRefIndex;
|
||||
let space = 1;
|
||||
for (
|
||||
let i = targetPalette.length - swatchesToSpace - 1;
|
||||
i < targetPalette.length;
|
||||
i++
|
||||
) {
|
||||
const currentRefIndex = refIndex(targetPalette[i]);
|
||||
const nextRefIndex =
|
||||
i === targetPalette.length - 1
|
||||
? referencePalette.lastIndex
|
||||
: currentRefIndex + space;
|
||||
targetPalette[i] = refSwatches[nextRefIndex];
|
||||
space++;
|
||||
}
|
||||
}
|
||||
|
||||
if (direction === 1) {
|
||||
targetPalette.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Palette with consistent minimum contrast between Swatches.
|
||||
*
|
||||
* @param source - The source color
|
||||
* @param options - Palette generation options
|
||||
* @returns A Palette meeting the requested contrast between Swatches.
|
||||
*/
|
||||
private static createColorPaletteByContrast(
|
||||
source: SwatchRGB,
|
||||
options: PaletteRGBOptions
|
||||
): SwatchRGB[] {
|
||||
const referencePalette = HighResolutionPaletteRGB.from(source);
|
||||
|
||||
// Ramp function to increase contrast as the swatches get darker
|
||||
const nextContrast = (swatch: SwatchRGB) => {
|
||||
const c =
|
||||
options.stepContrast +
|
||||
options.stepContrast *
|
||||
(1 - swatch.relativeLuminance) *
|
||||
options.stepContrastRamp;
|
||||
return roundToPrecisionSmall(c, 2);
|
||||
};
|
||||
|
||||
const swatches: SwatchRGB[] = [];
|
||||
|
||||
// Start with the source color (when preserving) or the light end color
|
||||
let ref = options.preserveSource ? source : referencePalette.swatches[0];
|
||||
swatches.push(ref);
|
||||
|
||||
// Add swatches by contrast toward dark
|
||||
do {
|
||||
const targetContrast = nextContrast(ref);
|
||||
ref = referencePalette.colorContrast(ref, targetContrast, undefined, 1);
|
||||
swatches.push(ref);
|
||||
} while (ref.relativeLuminance > 0);
|
||||
|
||||
// Add swatches by contrast toward light
|
||||
if (options.preserveSource) {
|
||||
ref = source;
|
||||
do {
|
||||
// This is off from the dark direction because `ref` here is the darker swatch, probably subtle
|
||||
const targetContrast = nextContrast(ref);
|
||||
ref = referencePalette.colorContrast(ref, targetContrast, undefined, -1);
|
||||
swatches.unshift(ref);
|
||||
} while (ref.relativeLuminance < 1);
|
||||
}
|
||||
|
||||
// Cleanup dark end
|
||||
this.adjustEnd(nextContrast, referencePalette, swatches, -1);
|
||||
|
||||
// Cleanup light end
|
||||
if (options.preserveSource) {
|
||||
this.adjustEnd(nextContrast, referencePalette, swatches, 1);
|
||||
}
|
||||
|
||||
return swatches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PaletteRGB from a source Swatch with options.
|
||||
*
|
||||
* @param source - The source Swatch to create a Palette from
|
||||
* @param options - Options to specify details of palette generation
|
||||
* @returns The PaletteRGB with Swatches based on `source`
|
||||
*/
|
||||
static from(
|
||||
source: SwatchRGB | string,
|
||||
options?: Partial<PaletteRGBOptions>
|
||||
): PaletteRGB {
|
||||
const swatch =
|
||||
source instanceof SwatchRGB
|
||||
? source
|
||||
: SwatchRGB.from(parseColorHexRGB(source)!);
|
||||
const opts =
|
||||
options === void 0 || null
|
||||
? defaultPaletteRGBOptions
|
||||
: { ...defaultPaletteRGBOptions, ...options };
|
||||
|
||||
return new PaletteRGB(
|
||||
swatch,
|
||||
Object.freeze(PaletteRGB.createColorPaletteByContrast(swatch, opts))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
import { clamp } from "@microsoft/fast-colors";
|
||||
import { Swatch } from "./swatch.js";
|
||||
import { binarySearch } from "./utilities/binary-search.js";
|
||||
import { directionByIsDark } from "./utilities/direction-by-is-dark.js";
|
||||
import { contrast, RelativeLuminance } from "./utilities/relative-luminance.js";
|
||||
|
||||
/**
|
||||
* Directional values for navigating {@link Swatch}es in {@link Palette}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const PaletteDirectionValue = Object.freeze({
|
||||
/**
|
||||
* Move darker, or up the Palette.
|
||||
*/
|
||||
darker: 1,
|
||||
|
||||
/**
|
||||
* Move lighter, or down the Palette.
|
||||
*/
|
||||
lighter: -1,
|
||||
} as const);
|
||||
|
||||
/**
|
||||
* Directional values for navigating {@link Swatch}es in {@link Palette}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PaletteDirectionValue =
|
||||
(typeof PaletteDirectionValue)[keyof typeof PaletteDirectionValue];
|
||||
|
||||
// I know we like to avoid enums so I tried to make it an object, but I'm not sure how to make this work with the type and function below.
|
||||
// export const PaletteDirectionValue = {
|
||||
// darker: 1,
|
||||
// lighter: -1
|
||||
// } as const;
|
||||
|
||||
/**
|
||||
* Convenience type to allow a fixed {@link (PaletteDirectionValue:variable)} or a function that resolves to one.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PaletteDirection = PaletteDirectionValue | (() => PaletteDirectionValue);
|
||||
|
||||
/**
|
||||
* Gets a fixed {@link (PaletteDirectionValue:variable)} from {@link PaletteDirection} which may be a function that needs to be resolved.
|
||||
*
|
||||
* @param direction - A fixed palette direction value or a function that resolves to one
|
||||
* @returns A fixed palette direction value
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function resolvePaletteDirection(
|
||||
direction: PaletteDirection
|
||||
): PaletteDirectionValue {
|
||||
if (typeof direction === "function") {
|
||||
return direction();
|
||||
} else {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of {@link Swatch}es that form a luminance gradient from light (index 0) to dark.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Palette<T extends Swatch = Swatch> {
|
||||
/**
|
||||
* The Swatch used to create the full palette.
|
||||
*/
|
||||
readonly source: T;
|
||||
|
||||
/**
|
||||
* The array of all Swatches from light to dark.
|
||||
*/
|
||||
readonly swatches: ReadonlyArray<T>;
|
||||
|
||||
/**
|
||||
* Returns a Swatch from the Palette that most closely meets
|
||||
* the `minContrast` ratio for to the `reference`.
|
||||
*
|
||||
* @param reference - The relative luminance of the reference
|
||||
* @param minContrast - The minimum amount of contrast from the `reference`
|
||||
* @param initialIndex - Optional starting point for the search
|
||||
* @param direction - Optional control for the direction of the search
|
||||
* @returns The Swatch that meets the provided contrast
|
||||
*/
|
||||
colorContrast(
|
||||
reference: RelativeLuminance,
|
||||
minContrast: number,
|
||||
initialIndex?: number,
|
||||
direction?: PaletteDirection
|
||||
): T;
|
||||
|
||||
/**
|
||||
* Returns a Swatch from the Palette that's the specified position and direction away from the `reference`.
|
||||
*
|
||||
* @param reference - The relative luminance of the reference
|
||||
* @param delta - The number of Swatches away from `reference`
|
||||
* @param direction - The direction to go from `reference`, 1 goes darker, -1 goes lighter
|
||||
*/
|
||||
delta(reference: RelativeLuminance, delta: number, direction: PaletteDirection): T;
|
||||
|
||||
/**
|
||||
* Returns the index of the Palette that most closely matches
|
||||
* the provided relative luminance.
|
||||
*
|
||||
* @param reference - The relative luminance of the reference
|
||||
* @returns The index
|
||||
*/
|
||||
closestIndexOf(reference: RelativeLuminance): number;
|
||||
|
||||
/**
|
||||
* Gets a Swatch by index. Index is clamped to the limits
|
||||
* of the Palette so a Swatch will always be returned.
|
||||
*
|
||||
* @param index - The index
|
||||
* @returns The Swatch
|
||||
*/
|
||||
get(index: number): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A base {@link Palette} with a common implementation of the interface. Use PaletteRGB for an implementation
|
||||
* of a palette generation algorithm that is ready to be used directly, or extend this class to generate custom Swatches.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class BasePalette<T extends Swatch> implements Palette<T> {
|
||||
/**
|
||||
* {@inheritdoc Palette.source}
|
||||
*/
|
||||
readonly source: T;
|
||||
/**
|
||||
* {@inheritdoc Palette.swatches}
|
||||
*/
|
||||
readonly swatches: ReadonlyArray<T>;
|
||||
|
||||
/**
|
||||
* An index pointer to the end of the palette.
|
||||
*/
|
||||
readonly lastIndex: number;
|
||||
|
||||
/**
|
||||
* A copy of the `Swatch`es in reverse order, used for optimized searching.
|
||||
*/
|
||||
readonly reversedSwatches: ReadonlyArray<T>;
|
||||
|
||||
/**
|
||||
* Cache from `relativeLuminance` to `Swatch` index in the `Palette`.
|
||||
*/
|
||||
readonly closestIndexCache = new Map<number, number>();
|
||||
|
||||
/**
|
||||
* Creates a new Palette.
|
||||
*
|
||||
* @param source - The source color for the Palette
|
||||
* @param swatches - All Swatches in the Palette
|
||||
*/
|
||||
constructor(source: T, swatches: ReadonlyArray<T>) {
|
||||
this.source = source;
|
||||
this.swatches = swatches;
|
||||
|
||||
this.reversedSwatches = Object.freeze([...this.swatches].reverse());
|
||||
this.lastIndex = this.swatches.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc Palette.colorContrast}
|
||||
*/
|
||||
colorContrast(
|
||||
reference: RelativeLuminance,
|
||||
contrastTarget: number,
|
||||
initialSearchIndex?: number,
|
||||
direction: PaletteDirection = directionByIsDark(reference)
|
||||
): T {
|
||||
if (initialSearchIndex === undefined) {
|
||||
initialSearchIndex = this.closestIndexOf(reference);
|
||||
}
|
||||
|
||||
let source: ReadonlyArray<T> = this.swatches;
|
||||
const endSearchIndex = this.lastIndex;
|
||||
let startSearchIndex = initialSearchIndex;
|
||||
|
||||
const condition = (value: T) => contrast(reference, value) >= contrastTarget;
|
||||
|
||||
if (direction === PaletteDirectionValue.lighter) {
|
||||
source = this.reversedSwatches;
|
||||
startSearchIndex = endSearchIndex - startSearchIndex;
|
||||
}
|
||||
|
||||
return binarySearch(source, condition, startSearchIndex, endSearchIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc Palette.delta}
|
||||
*/
|
||||
delta(reference: RelativeLuminance, delta: number, direction: PaletteDirection): T {
|
||||
const dir = resolvePaletteDirection(direction);
|
||||
return this.get(this.closestIndexOf(reference) + dir * delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc Palette.closestIndexOf}
|
||||
*/
|
||||
closestIndexOf(reference: RelativeLuminance): number {
|
||||
if (this.closestIndexCache.has(reference.relativeLuminance)) {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
return this.closestIndexCache.get(reference.relativeLuminance)!;
|
||||
}
|
||||
|
||||
let index = this.swatches.indexOf(reference as T);
|
||||
|
||||
if (index !== -1) {
|
||||
this.closestIndexCache.set(reference.relativeLuminance, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
const closest = this.swatches.reduce((previous, next) =>
|
||||
Math.abs(next.relativeLuminance - reference.relativeLuminance) <
|
||||
Math.abs(previous.relativeLuminance - reference.relativeLuminance)
|
||||
? next
|
||||
: previous
|
||||
);
|
||||
|
||||
index = this.swatches.indexOf(closest);
|
||||
this.closestIndexCache.set(reference.relativeLuminance, index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc Palette.get}
|
||||
*/
|
||||
get(index: number): T {
|
||||
return this.swatches[index] || this.swatches[clamp(index, 0, this.lastIndex)];
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
import { DesignTokenResolver } from "@microsoft/fast-foundation";
|
||||
import { Swatch } from "./swatch.js";
|
||||
|
||||
/**
|
||||
* A recipe that evaluates a single color value.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ColorRecipe {
|
||||
/**
|
||||
* Evaluate a single color value.
|
||||
*
|
||||
* @param resolver - A function that resolves design tokens
|
||||
* @param reference - The reference color, implementation defaults to `fillColor`, but sometimes overridden for nested color recipes
|
||||
*/
|
||||
evaluate(resolver: DesignTokenResolver, reference?: Swatch): Swatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* A recipe that evaluates a color value for rest, hover, active, and focus states.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface InteractiveColorRecipe {
|
||||
/**
|
||||
* Evaluate an interactive color set.
|
||||
*
|
||||
* @param resolver - A function that resolves design tokens
|
||||
* @param reference - The reference color, implementation defaults to `fillColor`, but sometimes overridden for nested color recipes
|
||||
*/
|
||||
evaluate(resolver: DesignTokenResolver, reference?: Swatch): InteractiveSwatchSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of {@link Swatch}es to use for an interactive control's states.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface InteractiveSwatchSet {
|
||||
/**
|
||||
* The Swatch to apply to the rest state.
|
||||
*/
|
||||
rest: Swatch;
|
||||
|
||||
/**
|
||||
* The Swatch to apply to the hover state.
|
||||
*/
|
||||
hover: Swatch;
|
||||
|
||||
/**
|
||||
* The Swatch to apply to the active state.
|
||||
*/
|
||||
active: Swatch;
|
||||
|
||||
/**
|
||||
* The Swatch to apply to the focus state.
|
||||
*/
|
||||
focus: Swatch;
|
||||
}
|