chore: remove adaptive ui related packages (#6764)

* decouple website from packagaes

* remove figma plugin
This commit is contained in:
Chris Holt 2023-06-24 16:57:23 -07:00 коммит произвёл GitHub
Родитель 0f8fc25054
Коммит dcbfbd03fa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
182 изменённых файлов: 53 добавлений и 23339 удалений

1
.github/CODEOWNERS поставляемый
Просмотреть файл

@ -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

130
.github/workflows/cd-deploy-color-staging.yml поставляемый
Просмотреть файл

@ -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.

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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"
}
}

3
packages/utilities/adaptive-ui/.gitignore поставляемый
Просмотреть файл

@ -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;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше