chore: remove fast animation and figma plugin from archives (#6843)

* remove fast animation as it has been deprecated

* remove fast-figma-plugin from archives

* Change files
This commit is contained in:
Chris Holt 2023-09-28 14:48:14 -07:00 коммит произвёл GitHub
Родитель c2b4076a8d
Коммит 0dea28d8c8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
95 изменённых файлов: 146 добавлений и 14270 удалений

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

@ -29,11 +29,7 @@ build/ @janechu @nicholasrice @chrisdholt @awentzel @EisenbergEffect
# Package specific owners
# Tooling
/packages/tooling/fast-figma-plugin-msft/ @nicholasrice @bheston @janechu @EisenbergEffect
# Utilities
/packages/utilities/fast-animation/ @nicholasrice @chrisdholt
/packages/utilities/fast-colors/ @nicholasrice @chrisdholt
/packages/utilities/fast-eslint-rules/ @nicholasrice @janechu @chrisdholt
/packages/utilities/fast-web-utilities/ @janechu @chrisdholt @nicholasrice

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

@ -5,7 +5,6 @@ on:
- master
paths:
- '.github/workflows/cd-deploy-www-staging.yml'
- 'packages/utilities/fast-animation/docs/api-report.md'
- 'packages/utilities/fast-color/docs/api-report.md'
- 'packages/utilities/fast-react-wrapper/docs/api-report.md'
- 'packages/web-components/**/docs/api-report.md'
@ -17,7 +16,6 @@ on:
- master
paths:
- '.github/workflows/cd-deploy-www-staging.yml'
- 'packages/utilities/fast-animation/docs/api-report.md'
- 'packages/utilities/fast-color/docs/api-report.md'
- 'packages/utilities/fast-react-wrapper/docs/api-report.md'
- 'packages/web-components/**/docs/api-report.md'

1
.github/workflows/ci-validate-pr.yml поставляемый
Просмотреть файл

@ -60,7 +60,6 @@ jobs:
coverageCommand: yarn lerna run coverage
coverageLocations: |
${{github.workspace}}/packages/tooling/fast-figma-plugin-msft/coverage/lcov.info:lcov
${{github.workspace}}/packages/utilities/fast-animation/coverage/lcov.info:lcov
${{github.workspace}}/packages/utilities/fast-colors/coverage/lcov.info:lcov
${{github.workspace}}/packages/utilities/fast-eslint-rules/coverage/lcov.info:lcov
debug: false

Просмотреть файл

@ -35,6 +35,5 @@ jobs:
with:
coverageCommand: lerna run coverage
coverageLocations: |
${{github.workspace}}/packages/utilities/fast-animation/coverage/lcov.info:lcov
${{github.workspace}}/packages/utilities/fast-colors/coverage/lcov.info:lcov
debug: true

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "none",
"comment": "add missing explicit types for fast-react-wrapper",
"packageName": "@microsoft/fast-react-wrapper",
"email": "chhol@microsoft.com",
"dependentChangeType": "none"
}

Просмотреть файл

@ -19,7 +19,6 @@
],
"nohoist": [
"**/@types/chai",
"**/@types/jest",
"**/@types/karma",
"**/@types/mocha",
"**/@types/node",
@ -54,9 +53,6 @@
"test:diff": "git update-index --refresh && git diff-index --quiet HEAD -- || yarn test:diff:error",
"test:validation": "yarn test:diff",
"test": "yarn eslint \"/**/*.{ts}\" ",
"unit-tests": "jest --maxWorkers=4",
"coverage": "yarn jest --coverage",
"unit-tests:watch": "jest --watch",
"watch": "tsc -p ./tsconfig.json -w --preserveWatchOutput",
"format:check": "lerna run prettier:diff",
"format": "lerna run prettier"
@ -66,49 +62,6 @@
"pre-commit": "lint-staged"
}
},
"jest": {
"collectCoverage": true,
"collectCoverageFrom": [
"build/helpers/*.ts"
],
"coverageReporters": [
"json",
[
"lcov",
{
"projectRoot": "."
}
]
],
"coverageThreshold": {
"global": {
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100
}
},
"testURL": "http://localhost",
"transform": {
"^.+\\.ts?$": "ts-jest",
"^.+\\.js?$": "babel-jest"
},
"transformIgnorePatterns": [
"!<rootDir>/node_modules/lodash-es",
"node_modules/jss-*",
"node_modules/css-vendor"
],
"testPathIgnorePatterns": [
"packages/",
"build/helpers/__tests__/*",
"build/helpers/"
],
"testRegex": "/__tests__/.*\\.(test|spec)\\.(js|ts|tsx)$",
"moduleFileExtensions": [
"ts",
"js"
]
},
"lint-staged": {
"*.{ts,tsx,js,html}": [
"prettier --write"
@ -144,7 +97,6 @@
"@babel/core": "^7.12.13",
"@babel/preset-env": "^7.12.13",
"@octokit/rest": "^18.0.6",
"@types/jest": "^25.2.1",
"@types/lodash-es": "^4.17.4",
"@types/node": "^15.0.1",
"beachball": "^2.24.0",
@ -155,13 +107,11 @@
"eyes.selenium": "3.6.2",
"glob": "^7.1.2",
"husky": "^4.2.5",
"jest": "^25.4.0",
"lerna": "^5.5.2",
"lint-staged": "^10.1.2",
"markdown-it": "^12.3.2",
"prettier": "2.0.2",
"rimraf": "^3.0.2",
"ts-jest": "^25.4.0",
"ts-node": "^8.8.2",
"typescript": "^4.6.2",
"yargs": "^16.2.0"

Просмотреть файл

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

Просмотреть файл

@ -1,12 +0,0 @@
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: {
node: "current",
},
},
],
],
};

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

До

Ширина:  |  Высота:  |  Размер: 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,59 +0,0 @@
{
"name": "@microsoft/fast-figma-plugin-msft",
"version": "0.8.16",
"description": "A Figma plugin for designing MSFT experiences",
"sideEffects": false,
"private": true,
"scripts": {
"build": "webpack --progress --mode=production",
"watch": "webpack --progress --watch --mode=production",
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.{ts,tsx}\"",
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.{ts,tsx}\" --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"
},
"bugs": {
"url": "https://github.com/Microsoft/fast/issues/new/choose"
},
"author": {
"name": "Microsoft",
"url": "https://discord.gg/FcSNfg4"
},
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/preset-env": "^7.12.13",
"@figma/plugin-typings": "^1.41.1",
"@types/node": "^15.0.1",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.9.4",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.1",
"eslint-config-prettier": "^6.10.1",
"html-webpack-inline-source-plugin": "^0.0.10",
"prettier": "2.0.2",
"raw-loader": "^4.0.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"style-loader": "^1.1.2",
"ts-loader": "^6.2.1",
"typescript": "^4.6.2",
"webpack": "^4.44.0",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@fluentui/web-components": "^2.2.1",
"@microsoft/fast-colors": "^5.3.0",
"@microsoft/fast-components": "^2.30.6",
"@microsoft/fast-element": "^1.12.0",
"@microsoft/fast-react-wrapper": "^0.3.19"
}
}

Просмотреть файл

@ -1,134 +0,0 @@
import { AppliedDesignTokens, AppliedRecipes, RecipeEvaluations } from "./model";
import { PluginNode } from "./node";
import { PluginUIProps } from "./ui";
import { PluginUINodeData } from "./ui/ui-controller";
/**
* 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(): Omit<PluginUIProps, "dispatch"> {
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.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: Omit<PluginUIProps, "dispatch">): void;
}

Просмотреть файл

@ -1,144 +0,0 @@
import { DesignTokenType } from "./ui/design-token-registry";
/**
* 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,239 +0,0 @@
import { ColorRGBA64 } from "@microsoft/fast-colors";
import {
AdditionalData,
AppliedDesignTokens,
AppliedRecipes,
PluginNodeData,
ReadonlyAppliedDesignTokens,
ReadonlyAppliedRecipes,
ReadonlyRecipeEvaluations,
RecipeEvaluation,
RecipeEvaluations,
TOOL_FILL_COLOR_TOKEN,
} from "./model";
import { DesignTokenType } from "./ui/design-token-registry";
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;
/**
* 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";
import { DesignTokenField } from "../design-token-field";
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<any>[] = [];
@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";
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,84 +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";
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,91 +0,0 @@
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
import { StealthButton } from "../stealth-button";
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;
padding-inline-start: 16px;
padding-inline-end: 8px;
}
: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,80 +0,0 @@
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
import { neutralFillStealthHover, neutralForegroundHint } from "@fluentui/web-components";
const template = html<GenericRecipe>`
<template
class="${x => x.orientation} ${x => (x.interactive ? "interactive" : "")} ${x =>
x.selected ? "selected" : ""}"
tabindex="${x => (x.interactive ? "interactive" : null)}"
aria-selected="${x => x.selected}"
>
<div class="icon">${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};
}
.icon {
width: 28px;
height: 28px;
position: relative;
overflow: hidden;
}
: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-generic-recipe",
template,
styles,
})
export class GenericRecipe extends FASTElement {
@attr
public value: string = "";
@attr
public icon?: string;
@attr
public orientation: "vertical" | "horizontal" = "vertical";
@attr({ mode: "boolean" })
public interactive: boolean = false;
@attr({ mode: "boolean" })
public selected: boolean = false;
}

Просмотреть файл

@ -1,27 +0,0 @@
import React from "react";
import { provideReactWrapper } from "@microsoft/fast-react-wrapper";
import { DesignTokenAdd } from "./design-token-add";
import { DesignTokensForm } from "./design-tokens-form";
export * from "./design-token-field";
export * from "./design-tokens-form";
export * from "./drawer";
export * from "./stealth-button";
export * from "./generic-recipe";
export * from "./swatch";
export * from "./corner-radius";
// Temporary React wrappers until complete conversion to web components
export const DesignTokenAddReact = provideReactWrapper(React).wrap(DesignTokenAdd, {
events: {
onAdd: "add",
},
});
export const DesignTokensFormReact = provideReactWrapper(React).wrap(DesignTokensForm, {
events: {
onTokenChange: "tokenchange",
onDetach: "detach",
},
});

Просмотреть файл

@ -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,141 +0,0 @@
import { attr, css, customElement, FASTElement, html } from "@microsoft/fast-element";
import {
controlCornerRadius,
neutralFillStealthHover,
neutralForegroundHint,
} from "@fluentui/web-components";
const template = html`
<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="swatch"></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);
}
: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 SwatchType {
background = "background",
border = "border",
}
@customElement({
name: "td-swatch",
template,
styles,
})
export class Swatch extends FASTElement {
@attr
public type: SwatchType = SwatchType.background;
@attr({ mode: "boolean" })
public circular: boolean = false;
@attr
public value: string | "none" = "none";
@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,73 +0,0 @@
import { CSSProperties } from "react";
declare global {
namespace JSX {
interface IntrinsicElements {
"plugin-button": {
appearance?: string;
style?: CSSProperties;
onClick?: any;
children?: React.ReactNode;
};
"plugin-checkbox": {
checked?: boolean;
style?: CSSProperties;
onChange?: any;
children?: React.ReactNode;
};
"plugin-divider": {
style?: CSSProperties;
};
"plugin-tab": {
id: string;
children?: React.ReactNode;
};
"plugin-tab-panel": {
id: string;
children?: React.ReactNode;
};
"plugin-tabs": {
activeid: string;
children?: React.ReactNode;
};
"td-drawer": {
key?: string;
expanded?: boolean;
name: string;
children?: React.ReactNode;
};
"td-swatch": {
key?: string;
type?: string;
circular?: boolean;
value: string | "none";
orientation?: "horizontal" | "vertical";
label?: string;
children?: React.ReactNode;
title?: string;
onClick?: any;
interactive?: boolean;
selected?: boolean;
};
"td-corner-radius": {
key?: string;
value: string;
children?: React.ReactNode;
orientation?: "horizontal" | "vertical";
onClick?: any;
interactive?: boolean;
selected?: boolean;
};
"td-generic-recipe": {
key?: string;
value: string;
icon?: string;
children?: React.ReactNode;
orientation?: "horizontal" | "vertical";
onClick?: any;
interactive?: boolean;
selected?: boolean;
};
}
}
}

Просмотреть файл

@ -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<T extends {} = any> {
/**
* 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<T>;
}
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<T>(id: string): DesignTokenDefinition<T> | 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,738 +0,0 @@
import React from "react";
// import DetachIcon from "./assets/detach.svg";
// import RevertIcon from "./assets/revert.svg";
import {
CornerRadius,
DesignTokenAddReact,
DesignTokensFormReact,
Drawer,
GenericRecipe,
Swatch as SwatchComponent,
} from "./components";
import { DesignTokenType } from "./design-token-registry";
import { PluginUINodeData, UIController } from "./ui-controller";
/* tslint:disable:no-unused-expression */
CornerRadius;
Drawer;
GenericRecipe;
SwatchComponent;
/* tslint:enable:no-unused-expression */
export interface PluginUIProps {
selectedNodes: PluginUINodeData[];
dispatch: (nodes: PluginUINodeData[]) => void;
}
export class PluginUI extends React.Component<PluginUIProps> {
public static defaultProps: PluginUIProps = {
selectedNodes: [],
dispatch: (): void => {
throw new Error(
`The UI message could not be dispatched - please provide a valid dispatch function the the PluginUI`
);
},
};
private readonly controller: UIController;
constructor(props: PluginUIProps) {
super(props);
this.controller = new UIController(nodes => this.dispatchState(nodes));
}
public render(): JSX.Element {
this.controller.setSelectedNodes(this.props.selectedNodes);
return this.renderBody();
}
private renderFooter(): JSX.Element {
const syncLabel =
"Evaluate and apply all design tokens and recipes to the curren selection.";
const revertLabel = "Remove all plugin data from the current selection.";
return (
<div style={{ overflow: "hidden" }}>
<plugin-divider></plugin-divider>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding:
"4px calc(var(--design-unit) * 2px) 4px calc(var(--design-unit) * 4px)",
}}
>
<p
style={{
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{this.props.selectedNodes
.map(node => `${node.type}`)
.join(" | ") || "No selection"}
</p>
<div style={{ display: "flex", gap: "8px" }}>
<plugin-button
appearance="accent"
aria-label={syncLabel}
style={{ display: this.controller.autoRefresh ? "none" : "" }}
onClick={this.controller.refreshSelectedNodes.bind(
this.controller
)}
>
Sync
</plugin-button>
<plugin-button
appearance="stealth"
aria-label={revertLabel}
onClick={this.controller.resetNodes.bind(this.controller)}
>
Reset
</plugin-button>
</div>
</div>
</div>
);
}
private renderBody(): JSX.Element {
// Get all applied design tokens except fillColor because it's handled through a recipe or plain color from the design tool.
const 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).
const availableDesignTokens = this.controller.getDesignTokenDefinitions().filter(
definition =>
!appliedDesignTokens.find(
appliedToken => appliedToken.definition.id === definition.id
) //&& definition.id !== "fillColor"
);
const layerRecipes = this.controller.appliedRecipes(DesignTokenType.layerFill);
const backgroundRecipes = this.controller.appliedRecipes(
DesignTokenType.backgroundFill
);
const foregroundRecipes = this.controller.appliedRecipes(
DesignTokenType.foregroundFill
);
const strokeRecipes = this.controller.appliedRecipes(DesignTokenType.strokeFill);
const strokeWidthRecipes = this.controller.appliedRecipes(
DesignTokenType.strokeWidth
);
const cornerRadiusRecipes = this.controller.appliedRecipes(
DesignTokenType.cornerRadius
);
const textRecipes = [
...this.controller.appliedRecipes(DesignTokenType.fontName),
...this.controller.appliedRecipes(DesignTokenType.fontSize),
...this.controller.appliedRecipes(DesignTokenType.lineHeight),
];
const supportsDesignSystem = this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.designToken)
);
return (
<div
style={{
display: "grid",
gridTemplateRows: "1fr auto",
height: "100%",
}}
>
<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={{ overflowY: "overlay" as any }}>
<td-drawer name="Color">
<div slot="collapsed-content">
{layerRecipes.length ? (
<>
<p className="title inset">Layer</p>
{layerRecipes.map(recipe => (
<p
className="applied-recipe"
key={recipe.id}
>
<td-swatch
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
>
{recipe.name}
</td-swatch>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</>
) : null}
{backgroundRecipes.length ? (
<>
<p className="title inset">Background</p>
{backgroundRecipes.map(recipe => (
<p
className="applied-recipe"
key={recipe.id}
>
<td-swatch
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
>
{recipe.name}
</td-swatch>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</>
) : null}
{foregroundRecipes.length ? (
<>
<p className="title inset">Foreground</p>
{foregroundRecipes.map(recipe => (
<p
className="applied-recipe"
key={recipe.id}
>
<td-swatch
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
>
{recipe.name}
</td-swatch>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</>
) : null}
{strokeRecipes.length ? (
<>
<p className="title inset">Border</p>
{strokeRecipes.map(recipe => (
<p
className="applied-recipe"
key={recipe.id}
>
<td-swatch
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
type="border"
>
{recipe.name}
</td-swatch>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</>
) : null}
</div>
<div>
{this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.layerFill)
) ? (
<>
<p className="title inset">
Layer backgrounds
</p>
<div className="swatch-stack">
{this.controller
.recipeOptionsByType(
DesignTokenType.layerFill
)
.map(recipe => {
return (
<td-swatch
key={recipe.id}
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-swatch>
);
})}
</div>
</>
) : null}
{this.props.selectedNodes.some(node =>
node.supports.includes(
DesignTokenType.backgroundFill
)
) ? (
<>
<p className="title inset">Fills</p>
<div className="swatch-stack">
{this.controller
.recipeOptionsByType(
DesignTokenType.backgroundFill
)
.map(recipe => {
return (
<td-swatch
key={recipe.id}
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-swatch>
);
})}
</div>
</>
) : null}
{this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.strokeFill)
) ? (
<>
<p className="title inset">Strokes</p>
<div className="swatch-stack">
{this.controller
.recipeOptionsByType(
DesignTokenType.strokeFill
)
.map(recipe => {
return (
<td-swatch
key={recipe.id}
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
interactive
type="border"
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-swatch>
);
})}
</div>
</>
) : null}
{this.props.selectedNodes.some(node =>
node.supports.includes(
DesignTokenType.foregroundFill
)
) ? (
<>
<p className="title inset">Foregrounds</p>
<div className="swatch-stack">
{this.controller
.recipeOptionsByType(
DesignTokenType.foregroundFill
)
.map(recipe => (
<td-swatch
key={recipe.id}
circular
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-swatch>
))}
</div>
</>
) : null}
</div>
</td-drawer>
<td-drawer name="Stroke Width">
{this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.strokeWidth)
) ? (
<div className="swatch-grid" style={{ marginTop: 8 }}>
{this.controller
.recipeOptionsByType(
DesignTokenType.strokeWidth
)
.map(recipe => {
return (
<td-generic-recipe
key={recipe.id}
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-generic-recipe>
);
})}
</div>
) : null}
{strokeWidthRecipes.length ? (
<div slot="collapsed-content">
{strokeWidthRecipes.map(recipe => (
<p className="applied-recipe" key={recipe.id}>
<td-generic-recipe
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-generic-recipe>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</div>
) : null}
</td-drawer>
<td-drawer name="Corner Radius">
{this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.cornerRadius)
) ? (
<div className="swatch-grid" style={{ marginTop: 8 }}>
{this.controller
.recipeOptionsByType(
DesignTokenType.cornerRadius
)
.map(recipe => {
return (
<td-corner-radius
key={recipe.id}
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-corner-radius>
);
})}
</div>
) : null}
{cornerRadiusRecipes.length ? (
<div slot="collapsed-content">
{cornerRadiusRecipes.map(recipe => (
<p className="applied-recipe" key={recipe.id}>
<td-corner-radius
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-corner-radius>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</div>
) : null}
</td-drawer>
<td-drawer name="Text">
{this.props.selectedNodes.some(node =>
node.supports.includes(DesignTokenType.fontName)
) ? (
<div className="swatch-grid" style={{ marginTop: 8 }}>
{[
...this.controller.recipeOptionsByType(
DesignTokenType.fontName
),
...this.controller.recipeOptionsByType(
DesignTokenType.fontSize
),
...this.controller.recipeOptionsByType(
DesignTokenType.lineHeight
),
].map(recipe => {
return (
<td-generic-recipe
key={recipe.id}
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
interactive
selected={
!!this.controller.recipeIsAssigned(
recipe.id
).length
}
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-generic-recipe>
);
})}
</div>
) : null}
{textRecipes.length ? (
<div slot="collapsed-content">
{textRecipes.map(recipe => (
<p className="applied-recipe" key={recipe.id}>
<td-generic-recipe
value={this.controller.getDefaultDesignTokenValue(
recipe.token
)}
orientation="horizontal"
onClick={this.controller.assignRecipe.bind(
this.controller,
recipe
)}
>
{recipe.name}
</td-generic-recipe>
<div>
<span>
{this.controller.getDefaultDesignTokenValue(
recipe.token
)}
</span>
<plugin-button
appearance="stealth"
aria-label="Detach"
onClick={this.controller.removeRecipe.bind(
this.controller,
recipe
)}
>
Detach
</plugin-button>
</div>
</p>
))}
</div>
) : null}
</td-drawer>
</div>
</plugin-tab-panel>
<plugin-tab-panel id="tokensPanel">
{supportsDesignSystem ? (
<div
style={{
display: "grid",
gridTemplateRows: "auto 1fr",
height: "100%",
}}
>
<div>
<DesignTokenAddReact
designTokens={availableDesignTokens}
onAdd={(e: CustomEvent) => {
this.controller.assignDesignToken(
e.detail.definition,
e.detail.value
);
}}
></DesignTokenAddReact>
<plugin-divider></plugin-divider>
</div>
<div style={{ overflowY: "overlay" as any }}>
<DesignTokensFormReact
designTokens={appliedDesignTokens}
onTokenChange={(e: CustomEvent) =>
this.controller.assignDesignToken(
e.detail.definition,
e.detail.value
)
}
onDetach={(e: CustomEvent) =>
this.controller.removeDesignToken(e.detail)
}
></DesignTokensFormReact>
</div>
</div>
) : (
<div>Selected layers don't support design tokens</div>
)}
</plugin-tab-panel>
</plugin-tabs>
{this.renderFooter()}
</div>
);
}
private dispatchState(selectedNodes: PluginUINodeData[]): void {
this.props.dispatch(selectedNodes);
}
}

Просмотреть файл

@ -1,331 +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,
Swatch,
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";
import { docBaseColor, docFillRest, docForeground } from "./custom-recipes";
interface DesignTokenStore<T> {
[key: string]: {
name: string;
token: DesignToken<T>;
type?: DesignTokenType;
formControlId?: string;
};
}
const designTokens: DesignTokenStore<any> = {
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<Swatch> = {
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<Swatch> = {
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<Swatch> = {
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<number> = {
strokeWidth: { token: strokeWidth, name: "Stroke width" },
focusStrokeWidth: { token: focusStrokeWidth, name: "Focus stroke width" },
};
const textFillRecipes: DesignTokenStore<Swatch> = {
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<number> = {
controlCornerRadius: { token: controlCornerRadius, name: "Control" },
layerCornerRadius: { token: layerCornerRadius, name: "Layer" },
};
const textRecipes: DesignTokenStore<any> = {
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<T>(
type: DesignTokenType | null,
store: DesignTokenStore<T>,
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,536 +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";
import {
DesignTokenDefinition,
DesignTokenRegistry,
DesignTokenType,
} from "./design-token-registry";
import { registerRecipes, registerTokens } from "./recipes";
/**
* 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);
}
/**
* 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<T>(
recipe: DesignTokenDefinition<T>,
node: PluginUINodeData
): T {
// console.log(" evaluateRecipe", recipe);
let value: T = this.getDesignTokenValue<T>(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<T>,
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);
}
};
}
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<any>[] {
return this._designTokenRegistry.find(DesignTokenType.designToken);
}
public getDesignTokenDefinition<T>(id: string): DesignTokenDefinition<T> | null {
return this._designTokenRegistry.get(id);
}
public getDefaultDesignTokenValue<T>(token: DesignToken<T>): string {
const val = this.valueToString(token.getValueFor(this._rootElement));
// console.log("getDefaultDesignTokenValue", "token", token, "value", val);
return val;
}
public getDesignTokenValue<T>(node: PluginUINodeData, token: DesignToken<T>): T {
// 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<T>(
node: PluginUINodeData,
definition: DesignTokenDefinition<T>,
value: T | null
): 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<T>, 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,120 +0,0 @@
import { Controller } from "../core/controller";
import {
AdditionalData,
AppliedDesignTokens,
AppliedRecipes,
RecipeEvaluations,
} from "../core/model";
import { PluginUIProps } from "../core/ui";
import { DesignTokenType } from "../core/ui/design-token-registry";
import { PluginUINodeData } from "../core/ui/ui-controller";
import { FigmaPluginNode } from "./node";
/**
* 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: Omit<PluginUIProps, "dispatch">): void {
const message = {
selectedNodes: serializeUINodes(state.selectedNodes),
};
figma.ui.postMessage(message);
}
}

Просмотреть файл

@ -1,440 +0,0 @@
import { ColorRGBA64, parseColor } from "@microsoft/fast-colors";
import {
AppliedDesignTokens,
AppliedRecipes,
PluginNodeData,
RecipeEvaluation,
} from "../core/model";
import { PluginNode } from "../core/node";
import { DesignTokenType } from "../core/ui/design-token-registry";
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;
}
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,146 +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%;
}
.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) * 4px);
}
.applied-recipe {
display: flex;
justify-content: space-between;
padding-inline-start: calc(var(--design-unit) * 4px);
padding-inline-end: calc(var(--design-unit) * 2px);
}
.applied-recipe > div {
display: flex;
align-items: center;
}
.applied-recipe > div > span {
margin-inline-end: 8px;
}
.button {
color: #FFF;
background: #18A0FB;
outline: none;
height: 32px;
border-radius: calc(var(--corner-radius) * 2px);
padding: 0 32px;
margin: 16px 0;
border: none;
margin-inline-start: calc(var(--design-unit) * 4px);
}
.button:hover {
background: rgb(22, 148, 231);
}
/* scrollbar */
/* For the "inset" look only */
::-webkit-scrollbar {
width: 8px;
background-color: transparent;
}
::-webkit-scrollbar-track {
margin: 1px;
background-color: transparent;
}
::-webkit-scrollbar-thumb {
border: 2px solid #fff;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0);
}
html:hover ::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.13);
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(0, 0, 0, 0.13);
}
/* Color Picker overrides */
.block-picker {
box-shadow: none !important;
width: 100% !important;
}
.block-picker input {
max-width: 118px;
margin-left: 64px;
height: 24px !important;
}
.block-picker > div:first-child,
.block-picker > div:nth-child(2) > div {
display: none;
}
.block-picker > div:nth-child(2) {
height: 24px !important;
width: 54px !important;
border-radius: 2px !important;
}
.block-picker > div:nth-child(3) {
padding-left: 0 !important;
padding-right: 0 !important;
display: flex;
flex-direction: column-reverse;
margin-top: -34px;
}
.block-picker > div:nth-child(3) > div:first-child {
margin-top: 12px;
}
.block-picker > div:nth-child(3) > div:first-child div {
border-radius: 50% !important;
margin-bottom: 0 !important;
}

Просмотреть файл

@ -1 +0,0 @@
export const css: string;

Просмотреть файл

@ -1 +0,0 @@
declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Просмотреть файл

@ -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">
<react-app></react-app>
</plugin-design-system-provider>
</body>
</html>

Просмотреть файл

@ -1,71 +0,0 @@
import {
deserializeUINodes,
FigmaController,
PluginUISerializableNodeData,
} from "./figma/controller";
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,61 +0,0 @@
import React from "react";
import ReactDOM from "react-dom";
import { DesignSystem } from "@microsoft/fast-foundation";
import {
fluentButton,
fluentDesignSystemProvider,
fluentDivider,
fluentTab,
fluentTabPanel,
fluentTabs,
} from "@fluentui/web-components";
import { PluginUI, PluginUIProps } from "./core/ui";
import { PluginUINodeData } from "./core/ui/ui-controller";
import {
deserializeUINodes,
PluginUISerializableNodeData,
serializeUINodes,
} from "./figma/controller";
DesignSystem.getOrCreate()
.withPrefix("plugin")
.register(
fluentButton(),
fluentDesignSystemProvider(),
fluentDivider(),
fluentTab(),
fluentTabs(),
fluentTabPanel()
);
/* eslint-disable */
const styles = require("./global.css");
/* eslint-enable */
/**
* Dispatches UI updates to the host Controller
* @param nodes The return node data
*/
function dispatchMessage(nodes: PluginUINodeData[]): void {
parent.postMessage({ pluginMessage: serializeUINodes(nodes) }, "*");
}
const root = document.querySelector("plugin-design-system-provider");
function render(props?: Omit<PluginUIProps, "dispatch">): void {
ReactDOM.render(<PluginUI {...props} dispatch={dispatchMessage} />, root);
}
/**
* Update UI from Controller's message
*/
window.onmessage = (e: any): void => {
const nodes = e.data.pluginMessage.selectedNodes as Array<
PluginUISerializableNodeData
>;
const deserializedNodes = deserializeUINodes(nodes);
render({ selectedNodes: deserializedNodes });
};
// Render UI
render();

Просмотреть файл

@ -1,19 +0,0 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist",
"declaration": false,
"jsx": "react",
"allowSyntheticDefaultImports": true,
"strictPropertyInitialization": false,
"typeRoots": [
"../../../node_modules/@types",
"../../../node_modules/@figma"
]
},
"include": [
"src/**/*.ts",
"src/custom.d.ts"
]
}

Просмотреть файл

@ -1,73 +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 appDir = path.resolve(__dirname, "./src");
const outDir = path.resolve(__dirname, "./dist");
module.exports = (env, args) => {
const isProduction = args.mode === "production";
return {
devtool: isProduction ? "none" : "inline-source-map",
entry: {
main: path.resolve(appDir, "main.ts"),
ui: path.resolve(appDir, "ui.tsx"),
},
output: {
path: outDir,
publicPath: "/",
filename: "[name].js",
},
mode: args.mode || "development",
module: {
rules: [
{
test: /.tsx?$/,
use: [
{
loader: "babel-loader",
},
{
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(),
new BundleAnalyzerPlugin({
// Remove this to inspect bundle sizes.
analyzerMode: "disabled",
}),
],
resolve: {
extensions: [".js", ".jsx", ".svg", ".tsx", ".ts"],
},
};
};

Просмотреть файл

@ -1,8 +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

Просмотреть файл

@ -1,3 +0,0 @@
module.exports = {
extends: ["@microsoft/eslint-config-fast-dna", "prettier"],
};

Просмотреть файл

@ -1,2 +0,0 @@
tsdoc-metadata.json
temp

Просмотреть файл

@ -1,14 +0,0 @@
# Source files
__tests__/
# Source files
client/
lib/
webpack/
www/
# Configs
babel.config.js
tsconfig.json
postcss.config.js
.eslintrc.js

Просмотреть файл

@ -1 +0,0 @@
package-lock=false

Просмотреть файл

@ -1,3 +0,0 @@
coverage/*
dist/*
www/*

Просмотреть файл

@ -1,220 +0,0 @@
{
"name": "@microsoft/fast-animation",
"entries": [
{
"date": "Fri, 06 May 2022 20:58:02 GMT",
"tag": "@microsoft/fast-animation_v4.2.2",
"version": "4.2.2",
"comments": {
"none": [
{
"comment": "update api extractor to 7.23.1 to ensure we can support TS 4.7 internal API changes",
"author": "chhol@microsoft.com",
"commit": "fbc6a7fb670453322c93f8f12a223febad86f735",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Wed, 04 May 2022 07:14:00 GMT",
"tag": "@microsoft/fast-animation_v4.2.2",
"version": "4.2.2",
"comments": {
"patch": [
{
"comment": "Bump @microsoft/fast-web-utilities to v5.4.1",
"author": "chhol@microsoft.com",
"commit": "d39284193f6d476b5b40b0fad75d3dbd836d55da",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Tue, 03 May 2022 07:15:44 GMT",
"tag": "@microsoft/fast-animation_v4.2.1",
"version": "4.2.1",
"comments": {
"patch": [
{
"comment": "Upgraded api-extractor",
"author": "44823142+williamw2@users.noreply.github.com",
"commit": "2341496a6fafe3051dc50333c21ca652026f725b",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Wed, 27 Apr 2022 07:21:09 GMT",
"tag": "@microsoft/fast-animation_v4.2.0",
"version": "4.2.0",
"comments": {
"minor": [
{
"comment": "update to typescript 4.6.2 and update ARIAMixin typings",
"author": "chhol@microsoft.com",
"commit": "35bdab45550b5d8b8762041110eccb06de78add5",
"package": "@microsoft/fast-animation"
}
],
"patch": [
{
"comment": "Bump @microsoft/eslint-config-fast-dna to v2.1.0",
"author": "chhol@microsoft.com",
"commit": "35bdab45550b5d8b8762041110eccb06de78add5",
"package": "@microsoft/fast-animation"
},
{
"comment": "Bump @microsoft/fast-web-utilities to v5.4.0",
"author": "chhol@microsoft.com",
"commit": "35bdab45550b5d8b8762041110eccb06de78add5",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Wed, 20 Apr 2022 07:13:17 GMT",
"tag": "@microsoft/fast-animation_v4.1.10",
"version": "4.1.10",
"comments": {
"none": [
{
"comment": "Pull up api-extractor.json to root and then extend it for each project",
"author": "stephanosp@microsoft.com",
"commit": "e3076337cbc2d260a497116061ee3bba3866a97d",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Sun, 17 Apr 2022 07:11:18 GMT",
"tag": "@microsoft/fast-animation_v4.1.10",
"version": "4.1.10",
"comments": {
"patch": [
{
"comment": "Bump @microsoft/fast-web-utilities to v5.3.0",
"author": "74849806+wannieman98@users.noreply.github.com",
"commit": "14bc5d5f2ae608328eb16ad7e619bab00415f60a",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Sun, 03 Apr 2022 07:12:01 GMT",
"tag": "@microsoft/fast-animation_v4.1.9",
"version": "4.1.9",
"comments": {
"patch": [
{
"comment": "Bump @microsoft/fast-web-utilities to v5.2.0",
"author": "nicholasrice@users.noreply.github.com",
"commit": "f6107c448ab6446667f4d9e86d9f9c11fff075aa",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Wed, 02 Feb 2022 07:13:39 GMT",
"tag": "@microsoft/fast-animation_v4.1.8",
"version": "4.1.8",
"comments": {
"none": [
{
"comment": "update node types to 15.0.1",
"author": "john.kreitlow@microsoft.com",
"commit": "48779abc5f0f746dc5d67b9d3c2875f455374ee6",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Sun, 30 Jan 2022 07:12:35 GMT",
"tag": "@microsoft/fast-animation_v4.1.8",
"version": "4.1.8",
"comments": {
"patch": [
{
"comment": "remove webpack-modernizr-plugin from fast-animation",
"author": "john.kreitlow@microsoft.com",
"commit": "34b614535176643a2424bba5a17e75373c345de5",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Tue, 25 Jan 2022 07:11:53 GMT",
"tag": "@microsoft/fast-animation_v4.1.7",
"version": "4.1.7",
"comments": {
"patch": [
{
"comment": "Bump @microsoft/fast-web-utilities to v5.1.0",
"author": "john.kreitlow@microsoft.com",
"commit": "97f653f8ee62c74d47df9b60024ff8eef05c79d1",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Sun, 31 Oct 2021 07:17:45 GMT",
"tag": "@microsoft/fast-animation_v4.1.6",
"version": "4.1.6",
"comments": {
"patch": [
{
"comment": "Bump @microsoft/eslint-config-fast-dna to v2.0.0",
"author": "chhol@microsoft.com",
"commit": "a150068ee196e73fe7a4f7b538a38752e0e506ba",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Wed, 13 Oct 2021 01:53:37 GMT",
"tag": "@microsoft/fast-animation_v4.1.5",
"version": "4.1.5",
"comments": {
"patch": [
{
"comment": "use requestAnimationFrame in fast-animation",
"author": "john.kreitlow@microsoft.com",
"commit": "696d66c1382aebedc9410b83362c4dbda131a638",
"package": "@microsoft/fast-animation"
},
{
"comment": "refactor: remove lodash-es as a dependency",
"author": "connor@peet.io",
"commit": "4ef4b325f8259dd0f648b5fe1b393ef8839e643e",
"package": "@microsoft/fast-animation"
}
]
}
},
{
"date": "Thu, 08 Jul 2021 07:17:00 GMT",
"tag": "@microsoft/fast-animation_v4.1.4",
"version": "4.1.4",
"comments": {
"patch": [
{
"comment": "update css-loader, mini-css-extract-plugin, and postcss-loader in fast-animation",
"author": "john.kreitlow@microsoft.com",
"commit": "c79ee7690f39f8aae16f50b67de5ce7925fd9612",
"package": "@microsoft/fast-animation"
}
]
}
}
]
}

Просмотреть файл

@ -1,496 +0,0 @@
# Change Log - @microsoft/fast-animation
This log was last generated on Wed, 04 May 2022 07:14:00 GMT and should not be manually modified.
<!-- Start content -->
## 4.2.2
Wed, 04 May 2022 07:14:00 GMT
### Patches
- Bump @microsoft/fast-web-utilities to v5.4.1 (chhol@microsoft.com)
## 4.2.1
Tue, 03 May 2022 07:15:44 GMT
### Patches
- Upgraded api-extractor (44823142+williamw2@users.noreply.github.com)
## 4.2.0
Wed, 27 Apr 2022 07:21:09 GMT
### Minor changes
- update to typescript 4.6.2 and update ARIAMixin typings (chhol@microsoft.com)
### Patches
- Bump @microsoft/eslint-config-fast-dna to v2.1.0 (chhol@microsoft.com)
- Bump @microsoft/fast-web-utilities to v5.4.0 (chhol@microsoft.com)
## 4.1.10
Sun, 17 Apr 2022 07:11:18 GMT
### Patches
- Bump @microsoft/fast-web-utilities to v5.3.0 (74849806+wannieman98@users.noreply.github.com)
## 4.1.9
Sun, 03 Apr 2022 07:12:01 GMT
### Patches
- Bump @microsoft/fast-web-utilities to v5.2.0 (nicholasrice@users.noreply.github.com)
## 4.1.8
Sun, 30 Jan 2022 07:12:35 GMT
### Patches
- remove webpack-modernizr-plugin from fast-animation (john.kreitlow@microsoft.com)
## 4.1.7
Tue, 25 Jan 2022 07:11:53 GMT
### Patches
- Bump @microsoft/fast-web-utilities to v5.1.0 (john.kreitlow@microsoft.com)
## 4.1.6
Sun, 31 Oct 2021 07:17:45 GMT
### Patches
- Bump @microsoft/eslint-config-fast-dna to v2.0.0 (chhol@microsoft.com)
## 4.1.5
Wed, 13 Oct 2021 01:53:37 GMT
### Patches
- use requestAnimationFrame in fast-animation (john.kreitlow@microsoft.com)
- refactor: remove lodash-es as a dependency (connor@peet.io)
## 4.1.4
Thu, 08 Jul 2021 07:17:00 GMT
### Patches
- update css-loader, mini-css-extract-plugin, and postcss-loader in fast-animation (john.kreitlow@microsoft.com)
## [4.1.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.1.1...@microsoft/fast-animation@4.1.3) (2021-02-08)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.1.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.1.1...@microsoft/fast-animation@4.1.2) (2021-02-08)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.1.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.1.0...@microsoft/fast-animation@4.1.1) (2020-09-28)
### Reverts
* Revert "chore: update all http to https to resolve WS1262 security vulnerability (#3658)" (#3699) ([0cac64a](https://github.com/Microsoft/fast/commit/0cac64a869e1b65a94ef14ed50b1324d0e15be46)), closes [#3658](https://github.com/Microsoft/fast/issues/3658) [#3699](https://github.com/Microsoft/fast/issues/3699)
# [4.1.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.17...@microsoft/fast-animation@4.1.0) (2020-07-14)
### Features
* update typescript version and remove utility types dependencies for react packages ([#3422](https://github.com/Microsoft/fast/issues/3422)) ([09d07b5](https://github.com/Microsoft/fast/commit/09d07b580cda3bcc5d28f83d3568521f710c9576))
## [4.0.17](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.16...@microsoft/fast-animation@4.0.17) (2020-07-02)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.16](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.15...@microsoft/fast-animation@4.0.16) (2020-06-26)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.14](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.13...@microsoft/fast-animation@4.0.14) (2020-06-09)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.13](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.12...@microsoft/fast-animation@4.0.13) (2020-05-18)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.12](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.11...@microsoft/fast-animation@4.0.12) (2020-04-29)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.11](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.10...@microsoft/fast-animation@4.0.11) (2020-04-27)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.10](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.9...@microsoft/fast-animation@4.0.10) (2020-04-22)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.9](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.8...@microsoft/fast-animation@4.0.9) (2020-04-10)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.8](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.7...@microsoft/fast-animation@4.0.8) (2020-03-13)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.7](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.6...@microsoft/fast-animation@4.0.7) (2019-12-18)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.6](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.5...@microsoft/fast-animation@4.0.6) (2019-11-07)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.5](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.4...@microsoft/fast-animation@4.0.5) (2019-10-25)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.4](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.3...@microsoft/fast-animation@4.0.4) (2019-10-16)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.2...@microsoft/fast-animation@4.0.3) (2019-08-09)
### Bug Fixes
* ensure all animation onFinish callbacks are called when used with an AnimateGroup ([51c6496](https://github.com/Microsoft/fast/commit/51c6496))
## [4.0.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.1...@microsoft/fast-animation@4.0.2) (2019-06-17)
**Note:** Version bump only for package @microsoft/fast-animation
## [4.0.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@4.0.0...@microsoft/fast-animation@4.0.1) (2019-04-09)
**Note:** Version bump only for package @microsoft/fast-animation
# [4.0.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.10...@microsoft/fast-animation@4.0.0) (2019-03-25)
### Bug Fixes
* update to use esModuleInterop in the TypeScript configuration files ([#1211](https://github.com/Microsoft/fast/issues/1211)) ([2ec0644](https://github.com/Microsoft/fast/commit/2ec0644))
### BREAKING CHANGES
* This will affect how imports will be handled by
consumers
## [3.0.10](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.9...@microsoft/fast-animation@3.0.10) (2019-03-19)
### Bug Fixes
* update jest to fix build break ([#1531](https://github.com/Microsoft/fast/issues/1531)) ([73ae6de](https://github.com/Microsoft/fast/commit/73ae6de))
## [3.0.9](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.8...@microsoft/fast-animation@3.0.9) (2019-02-21)
**Note:** Version bump only for package @microsoft/fast-animation
## [3.0.8](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.7...@microsoft/fast-animation@3.0.8) (2019-02-07)
**Note:** Version bump only for package @microsoft/fast-animation
<a name="3.0.7"></a>
## [3.0.7](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.6...@microsoft/fast-animation@3.0.7) (2019-01-26)
**Note:** Version bump only for package @microsoft/fast-animation
<a name="3.0.6"></a>
## [3.0.6](https://github.com/Microsoft/fast/compare/@microsoft/fast-animation@3.0.5...@microsoft/fast-animation@3.0.6) (2019-01-22)
**Note:** Version bump only for package @microsoft/fast-animation
<a name="2.2.0"></a>
# 2.2.0 (2018-09-11)
### Features
* add contrast based color system ([#810](https://github.com/Microsoft/fast/issues/810)) ([5ec457c](https://github.com/Microsoft/fast/commit/5ec457c))
<a name="2.1.0"></a>
# 2.1.0 (2018-08-29)
### Features
* update Lerna to ^3.0.0 ([#795](https://github.com/Microsoft/fast/issues/795)) ([9ce9a56](https://github.com/Microsoft/fast/commit/9ce9a56))
* upgrade to TypeScript 3.0.0 ([#793](https://github.com/Microsoft/fast/issues/793)) ([e203e86](https://github.com/Microsoft/fast/commit/e203e86))
* **fast-components-react-base:** add callback to horizontal overflow to return and object that informs scroll start and end ([#797](https://github.com/Microsoft/fast/issues/797)) ([37975f3](https://github.com/Microsoft/fast/commit/37975f3))
<a name="2.0.0-corrected"></a>
# 2.0.0-corrected (2018-08-03)
### Bug Fixes
* ensure app build and tslint processes run prior in the build gate ([#132](https://github.com/Microsoft/fast/issues/132)) ([e74f953](https://github.com/Microsoft/fast/commit/e74f953))
* fix tslint globbing issue and enforce whitespace in import/export statements ([#219](https://github.com/Microsoft/fast/issues/219)) ([4637a90](https://github.com/Microsoft/fast/commit/4637a90))
* **animation:** refactor animate sequence and group to remove code duplication ([#325](https://github.com/Microsoft/fast/issues/325)) ([2beeb98](https://github.com/Microsoft/fast/commit/2beeb98))
* travis-CI build-break ([#336](https://github.com/Microsoft/fast/issues/336)) ([bffbf5e](https://github.com/Microsoft/fast/commit/bffbf5e))
* **fast-components-react-msft:** fixes error running jest with components that require chroma ([#687](https://github.com/Microsoft/fast/issues/687)) ([140457c](https://github.com/Microsoft/fast/commit/140457c))
* **tslint:** fix tslint errors ([#114](https://github.com/Microsoft/fast/issues/114)) ([78fea3e](https://github.com/Microsoft/fast/commit/78fea3e))
* **tslint:** fixes incorrect tslint rule regarding ordered imports ([#188](https://github.com/Microsoft/fast/issues/188)) ([ebe0b30](https://github.com/Microsoft/fast/commit/ebe0b30))
### Features
* add form generator to the packages ([#311](https://github.com/Microsoft/fast/issues/311)) ([a339b3c](https://github.com/Microsoft/fast/commit/a339b3c))
* add snapshot test suite ([#207](https://github.com/Microsoft/fast/issues/207)) ([7ceaafe](https://github.com/Microsoft/fast/commit/7ceaafe))
* catagorizing relevant dependencies as peerDependencies ([#186](https://github.com/Microsoft/fast/issues/186)) ([7e15db6](https://github.com/Microsoft/fast/commit/7e15db6))
* **checkbox:** add new component with styles ([#252](https://github.com/Microsoft/fast/issues/252)) ([3ad3988](https://github.com/Microsoft/fast/commit/3ad3988))
* remove JSS manager dependency from React base components ([#148](https://github.com/Microsoft/fast/issues/148)) ([48de34a](https://github.com/Microsoft/fast/commit/48de34a))
* remove node sass from fast-animations ([#735](https://github.com/Microsoft/fast/issues/735)) ([645f811](https://github.com/Microsoft/fast/commit/645f811))
* **detail view:** add detail view ([#470](https://github.com/Microsoft/fast/issues/470)) ([665b871](https://github.com/Microsoft/fast/commit/665b871))
* **fast-css-editor-react:** add default editor component and position component ([#636](https://github.com/Microsoft/fast/issues/636)) ([72037a8](https://github.com/Microsoft/fast/commit/72037a8))
* **form generator:** updates styles found in configuration pane ([#420](https://github.com/Microsoft/fast/issues/420)) ([919121b](https://github.com/Microsoft/fast/commit/919121b))
* **Image:** add new component and msft styles ([#237](https://github.com/Microsoft/fast/issues/237)) ([ea057ed](https://github.com/Microsoft/fast/commit/ea057ed))
* **toggle:** add new component and msft styles ([#212](https://github.com/Microsoft/fast/issues/212)) ([b9dd3e0](https://github.com/Microsoft/fast/commit/b9dd3e0))
* update code coverage on travis ([#330](https://github.com/Microsoft/fast/issues/330)) ([63ab4f4](https://github.com/Microsoft/fast/commit/63ab4f4))
<a name="2.1.0"></a>
# [2.1.0](https://github.com/Microsoft/fast/compare/v2.0.0-corrected...v2.1.0) (2018-08-29)
### Features
* update Lerna to ^3.0.0 ([#795](https://github.com/Microsoft/fast/issues/795)) ([9ce9a56](https://github.com/Microsoft/fast/commit/9ce9a56))
* upgrade to TypeScript 3.0.0 ([#793](https://github.com/Microsoft/fast/issues/793)) ([e203e86](https://github.com/Microsoft/fast/commit/e203e86))
* **fast-components-react-base:** add callback to horizontal overflow to return and object that informs scroll start and end ([#797](https://github.com/Microsoft/fast/issues/797)) ([37975f3](https://github.com/Microsoft/fast/commit/37975f3))
<a name="2.0.0"></a>
# [2.0.0](https://github.com/Microsoft/fast/compare/v1.6.0...v2.0.0) (2018-08-02)
### Bug Fixes
* **fast-components-react-msft:** fixes error running jest with components that require chroma ([#687](https://github.com/Microsoft/fast/issues/687)) ([140457c](https://github.com/Microsoft/fast/commit/140457c))
### Features
* **detail view:** add detail view ([#470](https://github.com/Microsoft/fast/issues/470)) ([665b871](https://github.com/Microsoft/fast/commit/665b871))
* **fast-css-editor-react:** add default editor component and position component ([#636](https://github.com/Microsoft/fast/issues/636)) ([72037a8](https://github.com/Microsoft/fast/commit/72037a8))
* remove node sass from fast-animations ([#735](https://github.com/Microsoft/fast/issues/735)) ([645f811](https://github.com/Microsoft/fast/commit/645f811))
<a name="1.9.0"></a>
# [1.9.0](https://github.com/Microsoft/fast/compare/v1.6.0...v1.9.0) (2018-07-14)
### Bug Fixes
* **fast-components-react-msft:** fixes error running jest with components that require chroma ([#687](https://github.com/Microsoft/fast/issues/687)) ([140457c](https://github.com/Microsoft/fast/commit/140457c))
### Features
* **detail view:** add detail view ([#470](https://github.com/Microsoft/fast/issues/470)) ([665b871](https://github.com/Microsoft/fast/commit/665b871))
* **fast-css-editor-react:** add default editor component and position component ([#636](https://github.com/Microsoft/fast/issues/636)) ([72037a8](https://github.com/Microsoft/fast/commit/72037a8))
<a name="1.8.0"></a>
# [1.8.0](https://github.com/Microsoft/fast/compare/v1.6.0...v1.8.0) (2018-06-12)
<a name="1.7.0"></a>
# [1.7.0](https://github.com/Microsoft/fast/compare/v1.6.0...v1.7.0) (2018-06-01)
### Features
* **detail view:** add detail view ([#470](https://github.com/Microsoft/fast/issues/470)) ([665b871](https://github.com/Microsoft/fast/commit/665b871))
<a name="1.6.0"></a>
# [1.6.0](https://github.com/Microsoft/fast/compare/v1.2.0...v1.6.0) (2018-05-16)
<a name="1.5.0"></a>
# [1.5.0](https://github.com/Microsoft/fast/compare/v1.2.0...v1.5.0) (2018-05-16)
### Features
* **form generator:** updates styles found in configuration pane ([#420](https://github.com/Microsoft/fast/issues/420)) ([919121b](https://github.com/Microsoft/fast/commit/919121b))
<a name="1.4.0"></a>
# [1.4.0](https://github.com/Microsoft/fast/compare/v1.2.0...v1.4.0) (2018-05-14)
### Features
* **form generator:** updates styles found in configuration pane ([#420](https://github.com/Microsoft/fast/issues/420)) ([919121b](https://github.com/Microsoft/fast/commit/919121b))
<a name="1.2.0"></a>
# 1.2.0 (2018-05-10)
<a name="1.1.0"></a>
# 1.1.0 (2018-05-09)
### Bug Fixes
* ensure app build and tslint processes run prior in the build gate ([#132](https://github.com/Microsoft/fast/issues/132)) ([e74f953](https://github.com/Microsoft/fast/commit/e74f953))
* fix tslint globbing issue and enforce whitespace in import/export statements ([#219](https://github.com/Microsoft/fast/issues/219)) ([4637a90](https://github.com/Microsoft/fast/commit/4637a90))
* **animation:** refactor animate sequence and group to remove code duplication ([#325](https://github.com/Microsoft/fast/issues/325)) ([2beeb98](https://github.com/Microsoft/fast/commit/2beeb98))
* **tslint:** fix tslint errors ([#114](https://github.com/Microsoft/fast/issues/114)) ([78fea3e](https://github.com/Microsoft/fast/commit/78fea3e))
* **tslint:** fixes incorrect tslint rule regarding ordered imports ([#188](https://github.com/Microsoft/fast/issues/188)) ([ebe0b30](https://github.com/Microsoft/fast/commit/ebe0b30))
* travis-CI build-break ([#336](https://github.com/Microsoft/fast/issues/336)) ([bffbf5e](https://github.com/Microsoft/fast/commit/bffbf5e))
### Features
* **checkbox:** add new component with styles ([#252](https://github.com/Microsoft/fast/issues/252)) ([3ad3988](https://github.com/Microsoft/fast/commit/3ad3988))
* **Image:** add new component and msft styles ([#237](https://github.com/Microsoft/fast/issues/237)) ([ea057ed](https://github.com/Microsoft/fast/commit/ea057ed))
* **toggle:** add new component and msft styles ([#212](https://github.com/Microsoft/fast/issues/212)) ([b9dd3e0](https://github.com/Microsoft/fast/commit/b9dd3e0))
* add form generator to the packages ([#311](https://github.com/Microsoft/fast/issues/311)) ([a339b3c](https://github.com/Microsoft/fast/commit/a339b3c))
* add snapshot test suite ([#207](https://github.com/Microsoft/fast/issues/207)) ([7ceaafe](https://github.com/Microsoft/fast/commit/7ceaafe))
* catagorizing relevant dependencies as peerDependencies ([#186](https://github.com/Microsoft/fast/issues/186)) ([7e15db6](https://github.com/Microsoft/fast/commit/7e15db6))
* remove JSS manager dependency from React base components ([#148](https://github.com/Microsoft/fast/issues/148)) ([48de34a](https://github.com/Microsoft/fast/commit/48de34a))
* update code coverage on travis ([#330](https://github.com/Microsoft/fast/issues/330)) ([63ab4f4](https://github.com/Microsoft/fast/commit/63ab4f4))

Просмотреть файл

@ -1,164 +0,0 @@
# FAST Animation
Fast Animation is a library for simplifying the creation of animations and interactions using the Web Animation API.
![JavaScript](https://img.shields.io/badge/ES6-Supported-yellow.svg?style=for-the-badge&logo=JavaScript) &nbsp; ![TypeScript](https://img.shields.io/badge/TypeScript-Supported-blue.svg?style=for-the-badge) &nbsp; ![TypeScript](https://img.shields.io/badge/WAAPI-Supported-purple.svg?style=for-the-badge)
## Benefits
* Provides an abstraction layer over the [Web Animation API][WAAPI] while still allowing full access to its functionality.
* Simplifies complex animations on elements.
* Provides mechanisms for sequencing and grouping animation effects between elements.
## Usage
[//]: <> (todo: Implement once subsites for API exist [API Reference]https:/www.fast.design/docs/en/packages/fast-animation/api/index.html)
### Installation
```bash
npm install @microsoft/fast-animation
```
### Getting started
The primary tools for creating animations are the `AnimateTo` and `AnimateFrom` classes. These classes allow you to apply animations to HTML elements:
```javascript
var myHtmlElement = document.getElementById("myElement");
var myAnimation = new AnimateTo(myHtmlElement, { x: "30%", scale: 2 }, { duration: 300, delay: 20 });
// Play the animation.
myAnimation.play();
```
Sequencing and grouping classes are provided to help orchestrate more elaborate animations:
```javascript
var mySequence = new AnimateSequence([
new AnimateTo(element1, { x: 20 }),
new AnimateTo(element2, { x: 20 }),
new AnimateTo(element3, { x: 20 })
]);
// Attach an onFinish callback.
mySequence.onFinish = () => {
window.prompt("Did you like our animations?");
}
// Play all animations, one after another. When completed, ask the user if they liked the animation sequence.
mySequence.play();
```
### Package exports
#### AnimateTo | AnimateFrom
The`AnimateTo` and `AnimateFrom` classes can be used to create a variety of complex animations. These classes allow you to specify which properties should be animating as well as information like easing curves, duration, delay, and more.
##### Constructor
```js
constructor(el: HTMLElement, options?: Options, timing?: EffectTiming)
```
Both `AnimateTo` and `AnimateFrom` accept one required parameter and two optional parameters:
```javascript
var myAnimation = new AnimateTo(myHtmlElement, { x: 20 }, { duration: 250 });
```
##### Options
The following options are used to construct the animation and configure which properties the animation should change:
* `x: { number | string }` adjusts the element's horizontal position. This property adjusts the `transform` property.
* `y: { number | string }` adjusts the element's vertical position. This property adjusts the `transform` property.
* `scale: { number[] | number }` adjusts the scale of the element. If given a number, scale applies to both x and y-axes. If given an array of two numbers, x and y scale independently. This property adjusts the `transform` property.
* `rotate: { number }` adjusts the rotation of the object by a number of degrees. This property adjusts the `transform` property.
* `top: { string | number }` adjusts the `top` property.
* `right: { string | number }` adjusts the `right` property.
* `bottom: { string | number }` adjusts the `bottom` property.
* `left: { string | number }` adjusts the `left` property.
* `transformOrigin: { string }` adjusts the origin of any transform effects applied via the animation.
* `transformStyle: { string }` applies the value to the `transfrom-style` property of the element.
##### Effect timing
The `EffectTiming` object is passed directly to the WAAPI and should conform to the [AnimationEffectTiming][AET].
##### Public methods
The public methods exposed by the `AnimateTo` and ``AnimateFrom`` classes are `play`, `pause`, `finish`, `cancel`, and `reverse`. For more information on what these methods do, see the corresponding method exposed by the [WAAPI Animation object][WAAPI:AO].
###### Life cycle hooks
* If `onFinish: () => void` is provided, `onFinish` is called when the animation has completed.
* If `onCancel: () => void` is provided, `onCancel` is called when (and if) the animation is canceled.
#### AnimateGroup | AnimateSequence
The `AnimateGroup` and `AnimateSequence` classes are both classes that allow you to group and play animations together. Each takes an array of AnimateTo or AnimateFrom objects and provides an API to `play`, `pause`, `finish`, `cancel`, and `reverse` the collection of animations. `AnimateGroup` allows you to control all animations simultaneously while `AnimateSequence` allows you to control all animations in a sequence:
```javascript
var myAnimationGroup = new AnimateGroup([
new AnimateTo(element1),
new AnimateTo(element2)
]);
var myAnimationSequence = new AnimateSequence([
new AnimateTo(element3),
new AnimateTo(element4)
]);
// AnimateGroup plays all animations at the same time.
myAnimationGroup.play();
// AnimateSequence plays all animations one after another in array order.
myAnimationSequence.play();
```
##### Constructor
Both `AnimateGroup` and `AnimateSequence` accept an array of `AnimateTo` and `AnimateFrom` objects:
```js
constructor(AnimateTo[] | AnimateFrom[])
```
##### Public methods
The public methods exposed by the ``AnimateGroup`` and ``AnimateSequence`` classes are `play`, `pause`, `finish`, `cancel`, and `reverse`.
###### Life cycle hooks
If `onFinish: () => void` is provided, `onFinish` is called when the animation has completed.
### Testing
* Run `yarn dev-server:react` to view the React examples (localhost:9005).
* Run `yarn test` to run unit-tests.
## Dependencies
This system uses the emerging Web Animations API that is not supported in all browsers. To provide the broadest support, we recommend you include the web animations polyfill (use the 'next' version).
The polyfill assumes several global variables exist, and in our experience, it has caused polymorphic JavaScript to fail to compile with tools like TypeScript and Babel. There are also other advantages to CDN files, so we've chosen to take the Web Animations API polyfill as a peer dependency.
Find the polyfill and documentation [here][PF:WAAPI].
## Additional resources
[MDN Web Animations API documentation][WAAPI]
[Can I Use: Web Animations API][CIU:WAAPI]
[WAAPI]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API
[CIU:WAAPI]: https://caniuse.com/#feat=web-animation
[PF:WAAPI]: https://cdnjs.com/libraries/web-animations
[AET]: https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTiming
[WAAPI:AO]: https://developer.mozilla.org/en-US/docs/Web/API/Animation

Просмотреть файл

@ -1,331 +0,0 @@
import Animate, { AnimateConfig } from "../lib/animate";
class AnimateMock extends Animate {}
describe("Animate initilization", () => {
const fakeElement: HTMLElement = document.createElement("div");
test("should register a single input element", () => {
expect(new AnimateMock(fakeElement)["animationTarget"]).toBe(fakeElement);
});
test("should correctly assign options", () => {
expect(new AnimateMock(fakeElement).options).toEqual({});
});
});
describe("getPropertiesToAnimate", () => {
const fakeElement: HTMLElement = document.createElement("div");
test("should return an empty array when no options are set", () => {
expect(new AnimateMock(fakeElement)["getPropertiesToAnimate"]()).toEqual([]);
});
test('should return an array with a single index of "transform" when x is set', () => {
expect(
new AnimateMock(fakeElement, { x: 20 })["getPropertiesToAnimate"]()
).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when y is set', () => {
expect(
new AnimateMock(fakeElement, { y: 20 })["getPropertiesToAnimate"]()
).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when rotate is set', () => {
expect(
new AnimateMock(fakeElement, { rotate: 20 })["getPropertiesToAnimate"]()
).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when scale is set', () => {
expect(
new AnimateMock(fakeElement, { scale: 20 })["getPropertiesToAnimate"]()
).toEqual(["transform"]);
});
test('should return an array with a single index of "transform" when scale is set', () => {
expect(
new AnimateMock(fakeElement, { scale: [20, 10] })["getPropertiesToAnimate"]()
).toEqual(["transform"]);
});
test('should return an array with a single index of "opacity" when opacity is set', () => {
expect(
new AnimateMock(fakeElement, { opacity: 0 })["getPropertiesToAnimate"]()
).toEqual(["opacity"]);
});
test('should return an array with both "transform" and "opacity" when opacity and x are set', () => {
const properties: string[] = new AnimateMock(fakeElement, {
x: 20,
opacity: 0,
})["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and y are set', () => {
const properties: string[] = new AnimateMock(fakeElement, {
y: 20,
opacity: 0,
})["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and rotate are set', () => {
const properties: string[] = new AnimateMock(fakeElement, {
rotate: 20,
opacity: 0,
})["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with both "transform" and "opacity" when opacity and scale are set', () => {
const properties: string[] = new AnimateMock(fakeElement, {
scale: 20,
opacity: 0,
})["getPropertiesToAnimate"]();
expect(properties.includes("opacity")).toBe(true);
expect(properties.includes("transform")).toBe(true);
});
test('should return an array with "transform" when more than one transform properties are set', () => {
const properties: string[] = new AnimateMock(fakeElement, {
scale: 20,
rotate: 20,
x: 20,
y: 20,
})["getPropertiesToAnimate"]();
expect(properties).toEqual(["transform"]);
});
});
describe("formatTransformFunction", () => {
const animation: AnimateMock = new AnimateMock(document.createElement("div"));
test("should correctly format the x config", () => {
expect(animation["formatTransformFunction"]("x", 20)).toBe("translateX(20px)");
});
test("should correctly format the y config", () => {
expect(animation["formatTransformFunction"]("y", 20)).toBe("translateY(20px)");
});
test("should correctly format the x config", () => {
expect(animation["formatTransformFunction"]("x", "20%")).toBe("translateX(20%)");
});
test("should correctly format the y config", () => {
expect(animation["formatTransformFunction"]("y", "20%")).toBe("translateY(20%)");
});
test("should correctly format the rotate config", () => {
expect(animation["formatTransformFunction"]("rotate", 20)).toBe("rotate(20deg)");
});
test("should correctly format the scale config", () => {
expect(animation["formatTransformFunction"]("scale", 20)).toBe("scale(20)");
});
test("should correctly format the scale config", () => {
expect(animation["formatTransformFunction"]("scale", [20, 10])).toBe(
"scale(20,10)"
);
});
test("should return empty string if function cannot be converted to a transform function", () => {
expect(animation["formatTransformFunction"]("foobar", 20)).toBe("");
});
});
describe("getOptionKeyframeValues", () => {
const fakeElement: HTMLElement = document.createElement("div");
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { opacity: 0 })["getOptionKeyframeValues"]()
).toEqual({
opacity: "0",
});
});
test("should correclty create x keyframe value", () => {
expect(
new AnimateMock(fakeElement, { x: 20 })["getOptionKeyframeValues"]()
).toEqual({
transform: "translateX(20px)",
});
});
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { y: 20 })["getOptionKeyframeValues"]()
).toEqual({
transform: "translateY(20px)",
});
});
test("should correclty create x keyframe value", () => {
expect(
new AnimateMock(fakeElement, { x: "20%" })["getOptionKeyframeValues"]()
).toEqual({
transform: "translateX(20%)",
});
});
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { y: "20%" })["getOptionKeyframeValues"]()
).toEqual({
transform: "translateY(20%)",
});
});
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { scale: 20 })["getOptionKeyframeValues"]()
).toEqual({
transform: "scale(20)",
});
});
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { scale: [20, 10] })["getOptionKeyframeValues"]()
).toEqual({
transform: "scale(20,10)",
});
});
test("should correclty create opacity keyframe value", () => {
expect(
new AnimateMock(fakeElement, { rotate: 20 })["getOptionKeyframeValues"]()
).toEqual({
transform: "rotate(20deg)",
});
});
});
describe("getOptionKeyframes", () => {
const fakeElement: HTMLElement = document.createElement("div");
// All keyframes will have an initial property value of `undefined` because we rely on the native browsers 'getComputedStyle'
// method to obtain initial values, which in a node environment return undefined.
test("should correctly return keyframes with the x option", () => {
expect(new AnimateMock(fakeElement, { x: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: "" },
{ transform: "translateX(20px)" },
]);
});
test("should correctly return keyframes with the y option", () => {
expect(new AnimateMock(fakeElement, { y: 20 })["getOptionKeyframes"]()).toEqual([
{ transform: "" },
{ transform: "translateY(20px)" },
]);
});
test("should correctly return keyframes with the scale option", () => {
expect(
new AnimateMock(fakeElement, { scale: 20 })["getOptionKeyframes"]()
).toEqual([{ transform: "" }, { transform: "scale(20)" }]);
});
test("should correctly return keyframes with the independent scale option", () => {
expect(
new AnimateMock(fakeElement, { scale: [20, 10] })["getOptionKeyframes"]()
).toEqual([{ transform: "" }, { transform: "scale(20,10)" }]);
});
test("should correctly return keyframes with the rotate option", () => {
expect(
new AnimateMock(fakeElement, { rotate: 20 })["getOptionKeyframes"]()
).toEqual([{ transform: "" }, { transform: "rotate(20deg)" }]);
});
test("should correctly return keyframes with the opacity option", () => {
expect(
new AnimateMock(fakeElement, { opacity: 1 })["getOptionKeyframes"]()
).toEqual([{ opacity: "" }, { opacity: "1" }]);
});
test("should correctly return keyframes with both an opacity and transform function", () => {
expect(
new AnimateMock(fakeElement, { opacity: 1, scale: 2 })["getOptionKeyframes"]()
).toEqual([
{ opacity: "", transform: "" },
{ opacity: "1", transform: "scale(2)" },
]);
});
test("should correctly return keyframes with both two transform functions", () => {
expect(
new AnimateMock(fakeElement, { x: 20, scale: 2 })["getOptionKeyframes"]()
).toEqual([{ transform: "" }, { transform: "translateX(20px) scale(2)" }]);
});
});
describe("sortOffsets", () => {
test("should sort offset strings in ascending order", () => {
const setOne: string[] = ["0", "1"];
const setTwo: string[] = ["1", "0"];
const setThree: string[] = [".72", ".29", "1", ".1", "0"];
const sortOffsets: (offsets: string[]) => string[] = new AnimateMock(
document.createElement("div")
)["sortOffsets"];
expect(sortOffsets(setOne)).toEqual(["0", "1"]);
expect(sortOffsets(setTwo)).toEqual(["0", "1"]);
expect(sortOffsets(setThree)).toEqual(["0", ".1", ".29", ".72", "1"]);
});
});
describe("consolidateKeyframes", () => {
const fakeElement: HTMLElement = document.createElement("div");
const expectedOne: any[] = [
{ offset: 0, opacity: "", transform: "" },
{
offset: 1,
opacity: "0",
transform: "translateX(20px) translateY(20px) rotate(20deg) scale(20)",
},
];
const sourceOne: AnimateConfig = {
scale: 20,
opacity: 0,
rotate: 20,
x: 20,
y: 20,
};
const customKeyframes: Array<Partial<Keyframe>> = [
{ opacity: "0" },
{ opacity: "0.75", offset: 0.75 },
{ opacity: "1" },
];
test("should correctly consolidate and sort keyframes created by options", () => {
expect(new AnimateMock(fakeElement, sourceOne).keyframes).toEqual(expectedOne);
});
test("should correctly consolidate after addKeyframes is used", () => {
const mock: AnimateMock = new AnimateMock(fakeElement);
mock.addKeyframes(customKeyframes);
expect(mock.keyframes).toEqual([
{ opacity: "0", offset: 0 },
{ opacity: "0.75", offset: 0.75 },
{ opacity: "1", offset: 1 },
]);
});
test("should correclty consolidate both options and added keyframes", () => {
const mock: AnimateMock = new AnimateMock(fakeElement, { scale: 20 });
mock.addKeyframes(customKeyframes);
expect(mock.keyframes).toEqual([
{ offset: 0, opacity: "0", transform: "" },
{ offset: 0.75, opacity: "0.75" },
{ offset: 1, opacity: "1", transform: "scale(20)" },
]);
});
});

Просмотреть файл

@ -1,5 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../api-extractor.json",
"mainEntryPointFilePath": "dist/index.d.ts"
}

Просмотреть файл

@ -1,12 +0,0 @@
module.exports = {
presets: [
[
"@babel/preset-env", {
"targets": {
"node": "current"
}
}
],
"@babel/react"
]
}

Просмотреть файл

@ -1,22 +0,0 @@
import React from "react";
import ReactDOM from "react-dom";
import TestPage from "./components/TestPage";
/**
* App component definition
* @extends React.Component
*/
class App extends React.Component {
/**
* Renders the component
* @return {function}
*/
public render(): JSX.Element {
return <TestPage />;
}
}
/**
* Primary render function for app. Called on store updates
*/
ReactDOM.render(<App />, document.getElementById("root"));

Просмотреть файл

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20.38 29.24"><defs><style>.cls-1{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{fill:#dadada;}.cls-4{fill-opacity:0;}.cls-5{fill:#818181;}</style><clipPath id="clip-path" transform="translate(-490.06 -486.5)"><path class="cls-1" d="M500.05,482.69a14.5,14.5,0,0,0-14.5,14.5c0,8,6.42,20.63,14.43,20.63s14.51-12.62,14.51-20.62a14.45,14.45,0,0,0-14.44-14.5"/></clipPath></defs><title>Untitled-1</title><g class="cls-2"><path class="cls-3" d="M500.25,486.5a10.19,10.19,0,1,1-10.19,10.19A10.19,10.19,0,0,1,500.25,486.5Z" transform="translate(-490.06 -486.5)"/><path class="cls-3" d="M504.8,511h-9.43l4.72,4.72Z" transform="translate(-490.06 -486.5)"/><path class="cls-4" d="M506.05,492.69,497.78,501l-4-4" transform="translate(-490.06 -486.5)"/><polygon class="cls-5" points="7.72 16.59 2.63 11.49 4.75 9.37 7.72 12.34 14.94 5.13 17.06 7.25 7.72 16.59"/></g></svg>

До

Ширина:  |  Высота:  |  Размер: 992 B

Просмотреть файл

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600"><defs><style>.cls-1{fill:#4c4c4c;}.cls-2{fill:#7f7f7f;}</style></defs><title>easing-curveUntitled-1</title><path class="cls-1" d="M782.68,200.68a8.61,8.61,0,0,0-1.75-.18,7.27,7.27,0,0,0-.92.07s0,0,0-.07v.07a8.07,8.07,0,0,0-.94.18C205.62,214.62,437.16,800.5,200,800.5H800v-580h-.81v0l.81,0v-20C794.14,200.5,788.38,200.57,782.68,200.68Z" transform="translate(-200 -200.5)"/><rect class="cls-2" width="600" height="600"/><path class="cls-1" d="M200,800.5H800v-600C320,200.5,680,800.5,200,800.5Z" transform="translate(-200 -200.5)"/></svg>

До

Ширина:  |  Высота:  |  Размер: 630 B

Просмотреть файл

@ -1,272 +0,0 @@
body {
padding: 150px;
}
body * {
box-sizing: border-box;
}
.container-square {
width: 336px;
height: 336px;
padding: 32px;
background: #686868;
position: relative;
z-index: 1;
margin-bottom: 36px;
overflow: hidden;
}
.container-square .fill-bar-container {
width: 196px;
margin: 0 auto;
font-family: 'Segoe UI', 'Segoe', sans-serif;
font-weight: bold;
color: #a3a3a3;
position: absolute;
bottom:28px;
left: 0;
right: 0;
}
#doSvg1 {
width: 18px;
position: absolute;
left: 130px;
top: -28px;
transform: scale(0, 0);
}
#highlighter {
height: 112px;
width: 100%;
position: absolute;
top: 72px;
left: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 3;
}
#diamond {
margin: 50px auto;
width: 0;
height: 0;
border: 60px solid transparent;
border-bottom-color: #dadada;
position: relative;
top: -60px;
}
#diamond:before {
content: '';
width: 118px;
height: 2px;
background: #dadada;
position: absolute;
left: -59px;
top: 59px;
}
#diamond:after {
content: '';
position: absolute;
left: -60px;
top: 60px;
width: 0;
height: 0;
border: 60px solid transparent;
border-top-color: #dadada;
}
#easingCurve {
width: 200px;
height: 200px;
margin-top: 35px;
}
.container-square#overlapping {
display: flex;
align-items: center;
justify-content: center;
}
.container-square .page-container {
background-color: #dadada;
min-height: 100%;
padding: 15px 0 8px 0;
}
.container-square .page-container .ui-line {
width: 120px;
height: 10px;
background: #818181;
margin: 0 auto 34px auto;
position: relative;
z-index: 4;
}
.container-square .page-container .ui-box {
width: 175px;
height: 82px;
margin: 0 auto;
background: #818181;
display: flex;
align-items: center;
justify-content: center;
margin-top: 22px;
position: relative;
z-index: 4;
}
.container-square .page-container .ui-box .ui-circle {
width: 27px;
height: 27px;
border-radius: 50%;
background: #dadada;
position: relative;
z-index: 5;
}
.container-square .fill-bar-container .bar-track {
background: #818181;
padding: 3px 0;
}
.container-square .fill-bar-container .bar-track .bar-fill {
width: 1px;
height: 8px;
background: #fff;
transform: scale(0, 1);
}
.container-square .fill-bar-container .bar-numbers {
display: table;
table-layout: fixed;
width: calc(100% + 24px);
margin: 0 -12px;
}
.container-square .fill-bar-container .bar-numbers span {
display: table-cell;
text-align: center;
}
.container-square #overlapping .light-square,
.container-square #overlapping .medium-square {
margin: 0 auto;
}
.light-square {
background: #d1d1d1;
}
.medium-square {
background: #a4a4a4;
}
.dark-square {
background: #4c4c4c;
}
.light-square,
.medium-square,
.dark-square {
display: inline-block;
}
.size-small {
width: 36px;
height: 36px;
position: relative;
z-index: 4;
margin: 28px;
}
.size-medium {
width: 60px;
height: 60px;
}
.size-medium.fill-width {
width: 100%;
}
.size-large {
width: 120px;
height: 120px;
}
.size-fill {
margin: 32px;
width: calc(100% - 64px);
padding-bottom: calc(100% - 64px);
position: absolute;
top: 0;
left: 0;
transform: scale(0);
}
.circle-large {
background: #498f90;
border-radius: 50%;
height: 50px;
width: 50px;
position: absolute;
bottom: 42px;
right: 32px;
}
.circle-small {
background: #d1d1d1;
border-radius: 50%;
height: 16px;
width: 16px;
position: absolute;
bottom: 59px;
left: 24px;
}
.centerizer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 212px;
height: 60px;
display: flex;
justify-content: space-between;
}
.centerizer.foursquare {
width: 288px;
}
.centerizer.large {
height: 120px;
}
.centerizer.large.single {
width: 120px;
}
.centerizer.layout {
width: 136px;
height: 212px;
flex-direction: column;
}
.centerizer.layout .row1,
.centerizer.layout .row2,
.centerizer.layout .row3 {
opacity: 0;
}
.centerizer.layout > div {
width: 100%;
display: flex;
justify-content: space-between;
}

Просмотреть файл

@ -1,418 +0,0 @@
import React from "react";
import ScrollTrigger from "../../../../lib/triggers/ScrollTrigger";
import ViewEnterTrigger from "../../../../lib/triggers/ViewEnterTrigger";
import ViewExitTrigger from "../../../../lib/triggers/ViewExitTrigger";
import { AnimateConfig } from "../../../../lib/animate";
import AnimateTo from "../../../../lib/animateTo";
import AnimateFrom from "../../../../lib/animateFrom";
import { cubicBezier } from "../../../../lib/curves";
import AnimateGroup from "../../../../lib/animateGroup";
import AnimateSequence from "../../../../lib/animateSequence";
/* eslint-disable */
const sass: string = require("../assets/styles/test-page.css");
const easingCurve: string = require("../assets/images/easing-curve.svg");
const doSvg1: string = require("../assets/images/do-check.svg");
/* eslint-enable */
class TestPage extends React.Component {
private scrollElement: HTMLElement;
public componentDidMount(): void {
const overlapping: HTMLElement = document.getElementById("overlapping");
const scrollTrigger: ScrollTrigger = new ScrollTrigger();
const viewEnterTrigger: ViewEnterTrigger = new ViewEnterTrigger();
const viewExitTrigger: ViewExitTrigger = new ViewExitTrigger();
const lightSquare: HTMLElement = overlapping.querySelector(".light-square");
const mediumSquare: HTMLElement = overlapping.querySelector(".medium-square");
const effects: EffectTiming = {
duration: 750,
iterations: Infinity,
direction: "alternate",
easing: cubicBezier("fastInOut"),
};
new AnimateTo(
lightSquare as HTMLElement,
{
scale: 2.27,
transformOrigin: "0 50%",
},
effects
).play();
new AnimateTo(
mediumSquare as HTMLElement,
{
scale: 2.27,
transformOrigin: "100% 50%",
},
Object.assign({}, effects, { delay: 750 })
).play();
/* eslint-disable */
scrollTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log(distance);
});
viewEnterTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log('entered');
});
viewExitTrigger.subscribe(this.scrollElement, (distance: number) => {
// Save for debugging
// console.log('exited');
});
/* eslint-enable */
}
public render(): JSX.Element {
return (
<div>
<div className="container-square">
<div id="highlighter" />
<div className="page-container">
<div className="ui-line" />
{this.generateUIBox("circle1")}
{this.generateUIBox("circle2")}
</div>
</div>
<div className="container-square" onClick={this.clickContainer2}>
<div id="diamond" />
<div className="fill-bar-container">
<img id="doSvg1" src={doSvg1} alt="check mark" />
<div className="bar-track">
<div className="bar-fill" />
</div>
<div className="bar-numbers">
<span>0</span>
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
</div>
</div>
</div>
<div id="overlapping" className="container-square">
<div className="light-square size-medium" />
<div className="medium-square size-medium" />
</div>
<div className="container-square" onMouseEnter={this.hoverContainer4}>
<div className="centerizer">
<div className="dark-square size-medium" />
<div className="medium-square size-medium" />
<div className="light-square size-medium" />
</div>
</div>
<div className="container-square">
<div
className="medium-square size-small"
onClick={this.clickContainer5}
/>
<div className="light-square size-fill" />
</div>
<div className="container-square" onClick={this.clickContainer6}>
<img id="easingCurve" src={easingCurve} alt="easing curve" />
<div className="circle-small" />
<div className="circle-large" />
</div>
<div
className="container-square"
onMouseEnter={this.hoverContainer7}
ref={this.setRef("scrollElement")}
>
<div className="centerizer large single">
<div className="medium-square size-large" />
</div>
</div>
<div className="container-square" onClick={this.clickContainer8}>
<div className="centerizer layout">
<div className="row1">
<div className="medium-square size-medium fill-width" />
</div>
<div className="row2">
<div className="dark-square size-medium" />
<div className="light-square size-medium" />
</div>
<div className="row3">
<div className="medium-square size-medium" />
<div className="dark-square size-medium" />
</div>
</div>
</div>
<div className="container-square" onClick={this.clickContainer9}>
<div className="centerizer foursquare">
<div id="fourSquare1" className="dark-square size-medium" />
<div id="fourSquare2" className="medium-square size-medium" />
<div id="fourSquare3" className="light-square size-medium" />
<div id="fourSquare4" className="light-square size-medium" />
</div>
</div>
</div>
);
}
private generateUIBox(id: string): JSX.Element {
return (
<div className="ui-box" onClick={this.clickContainer1}>
<div id={id} className="ui-circle" />
</div>
);
}
private setRef(name: string): (ref: HTMLElement) => void {
return (ref: HTMLElement): void => {
this[name] = ref;
};
}
private clickContainer1 = (e: React.MouseEvent<any>): void => {
const highlighter: HTMLElement = document.getElementById("highlighter");
const circle: HTMLElement = e.currentTarget.querySelector(".ui-circle");
const slide: AnimateTo = new AnimateTo(
highlighter,
{ y: e.currentTarget.offsetTop - 87 },
{ duration: 250 }
);
const bounceKeyframes: Array<Partial<Keyframe>> = [
{ transform: "scale(1)" },
{ transform: "scale(2.2)", offset: 0.35 },
{ transform: "scale(2)", offset: 0.55 },
{ transform: "scale(0.9)", offset: 0.93 },
{ transform: "scale(1)" },
];
const circleAnimation: AnimateTo = new AnimateTo(
circle,
{},
{ duration: 500, easing: cubicBezier("fastInOut") }
);
circleAnimation.addKeyframes(bounceKeyframes);
circleAnimation.play();
slide.play();
};
private clickContainer2 = (e: React.MouseEvent<any>): void => {
const bar: HTMLElement = e.currentTarget.querySelector(".bar-fill");
const diamond: HTMLElement = e.currentTarget.querySelector("#diamond");
const checkmark: HTMLElement = e.currentTarget.querySelector("#doSvg1");
const diamondFrames: Array<Partial<Keyframe>> = [
{ transform: "scale(1)" },
{ transform: "scale(1.6)", offset: 0.75 },
{ transform: "scale(0.95)", offset: 0.78 },
{ transform: "scale(1.05)", offset: 0.81 },
{ transform: "scale(0.97)", offset: 0.83 },
{ transform: "scale(1)", offset: 0.85 },
{ transform: "scale(1)" },
];
const doSvgFrames: Array<Partial<Keyframe>> = [
{ transform: "scale(0)" },
{ transform: "scale(1.1)", offset: 0.9 },
{ transform: "scale(1)" },
];
const barAnimation: AnimateTo = new AnimateTo(
bar,
{ scale: [138, 1], transformOrigin: "0 0" },
{ duration: 5000 }
);
const diamondAnimation: AnimateTo = new AnimateTo(
diamond,
{ transformOrigin: "50% 100%" },
{ duration: 5000 }
);
const doAnimation: AnimateTo = new AnimateTo(
checkmark,
{ transformOrigin: "50% 100%" },
{ fill: "both", delay: 4650, duration: 250 }
);
doAnimation.addKeyframes(doSvgFrames);
diamondAnimation.addKeyframes(diamondFrames);
new AnimateGroup([barAnimation, diamondAnimation, doAnimation]).play();
};
private hoverContainer4 = (e: React.MouseEvent<any>): void => {
const darkSquare: HTMLElement = e.currentTarget.querySelector(".dark-square");
const mediumSquare: HTMLElement = e.currentTarget.querySelector(".medium-square");
const lightSquare: HTMLElement = e.currentTarget.querySelector(".light-square");
const fadeOptions: AnimateConfig = { opacity: 0 };
const fadeEffect: EffectTiming = { duration: 100 };
const slideOptions: AnimateConfig = { x: 180 };
const slideEffect: EffectTiming = {
duration: 300,
fill: "backwards" as FillMode,
easing: cubicBezier("easeOut"),
};
new AnimateGroup([
new AnimateFrom(darkSquare, fadeOptions, fadeEffect),
new AnimateFrom(darkSquare, slideOptions, slideEffect),
new AnimateFrom(mediumSquare, fadeOptions, fadeEffect),
new AnimateFrom(
mediumSquare,
slideOptions,
Object.assign({}, slideEffect, { delay: 40 })
),
new AnimateFrom(lightSquare, fadeOptions, fadeEffect),
new AnimateFrom(
lightSquare,
slideOptions,
Object.assign({}, slideEffect, { delay: 80 })
),
]).play();
};
private clickContainer5 = (e: React.MouseEvent<any>): void => {
const lilsquare: HTMLElement = e.currentTarget;
const filler: HTMLElement = (lilsquare.parentNode as HTMLElement).querySelector(
".size-fill"
);
const fillAnimation: AnimateTo = new AnimateTo(
filler,
{ scale: 1, transformOrigin: " 0 0" },
{ duration: 250, easing: cubicBezier("easeOutSmooth") }
);
fillAnimation.play();
lilsquare.classList.add("dark-square");
lilsquare.classList.remove("medium-square");
function swapBack(): void {
lilsquare.classList.remove("dark-square");
lilsquare.classList.add("medium-square");
}
window.setTimeout((): void => {
fillAnimation.reverse();
swapBack();
}, 2750);
};
private clickContainer6 = (e: React.MouseEvent<any>): void => {
const lilcircle: HTMLElement = e.currentTarget.querySelector(".circle-small");
const bigcircle: HTMLElement = e.currentTarget.querySelector(".circle-large");
new AnimateGroup([
new AnimateTo(lilcircle, { x: 200 }, { duration: 3000 }),
new AnimateTo(
lilcircle,
{ bottom: 260 },
{ duration: 3000, easing: cubicBezier("fastInOut") }
),
new AnimateTo(
bigcircle,
{ y: -200 },
{ duration: 3000, easing: cubicBezier("fastInOut") }
),
]).play();
};
private hoverContainer7 = (e: React.MouseEvent<any>): void => {
const square: HTMLElement = e.currentTarget.querySelector(".medium-square");
new AnimateFrom(
square,
{ opacity: 0, x: 60 },
{ duration: 250, easing: cubicBezier("easeOut") }
).play();
};
private clickContainer8 = (e: React.MouseEvent<any>): void => {
const row1: HTMLElement = e.currentTarget.querySelector(".row1");
const row2: HTMLElement = e.currentTarget.querySelector(".row2");
const row3: HTMLElement = e.currentTarget.querySelector(".row3");
const fadeInOptions: AnimateConfig = { opacity: 1 };
const fadeInEffect: EffectTiming = {
duration: 350,
easing: cubicBezier("fastInOut"),
};
const sequence: AnimateSequence = new AnimateSequence([
new AnimateTo(row1, fadeInOptions, fadeInEffect),
new AnimateTo(row2, fadeInOptions, fadeInEffect),
new AnimateTo(row3, fadeInOptions, fadeInEffect),
]);
sequence.onFinish = (): void => {
sequence.onFinish = sequence.pause.bind(sequence);
window.setTimeout(sequence.reverse.bind(sequence), 3000);
};
sequence.play();
};
private clickContainer9 = (e: React.MouseEvent<any>): void => {
const square1: HTMLElement = e.currentTarget.querySelector("#fourSquare1");
const square2: HTMLElement = e.currentTarget.querySelector("#fourSquare2");
const square3: HTMLElement = e.currentTarget.querySelector("#fourSquare3");
const square4: HTMLElement = e.currentTarget.querySelector("#fourSquare4");
const fadeKeyFrames: Array<Partial<Keyframe>> = [
{ opacity: "1" },
{ opacity: "0", offset: 0.4 },
{ opacity: "0", offset: 0.8 },
{ opacity: "1" },
];
const slideOptions: AnimateConfig = { y: -100 };
const slideEffect: EffectTiming = {
duration: 750,
easing: cubicBezier("fastInOut"),
};
const square1Animation: AnimateTo = new AnimateTo(
square1,
slideOptions,
slideEffect
);
const square2Animation: AnimateTo = new AnimateTo(
square2,
slideOptions,
slideEffect
);
const square3Animation: AnimateTo = new AnimateTo(
square3,
slideOptions,
slideEffect
);
const square4Animation: AnimateTo = new AnimateTo(
square4,
slideOptions,
slideEffect
);
square1Animation.addKeyframes(fadeKeyFrames);
square2Animation.addKeyframes(fadeKeyFrames);
square2Animation.effectTiming.delay = 100;
square3Animation.addKeyframes(fadeKeyFrames);
square3Animation.effectTiming.delay = 200;
square4Animation.addKeyframes(fadeKeyFrames);
square4Animation.effectTiming.delay = 2000;
const group: AnimateGroup = new AnimateGroup([
square1Animation,
square2Animation,
square3Animation,
square4Animation,
]);
group.play();
window.setTimeout(() => {
group.reverse();
}, 3000);
};
}
export default TestPage;

Просмотреть файл

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Animation system test page</title>
<link rel="stylesheet" href="/fw_package.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/web-animations/2.3.1/web-animations-next.min.js"></script>
</head>
<body>
<div id="root"></div>
<script src="/app.js" charset="utf-8"></script>
</body>
</html>

Просмотреть файл

@ -1,93 +0,0 @@
## API Report File for "@microsoft/fast-animation"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
// @public
export abstract class Animate {
constructor(element: HTMLElement, options?: AnimateConfig, effectTiming?: EffectTiming);
addKeyframes: (keyframes: Array<Partial<Keyframe>>) => void;
cancel: () => void;
effectTiming: EffectTiming;
finish: () => void;
get keyframeEffect(): KeyframeEffect;
get keyframes(): Keyframe[];
// Warning: (ae-forgotten-export) The symbol "AnimationMode" needs to be exported by the entry point index.d.ts
protected mode: AnimationMode;
onCancel: () => void;
// (undocumented)
get onFinish(): () => void;
set onFinish(callback: () => void);
// Warning: (ae-forgotten-export) The symbol "AnimateConfig" needs to be exported by the entry point index.d.ts
options: AnimateConfig;
pause: () => void;
play: () => void;
reverse: () => void;
}
// @public
export class AnimateFrom extends Animate {
// (undocumented)
protected mode: AnimationMode;
}
// @public
export class AnimateGroup {
constructor(animations: Array<AnimateTo | AnimateFrom>);
cancel: () => void;
finish: () => void;
get onFinish(): () => void;
set onFinish(callback: () => void);
pause: () => void;
play(): void;
reverse(): void;
}
// @public
export class AnimateSequence {
constructor(animations: Array<AnimateTo | AnimateFrom>);
cancel: () => void;
finish: () => void;
onFinish: () => void;
pause: () => void;
play: () => void;
reverse: () => void;
}
// @public
export class AnimateTo extends Animate {
// (undocumented)
protected mode: AnimationMode;
}
// @public
export function cubicBezier(name: "linear" | "easeOut" | "easeOutSmooth" | "easeIn" | "drillIn" | "backToApp" | "appToApp" | "fastIn" | "fastOut" | "fastInOut" | "exponential" | "fastInFortySevenPercent" | "exponentialReversed" | "navPane" | /* @deprecated */ string): string;
// @public
export function fadeIn(element: HTMLElement, effectTiming?: EffectTiming): AnimateTo;
// @public
export function fadeOut(element: HTMLElement, effectTiming?: EffectTiming): AnimateTo;
// Warning: (ae-forgotten-export) The symbol "ScrollTrigger" needs to be exported by the entry point index.d.ts
//
// @public
export class ScrollTrigger extends ScrollTrigger_2 {
protected update(): void;
}
// @public
export class ViewEnterTrigger extends ScrollTrigger_2 {
protected update(): void;
}
// @public
export class ViewExitTrigger extends ScrollTrigger_2 {
protected update(): void;
}
// (No @packageDocumentation comment for this package)
```

Просмотреть файл

@ -1,514 +0,0 @@
/**
* Describes the animation properties
* @public
*/
export interface AnimateConfig {
/**
* The x position change of the animation
*/
x?: number | string;
/**
* The y position change of the animation
*/
y?: number | string;
/**
* The top property
*/
top?: number | string;
/**
* The right property
*/
right?: number | string;
/**
* The bottom proper
*/
bottom?: number | string;
/**
* The left property
*/
left?: number | string;
/**
* The rotation of the animation in degrees
*/
rotate?: number;
/**
* The scale factor of the animation
*/
scale?: number | [number, number];
/**
* The opacity after the animation
*/
opacity?: number;
/**
* The transform-origin for the element
*/
transformOrigin?: string;
/**
* The transform-style for the element
*/
transformStyle?: string;
}
/**
* Enumerates all properties that can be animated, outside of properties supplied directly via Animate.addKeyframes()
* @public
*/
export interface AnimationProperties {
top?: string;
right?: string;
bottom?: string;
left?: string;
opacity?: number;
transform?: string;
}
/**
* Animation mode describes if an animation should animate toward an elements natural position or away from it
*
* @internal
*/
export enum AnimationMode {
animateTo,
animateFrom,
}
/**
* Maps css property names to animation options
*/
interface PropertyMap {
opacity: string[];
transform: string[];
top: string[];
left: string[];
bottom: string[];
right: string[];
}
/**
* Base animate type. This is extended by {@link @microsoft/fast-animation#AnimateTo} and {@link @microsoft/fast-animation#AnimateFrom}.
*
* @public
*/
export default abstract class Animate {
/**
* A mapping between animation options and the css property names they apply to
*/
private static propertyMap: PropertyMap = {
opacity: ["opacity"],
transform: ["x", "y", "rotate", "scale"],
top: ["top"],
left: ["left"],
bottom: ["bottom"],
right: ["right"],
};
/**
* Stores animation options
*/
public options: AnimateConfig;
/**
* Stores animation timing functions
*/
public effectTiming: EffectTiming = {
fill: "forwards",
iterations: 1,
duration: 500,
};
/**
* Callback to call when the animation is canceled
*/
public onCancel: () => void;
/**
* Tracks if the animation should animate toward an elements natural position or away from it
*/
protected mode: AnimationMode;
/**
* Stores the HTML element to be animated
*/
private animationTarget: HTMLElement;
/**
* Stores the WAAPI object for manipulation by our API
*/
private animation: Animation;
/**
* Callback to call when the animation finishes playing
*/
private _onFinish: () => void;
/**
* Stores animation keyframe sets and is accessed by a getter
*/
private _keyframes: Keyframe[][] = [];
public get onFinish(): () => void {
return this._onFinish;
}
public set onFinish(callback: () => void) {
this._onFinish = callback;
if (this.animation) {
this.animation.onfinish = callback;
}
}
constructor(
element: HTMLElement,
options?: AnimateConfig,
effectTiming?: EffectTiming
) {
this.animationTarget = element;
if (effectTiming) {
this.effectTiming = Object.assign({}, this.effectTiming, effectTiming);
}
if (options) {
if (options.transformOrigin) {
element.style.transformOrigin = options.transformOrigin;
}
if (options.transformStyle) {
element.style.transformStyle = options.transformStyle;
}
}
this.options = options || {};
}
/**
* plays the animation
*/
public play = (): void => {
this.ensureAnimationObjectExists();
this.animation.play();
};
/**
* pauses the animation
*/
public pause = (): void => {
this.ensureAnimationObjectExists();
this.animation.pause();
};
/**
* finishes the animation
*/
public finish = (): void => {
this.ensureAnimationObjectExists();
this.animation.finish();
};
/**
* cancels the animation
*/
public cancel = (): void => {
this.ensureAnimationObjectExists();
this.animation.cancel();
};
/**
* reverses an animation
*/
public reverse = (): void => {
this.ensureAnimationObjectExists();
this.animation.reverse();
};
/**
* adds a set of keyframes to set of animation keyframes the animation should execute
*/
public addKeyframes = (keyframes: Array<Partial<Keyframe>>): void => {
this._keyframes.push(keyframes as Keyframe[]);
};
/**
* Ensure animation object
*/
private ensureAnimationObjectExists(): void {
if (typeof this.animation === "undefined") {
this.createAnimationObject();
}
}
/**
* Creates the animation object
*/
private createAnimationObject(): void {
this.animation = new Animation(this.keyframeEffect, document.timeline);
if (typeof this.onFinish !== "undefined") {
this.animation.onfinish = this.onFinish;
}
if (typeof this.onCancel !== "undefined") {
this.animation.oncancel = this.onCancel;
}
}
/**
* Returns a list of properties that will be animated based options
*/
private getPropertiesToAnimate(): string[] {
return Object.keys(Animate.propertyMap).filter((property: string) => {
// Filter out all properties that don't need to be set based on our options
return Animate.propertyMap[property].reduce(
(hasProperty: boolean, animationProp: string) => {
return (
typeof this.options[animationProp] !== "undefined" || hasProperty
);
},
false
);
});
}
/**
* Current implmentations of web animations seem to have trouble animating both scale and opacity
* from a starting value of 0. This method detects when those values are 0 and alters them slightly
* to known-working starting values
*/
private normalizeInitialValue(property: string, value: string): string {
if (value === undefined) {
return;
}
const coercedReturn: string = "0.01";
switch (property) {
case "transform":
/* eslint-disable */
const matrixValuesRegex: RegExp = /matrix\((.+)\)/;
const matrixValues: string[] | null = value.match(matrixValuesRegex);
/* eslint-enable */
if (Array.isArray(matrixValues)) {
const normalizedValues: string[] = matrixValues[1]
.split(",")
.map((matchedValue: string, index: number) => {
const parsedValueIsZero: boolean = parseFloat(value) === 0;
if (!parsedValueIsZero) {
return matchedValue;
}
// If this is the scaleX index or the scaleY index, return the coerced value
return index === 0 || index === 3
? coercedReturn
: matchedValue;
});
return `matrix(${normalizedValues.join(",")})`;
}
break;
case "opacity":
return parseFloat(value) === 0 ? coercedReturn : value;
}
return value;
}
/**
* Returns the initial values for all properties being animated
*/
private getInitialKeyframeValues(): Keyframe {
if (
!(this.animationTarget instanceof HTMLElement) ||
typeof window === "undefined"
) {
return {} as Keyframe;
}
const animatedProperties: string[] = this.getPropertiesToAnimate();
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(
this.animationTarget
);
const initialKeyframeValues: Partial<Keyframe> = {};
animatedProperties.forEach((property: string) => {
initialKeyframeValues[property] = this.normalizeInitialValue(
property,
computedStyle[property]
);
});
return initialKeyframeValues as Keyframe;
}
/**
* Formats a config option into a transform function
*/
private formatTransformFunction(
functionType: string,
value: string | number | number[]
): string {
// If `functionType` can't be converted into a transform function, just return empty string
if (Animate.propertyMap.transform.indexOf(functionType) === -1) {
return "";
}
switch (functionType) {
case "x":
case "y":
functionType = `translate${functionType.toUpperCase()}`;
value =
typeof value === "number" ? (value = this.pixelify(value)) : value;
break;
case "rotate":
value = `${value.toString()}deg`;
break;
case "scale":
if (value === 0) {
// There is a strange bug where you can't animate from a scale 0
value = 0.01;
}
}
if (typeof value !== "string") {
value = value.toString();
}
return `${functionType}(${value})`;
}
/**
* Converts a number to a pixel string
*/
private pixelify(num: number): string {
return `${num}px`;
}
/**
* Returns keyframe values based on option configuration
*/
private getOptionKeyframeValues(): Keyframe {
const animateProperties: string[] = this.getPropertiesToAnimate();
const keyframeValues: Partial<Keyframe> = {};
animateProperties.forEach((property: string) => {
keyframeValues[property] = Animate.propertyMap[property]
.map((option: string): string => {
const value: string | number = this.options[option];
if (typeof value === "undefined") {
return null;
}
switch (option) {
case "opacity":
return value.toString();
case "top":
case "right":
case "bottom":
case "left":
return typeof value === "number"
? this.pixelify(value)
: value;
default:
return this.formatTransformFunction(option, value);
}
})
.filter((option: string) => Boolean(option))
.join(" ");
});
return keyframeValues as Keyframe;
}
/**
* Gets all keyframes configured by options
*/
private getOptionKeyframes(): Keyframe[] {
const keyframes: Keyframe[] = [
this.getInitialKeyframeValues(),
this.getOptionKeyframeValues(),
];
return this.mode === AnimationMode.animateFrom ? keyframes.reverse() : keyframes;
}
/**
* Sorts an array of offset keys in ascending order
*/
private sortOffsets(offsets: string[]): string[] {
return offsets.sort((a: string, b: string): number => {
const A: number = parseFloat(a);
const B: number = parseFloat(b);
if (A < B) {
return -1;
} else if (A > B) {
return 1;
} else {
return 0;
}
});
}
/**
* Consolidates all keyframe arrays into a single keyframe array
*/
private consolidateKeyframes(keyframeSets: Keyframe[][]): Keyframe[] {
const frames: Partial<Keyframe[]> = [];
// Merge all keyframes into a single frames object where each key is a keyframe offset
keyframeSets.forEach((keyframeSet: Keyframe[]) => {
keyframeSet.forEach((keyframe: Keyframe, index: number) => {
let offset: number | number[] = keyframe.offset;
if (typeof offset === "undefined") {
offset = index === 0 ? 0 : 1;
keyframe.offset = offset;
}
const offsetKey: string = offset.toString();
frames[offsetKey] =
typeof frames[offsetKey] === "undefined"
? keyframe
: Object.assign(frames[offsetKey], keyframe);
});
});
return this.sortOffsets(Object.keys(frames)).map((offset: string) => {
return frames[offset];
});
}
/**
* Returns the animation's keyframes
*/
public get keyframes(): Keyframe[] {
return this.consolidateKeyframes(
this._keyframes.concat([this.getOptionKeyframes()])
);
}
/**
* Returns the key frame effect object
*/
public get keyframeEffect(): KeyframeEffect {
return new KeyframeEffect(
this.animationTarget,
this.keyframes,
this.effectTiming
);
}
}

Просмотреть файл

@ -1,11 +0,0 @@
import Animate, { AnimationMode } from "../animate";
/**
* An animation from provided property values to the element's current values.
* Extends {@link @microsoft/fast-animation#Animate}.
*
* @public
*/
export default class AnimateFrom extends Animate {
protected mode: AnimationMode = AnimationMode.animateFrom;
}

Просмотреть файл

@ -1,117 +0,0 @@
import AnimateTo from "../animateTo";
import AnimateFrom from "../animateFrom";
import { invokeFunctionForEach } from "../utilities/invokeFunctionForEach";
/**
* Groups {@link @microsoft/fast-animation#AnimateTo} and {@link @microsoft/fast-animation#AnimateFrom} instances, providing a single API to operate on all of them.
* @public
*/
class AnimateGroup {
/**
* Stores the onFinish callback
*/
private _onFinish: () => void;
/**
* Stores the group effect object
*/
private animations: Array<AnimateTo | AnimateFrom>;
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
/**
* The onFinish callback.
*/
public get onFinish(): () => void {
return this._onFinish;
}
public set onFinish(callback: () => void) {
this._onFinish = callback;
const longestRunning: AnimateTo | AnimateFrom = this.getLongestAnimation();
if (typeof longestRunning.onFinish === "function") {
const fn: () => void = longestRunning.onFinish;
longestRunning.onFinish = (): void => {
fn();
this._onFinish();
};
} else {
longestRunning.onFinish = this._onFinish;
}
}
/**
* Play the group of animations
*/
public play(): void {
invokeFunctionForEach(this.animations, "play");
}
/**
* Reverses all animations in the group
*/
public reverse(): void {
invokeFunctionForEach(this.animations, "reverse");
}
/**
* Pauses all animations in the group
*/
public pause = (): void => {
invokeFunctionForEach(this.animations, "pause");
};
/**
* Finishes all animations in the group
*/
public finish = (): void => {
invokeFunctionForEach(this.animations, "finish");
};
/**
* Cancels all animations in the group
*/
public cancel = (): void => {
invokeFunctionForEach(this.animations, "cancel");
};
/**
* Returns the longest running animation in the group
*/
private getLongestAnimation(): AnimateTo | AnimateFrom {
return this.animations.reduce(
(
previousValue: AnimateTo | AnimateFrom,
currentValue: AnimateTo | AnimateFrom
) => {
const previousDuration: number = this.getAnimationDuration(
previousValue.effectTiming
);
const currentDuration: number = this.getAnimationDuration(
currentValue.effectTiming
);
// If two durations in the group are equal, consider the higher index the
// longest animation - this helps ensure the group onFinish callback
// is fired after all individual animation onFinish callbacks have fired.
return currentDuration >= previousDuration ? currentValue : previousValue;
}
);
}
/**
* Returns the cumulative time it will take to complete an animation
*/
private getAnimationDuration(effectTiming: EffectTiming): number {
const duration: string | number = effectTiming.duration;
const sanitizedDuration: number =
typeof duration === "string" ? parseFloat(duration) : duration;
return (effectTiming.delay || 0) + (sanitizedDuration || 0);
}
}
export default AnimateGroup;

Просмотреть файл

@ -1,83 +0,0 @@
import AnimateTo from "../animateTo";
import AnimateFrom from "../animateFrom";
import { invokeFunctionForEach } from "../utilities/invokeFunctionForEach";
/**
* Animate a collection of {@link @microsoft/fast-animation#AnimateTo} and {@link @microsoft/fast-animation#AnimateFrom} in sequence.
* @public
*/
class AnimateSequence {
/**
* onFinish callback method
*/
public onFinish: () => void;
private animations: Array<AnimateTo | AnimateFrom>;
constructor(animations: Array<AnimateTo | AnimateFrom>) {
this.animations = animations;
}
/**
* Play the sequence of animations
*/
public play = (): void => {
this.applySequencedCallback(this.animations, "play");
};
/**
* Play the sequence in reverse
*/
public reverse = (): void => {
this.applySequencedCallback(this.animations.reverse(), "reverse");
};
/**
* Pauses all animations in the sequence
*/
public pause = (): void => {
invokeFunctionForEach(this.animations, "pause");
};
/**
* Finishes all animations in the sequence
*/
public finish = (): void => {
invokeFunctionForEach(this.animations, "finish");
};
/**
* Cancels all animations in the sequence
*/
public cancel = (): void => {
invokeFunctionForEach(this.animations, "cancel");
};
/**
* Sequences a set of animations and calls the specified method
*/
private applySequencedCallback(
animations: Array<AnimateTo | AnimateFrom>,
method: string
): void {
const animationCount: number = animations.length;
if (animationCount <= 0) {
return;
}
animations.forEach((animation: AnimateTo | AnimateFrom, index: number) => {
// If this is not the last animation, format animation sequence chain
if (index < animationCount - 1) {
animation.onFinish = this.animations[index + 1][method];
} else {
// Else attach onFinish or nullify any existing onFinish on the animation
animation.onFinish = this.onFinish || void 0;
}
});
animations[0][method]();
}
}
export default AnimateSequence;

Просмотреть файл

@ -1,10 +0,0 @@
import Animate, { AnimationMode } from "../animate";
/**
* An animation to provided property values from the element's current values.
* Extends {@link @microsoft/fast-animation#Animate}.
* @public
*/
export default class AnimateTo extends Animate {
protected mode: AnimationMode = AnimationMode.animateTo;
}

Просмотреть файл

@ -1,58 +0,0 @@
import { BezierCurve } from "./index";
export const linear: BezierCurve = [
[0, 0],
[1, 1],
];
export const easeOut: BezierCurve = [
[0, 0],
[0.58, 1],
];
export const easeOutSmooth: BezierCurve = [
[0, 0.35],
[0.15, 1],
];
export const easeIn: BezierCurve = [
[0.25, 0.1],
[0.25, 1],
];
export const drillIn: BezierCurve = [
[0.17, 0.17],
[0, 1],
];
export const backToApp: BezierCurve = [
[0.5, 0],
[0.6, 1],
];
export const appToApp: BezierCurve = [
[0.5, 0],
[1, 0.9],
];
export const fastIn: BezierCurve = [
[0.1, 0.9],
[0.2, 1],
];
export const fastOut: BezierCurve = [
[0.9, 0.1],
[1, 0.2],
];
export const fastInOut: BezierCurve = [
[0.8, 0],
[0.2, 1],
];
export const exponential: BezierCurve = [
[0.1, 0.25],
[0.75, 0.9],
];
export const fastInFortySevenPercent: BezierCurve = [
[0.11, 0.5],
[0.24, 0.96],
];
export const exponentialReversed: BezierCurve = [
[0.25, 0.1],
[0.9, 0.75],
];
export const navPane: BezierCurve = [
[0.1, 0.7],
[0.1, 1],
];

Просмотреть файл

@ -1,68 +0,0 @@
import * as bezierCurves from "./config";
/**
* Coordinates for a cubic bezier curve.
* @public
*/
export type BezierCurvePoint = [number, number];
/**
* Defines interface for a cubic bezier curve
* @public
*/
export interface BezierCurve {
/**
* Control point 1 (P0)
*/
0: BezierCurvePoint;
/**
* Control point 2 (P1)
*/
1: BezierCurvePoint;
}
/**
* Formats a cubic bezier config into a cubic bezier string
*
* @public
*/
export function formatCubicBezier(points: BezierCurve): string {
if (
!Array.isArray(points) ||
!Array.isArray(points[0]) ||
!Array.isArray(points[1])
) {
return "";
}
const p0: BezierCurvePoint = points[0];
const p1: BezierCurvePoint = points[1];
return `cubic-bezier(${p0[0]}, ${p0[1]}, ${p1[0]}, ${p1[1]})`;
}
/**
* Get a cubic bezier curve, formatted as a string, by name.
* @param name - the name of the bezier curve to use.
*
* @public
*/
export function cubicBezier(
name:
| "linear"
| "easeOut"
| "easeOutSmooth"
| "easeIn"
| "drillIn"
| "backToApp"
| "appToApp"
| "fastIn"
| "fastOut"
| "fastInOut"
| "exponential"
| "fastInFortySevenPercent"
| "exponentialReversed"
| "navPane"
| /* @deprecated */ string
): string {
return name in bezierCurves ? formatCubicBezier(bezierCurves[name]) : "";
}

Просмотреть файл

@ -1,59 +0,0 @@
import AnimateTo from "../animateTo";
/**
* Key frame object for fade-in animations
*/
export const fadeInKeyframes: Array<Partial<Keyframe>> = [
{ opacity: "0.01" }, // Start at 0.01 due to a bug animating from 0
{ opacity: "1" },
];
/**
* Key frame object for fade-out animations
*/
export const fadeOutKeyframes: Array<Partial<Keyframe>> = [
{ opacity: "1" },
{ opacity: "0" },
];
/**
* EffectTiming defaults for fade animations
*/
export const fadeEffectTiming: EffectTiming = {
easing: "linear",
duration: 500,
};
export function applyFade(
element: HTMLElement,
keyframes: Array<Partial<Keyframe>>,
timing: EffectTiming = {}
): AnimateTo {
const fadeAnimationTiming: EffectTiming = Object.assign({}, fadeEffectTiming, timing);
const fadeAnimation: AnimateTo = new AnimateTo(element, null, fadeAnimationTiming);
fadeAnimation.addKeyframes(keyframes);
return fadeAnimation;
}
/**
* Creates an animation to fade an element into view
*
* @public
*/
export function fadeIn(element: HTMLElement, effectTiming: EffectTiming = {}): AnimateTo {
return applyFade(element, fadeInKeyframes, effectTiming);
}
/**
* Creates an animation to fade an element out of view
*
* @public
*/
export function fadeOut(
element: HTMLElement,
effectTiming: EffectTiming = {}
): AnimateTo {
return applyFade(element, fadeOutKeyframes, effectTiming);
}

Просмотреть файл

@ -1,26 +0,0 @@
import AnimateTo from "./animateTo";
import AnimateFrom from "./animateFrom";
import AnimateGroup from "./animateGroup";
import AnimateSequence from "./animateSequence";
import Animate from "./animate";
import { fadeIn, fadeOut } from "./fade";
import { cubicBezier } from "./curves";
import ScrollTrigger from "./triggers/ScrollTrigger";
import ViewEnterTrigger from "./triggers/ViewEnterTrigger";
import ViewExitTrigger from "./triggers/ViewExitTrigger";
export {
Animate,
AnimateFrom,
AnimateGroup,
AnimateSequence,
AnimateTo,
cubicBezier,
fadeIn,
fadeOut,
ScrollTrigger,
ViewEnterTrigger,
ViewExitTrigger,
};

Просмотреть файл

@ -1,108 +0,0 @@
import isElementInView from "../utilities/isElementInView";
import scrollY from "../utilities/scrollY";
/**
* AnimationTrigger callback contract
*/
export type ScrollTriggerCallback = (distance: any) => void;
/**
* Export subscription interface
*/
export interface ScrollTriggerSubscription {
element: HTMLElement;
callback: ScrollTriggerCallback;
inView: boolean;
}
/**
* Scroll trigger base-class that handles event binding and element/callback registration.
*/
export default abstract class ScrollTrigger {
protected subscriptions: ScrollTriggerSubscription[] = [];
protected scrollDistance: number = 0;
private requestedFrame: number | void;
private lastScrollY: number;
constructor() {
this.lastScrollY = scrollY();
// We need to use .bind instead of lambda (fat-arrow) syntax here because
// protected methods declared as lambda functions cannot be invoked by
// extending classes via the `super` object
this.update = this.update.bind(this);
}
/**
* Subscribe an element and callback for scroll triggers
*/
public subscribe(element: HTMLElement, callback: ScrollTriggerCallback): void {
if (
!(element instanceof HTMLElement) ||
typeof callback !== "function" ||
this.isSubscribed(element, callback)
) {
return;
}
if (this.subscriptions.length === 0) {
window.addEventListener("scroll", this.requestFrame);
}
this.subscriptions.push({
element,
callback,
inView: isElementInView(element),
});
}
/**
* Unsubscribe an element and callback for scroll triggers
*/
public unsubscribe(element: HTMLElement, callback: ScrollTriggerCallback): void {
this.subscriptions = this.subscriptions.filter(
(subscription: ScrollTriggerSubscription) => {
return !(
element === subscription.element && callback === subscription.callback
);
}
);
if (this.subscriptions.length === 0) {
window.removeEventListener("scroll", this.requestFrame);
}
}
/**
* Make any arbitrary updates to UI
*/
protected update(): void {
const yOffset: number = scrollY();
this.scrollDistance = yOffset - this.lastScrollY;
this.lastScrollY = yOffset;
}
/**
* Checks to see if element/callback pairs have been registered so we don't duplicate registration.
*/
private isSubscribed(element: HTMLElement, callback: ScrollTriggerCallback): boolean {
return !!this.subscriptions.filter(
(subscription: ScrollTriggerSubscription): boolean => {
return (
element === subscription.element && callback === subscription.callback
);
}
).length;
}
/**
* Request's an animation frame if there are currently no open animation frame requests
*/
private requestFrame = (): void => {
if (this.requestedFrame) {
cancelAnimationFrame(this.requestedFrame);
}
this.requestedFrame = requestAnimationFrame(this.update);
};
}

Просмотреть файл

@ -1,28 +0,0 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase, { ScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be called on scroll while the element is in view.
*
* @public
*/
export default class ScrollTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update(): void {
super.update();
this.subscriptions.forEach((subscription: ScrollTriggerSubscription) => {
const inView: boolean = isElementInView(subscription.element);
if (inView) {
subscription.callback(this.scrollDistance);
}
if (inView !== subscription.inView) {
subscription.inView = inView;
}
});
}
}

Просмотреть файл

@ -1,32 +0,0 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase, { ScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be called when the element enters the view-port
*
* @public
*/
export default class ViewEnterTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update(): void {
super.update();
this.subscriptions.forEach(
/* eslint-disable-next-line */
(subscription: ScrollTriggerSubscription, index: number) => {
const inView: boolean = isElementInView(subscription.element);
// If the element is in view but previously wasn't
if (inView && !subscription.inView) {
subscription.callback(this.scrollDistance);
}
if (inView !== subscription.inView) {
subscription.inView = inView;
}
}
);
}
}

Просмотреть файл

@ -1,32 +0,0 @@
import isElementInView from "../utilities/isElementInView";
import ScrollBase, { ScrollTriggerSubscription } from "./ScrollBase";
/**
* Utility for registering element/callback pairs where the callback will be invoked when the element exits the view-port
*
* @public
*/
export default class ViewExitTrigger extends ScrollBase {
/**
* Check if elements are in view-port and apply scroll method if they are
*/
protected update(): void {
super.update();
this.subscriptions.forEach(
/* eslint-disable-next-line */
(subscription: ScrollTriggerSubscription, index: number) => {
const inView: boolean = isElementInView(subscription.element);
// If the element is out of view but previously was in view
if (!inView && subscription.inView) {
subscription.callback(this.scrollDistance);
}
if (inView !== subscription.inView) {
subscription.inView = inView;
}
}
);
}
}

Просмотреть файл

@ -1,11 +0,0 @@
{
"compilerOptions": {
"lib": [ "es2015", "es2017", "dom", "scripthost" ],
"moduleResolution": "node",
"module": "CommonJS",
"target": "es5",
"baseUrl": ".",
"declaration": true,
"outDir": "../dist"
}
}

Просмотреть файл

@ -1,6 +0,0 @@
/**
* For each item in an array, invoke a function
*/
export function invokeFunctionForEach<T>(arr: T[], name: string): void {
arr.forEach((arrItem: T) => arrItem[name]());
}

Просмотреть файл

@ -1,13 +0,0 @@
/**
* Checks to see if any part of an element is within the viewport
*/
export default function isElementInView(el: HTMLElement): boolean {
const rect: ClientRect = el.getBoundingClientRect();
return (
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= window.innerHeight &&
rect.left <= window.innerWidth
);
}

Просмотреть файл

@ -1,10 +0,0 @@
/**
* Gets the document's scrollY
*/
export default function scrollY(): number {
if (typeof window === "undefined") {
return NaN;
}
return typeof window.scrollY !== "undefined" ? window.scrollY : window.pageYOffset;
}

Просмотреть файл

@ -1,109 +0,0 @@
{
"name": "@microsoft/fast-animation",
"description": "An animation library that simplifies interactions and animations using the Web Animation API.",
"version": "4.2.2",
"author": {
"name": "Microsoft",
"url": "https://discord.gg/FcSNfg4"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/Microsoft/fast.git"
},
"bugs": {
"url": "https://github.com/Microsoft/fast/issues/new/choose"
},
"main": "dist/index.js",
"scripts": {
"build": "tsc -p ./lib/tsconfig.json && yarn doc",
"build:app": "webpack --mode=production",
"coverage": "jest --coverage",
"doc": "api-extractor run --local",
"doc:ci": "api-extractor run",
"prepare": "yarn build",
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.{ts,tsx,html}\"",
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.{ts,tsx,html}\" --list-different",
"start": "node_modules/.bin/webpack-dev-server --progress",
"test": "yarn unit-tests && yarn build:app && yarn doc:ci",
"eslint": "eslint . --ext .ts,.tsx",
"eslint:fix": "eslint . --ext .ts,.tsx --fix",
"unit-tests": "jest --runInBand",
"watch": "yarn build -- -w --preserveWatchOutput"
},
"jest": {
"collectCoverage": true,
"coverageReporters": [
"json",
"text",
[
"lcov",
{
"projectRoot": "../../../"
}
]
],
"coverageThreshold": {
"global": {
"statements": 72,
"branches": 56,
"functions": 67,
"lines": 72
}
},
"rootDir": "./",
"testEnvironment": "jsdom",
"transform": {
".(ts|tsx)": "ts-jest"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"moduleDirectories": [
"node_modules"
]
},
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/preset-env": "^7.12.13",
"@babel/preset-react": "^7.12.13",
"@microsoft/api-extractor": "7.23.1",
"@microsoft/eslint-config-fast-dna": "^2.1.0",
"@types/jest": "^27.0.0",
"@types/node": "^15.0.1",
"@types/react": "^16.3.0",
"@types/react-dom": "^16.3.0",
"@types/react-redux": "^4.4.47",
"babel-loader": "^8.2.2",
"css-loader": "^5.2.6",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.1",
"eslint-loader": "^4.0.0",
"file-loader": "^3.0.1",
"fork-ts-checker-webpack-plugin": "^0.4.0",
"html-webpack-plugin": "^3.2.0",
"jest": "^27.0.0",
"mini-css-extract-plugin": "^1.6.1",
"postcss": "^8.3.5",
"postcss-loader": "^4.2.0",
"prettier": "2.0.2",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-router": "^4.1.1",
"require-dir": "^0.3.2",
"style-loader": "^0.13.2",
"ts-jest": "^27.0.0",
"ts-loader": "^8.0.0",
"typescript": "^4.6.2",
"webpack": "^4.44.0",
"webpack-bundle-analyzer": "^3.5.2",
"webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"@microsoft/fast-web-utilities": "^5.4.1"
}
}

Просмотреть файл

@ -1,7 +0,0 @@
module.exports ={
plugins: [
require('autoprefixer', {
browsers: 'last 2 versions'
})
]
}

Просмотреть файл

@ -1,21 +0,0 @@
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"jsx": "react",
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"removeComments": true,
"sourceMap": true,
"rootDir": "./",
"baseUrl": ".",
"lib": [ "es2015", "es2017", "dom", "scripthost" ],
"paths": {
"[lib]/*": [
"./lib/*"
]
}
}
}

Просмотреть файл

@ -1,81 +0,0 @@
const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const appDir = path.resolve("./client/examples/app");
const outDir = path.resolve("./www");
module.exports = (env, args) => {
const isProduction = args.mode === "production";
return {
devServer: {
compress: false,
historyApiFallback: true,
disableHostCheck: true,
open: true,
overlay: true,
port: 9005,
},
devtool: isProduction ? "none" : "inline-source-map",
entry: appDir + "/app.tsx",
mode: args.mode || "development",
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /\.(test|spec)/,
use: [
{ loader: "babel-loader" },
{ loader: "ts-loader" },
{ loader: "eslint-loader" },
],
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
exclude: /fonts/,
use: [{ loader: `file-loader?name=images/[name].[ext]` }],
},
{
test: /\.css$/,
use: [
{ loader: MiniCSSExtractPlugin.loader },
{ loader: "css-loader" },
{ loader: "postcss-loader" },
],
},
],
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
},
},
},
},
output: {
path: outDir,
publicPath: "/",
filename: isProduction ? "[name]-[contenthash].js" : "[name].js",
},
plugins: [
new HtmlWebpackPlugin({
title: "Animation system examples",
template: path.resolve(appDir, "index.html"),
}),
new MiniCSSExtractPlugin({
filename: "fast-animation-package.css",
}),
new BundleAnalyzerPlugin({
analyzerMode: "disabled",
}),
],
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
};
};

Просмотреть файл

@ -48,6 +48,7 @@
"@types/chai": "^4.2.11",
"@types/karma": "^5.0.0",
"@types/mocha": "^7.0.2",
"@types/react-dom": "^16.9.0",
"@types/webpack-env": "^1.15.2",
"chai": "^4.2.0",
"esm": "^3.2.25",

Просмотреть файл

@ -36,7 +36,6 @@
"@rollup/plugin-node-resolve": "^13.1.3",
"@types/node": "^15.0.1",
"@types/react": "^16.8.0",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.2",
"badge-maker": "^3.2.0",
"chalk": "^4.1.0",
@ -45,7 +44,6 @@
"eslint-plugin-react": "^7.19.0",
"favicons": "^6.2.2",
"fs-extra": "^9.0.1",
"jest": "^25.4.0",
"jss": "^9.8.7",
"jss-preset-default": "^4.5.0",
"lodash-es": "4.17.15",
@ -53,7 +51,6 @@
"raf-throttle": "^2.0.3",
"react": "^16.8.0",
"rimraf": "^3.0.2",
"ts-jest": "^25.4.0",
"typescript": "^4.6.2",
"vscode-web-custom-data": "^0.3.1"
},

Просмотреть файл

@ -38,13 +38,7 @@ function findFiles(startPath, filter, paths = []) {
return paths;
}
const packages = [
"fast-animation",
"fast-colors",
"fast-element",
"fast-foundation",
"fast-components",
];
const packages = ["fast-colors", "fast-element", "fast-foundation", "fast-components"];
function identifyPackage(path) {
for (const pkg of packages) {

2656
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу