diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7eed465ee09049..85dac4ae69d1ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -116,6 +116,8 @@ apps/public-docsite-v9 @microsoft/cxe-red @microsoft/cxe-coastal @microsoft/flue apps/theming-designer @microsoft/fluentui-react apps/ssr-tests-v9 @microsoft/fluentui-react-build apps/stress-test @microsoft/cxe-red @spmonahan @micahgodbolt +apps/react-18-tests-v8 @microsoft/cxe-red @microsoft/cxe-coastal @micahgodbolt +apps/react-18-tests-v9 @microsoft/cxe-red @microsoft/cxe-coastal @micahgodbolt apps/recipes-react-components @microsoft/cxe-red @microsoft/cxe-coastal @microsoft/fluentui-react @sopranopillow #### Packages @@ -138,7 +140,7 @@ packages/react-components/react-conformance-griffel @microsoft/teams-prg packages/react-components/react-context-selector @microsoft/teams-prg packages/react-date-time @microsoft/cxe-red packages/react-docsite-components @microsoft/fluentui-v8-website -packages/react-file-type-icons @jahnp @bigbadcapers +packages/react-file-type-icons @microsoft/cxe-red @jahnp @bigbadcapers packages/react-hooks @microsoft/cxe-red packages/react-icons-mdl2 @microsoft/cxe-red @microsoft/cxe-coastal packages/react-monaco-editor @microsoft/fluentui-v8-website diff --git a/apps/perf-test/package.json b/apps/perf-test/package.json index 8ce2280d4aeb6d..d4531ccc166c12 100644 --- a/apps/perf-test/package.json +++ b/apps/perf-test/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@fluentui/example-data": "^8.4.6", - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@microsoft/load-themed-styles": "^1.10.26", "flamegrill": "0.2.0", "lodash": "^4.17.15", diff --git a/apps/public-docsite-resources/package.json b/apps/public-docsite-resources/package.json index 6665cd943f703b..2a76b8729d3e7c 100644 --- a/apps/public-docsite-resources/package.json +++ b/apps/public-docsite-resources/package.json @@ -34,15 +34,15 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/react-examples": "^8.34.4", "@microsoft/load-themed-styles": "^1.10.26", - "@fluentui/azure-themes": "^8.5.68", - "@fluentui/react-docsite-components": "^8.11.29", + "@fluentui/azure-themes": "^8.5.69", + "@fluentui/react-docsite-components": "^8.11.30", "@fluentui/font-icons-mdl2": "^8.5.12", "@fluentui/set-version": "^8.2.5", - "@fluentui/theme-samples": "^8.7.64", - "@fluentui/react-monaco-editor": "^1.7.64", + "@fluentui/theme-samples": "^8.7.65", + "@fluentui/react-monaco-editor": "^1.7.65", "office-ui-fabric-core": "^11.0.0", "react": "17.0.2", "react-dom": "17.0.2", diff --git a/apps/public-docsite-v9/package.json b/apps/public-docsite-v9/package.json index 0e62d83b73826b..3c43d4fe369489 100644 --- a/apps/public-docsite-v9/package.json +++ b/apps/public-docsite-v9/package.json @@ -24,7 +24,7 @@ "dependencies": { "@fluentui/react-migration-v8-v9": "^9.1.13", "@fluentui/react-migration-v0-v9": "9.0.0-alpha.0", - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/react-northstar": "^0.66.3", "@fluentui/react-icons-northstar": "^0.66.3", "@fluentui/react-components": "^9.18.0", diff --git a/apps/public-docsite-v9/src/Concepts/Migration/KeepingDesignConsistent.stories.mdx b/apps/public-docsite-v9/src/Concepts/Migration/KeepingDesignConsistent.stories.mdx index 1780bffbb179a2..2c25885c696e1b 100644 --- a/apps/public-docsite-v9/src/Concepts/Migration/KeepingDesignConsistent.stories.mdx +++ b/apps/public-docsite-v9/src/Concepts/Migration/KeepingDesignConsistent.stories.mdx @@ -102,7 +102,7 @@ As detailed in the _/Concepts/Developer/Styling Components_ topic, you can creat If you do create custom styles, we strongly recommend using design tokens. This ensures styles update with theme changes. -## You can recompose components with custom styles +## You can recompose components with different styles Fluent v9 has a powerful composition model using React hooks. Each component is separated into a hook that maps props to state, a hook that uses state to set className styles on each slot, and a render function that outputs each slot with applied props. @@ -145,3 +145,43 @@ export const MyButton: ForwardRefComponent = React.forwardRef((prop ``` This is the most advanced form of customization in Fluent UI React v9, but provides you complete control over components. + +## You can set custom style hooks per component type + +**⚠ Avoid custom style hooks** + +Prefer creating custom themes, setting className, replacing slots, extending theme tokens, and component recomposition. +This approach is meant as an escape-hatch when all other customizations are not enough for your use case. + +**⚠ Be careful ** + +- Never change any state properties other than `className`. +- Never dynamically change hooks at runtime. This will lead to unstable hook calls. +- Custom style hooks are called from within the component and receive the component state. +- Be judicious with creating styles and making `mergeClasses` call as you may affect the performance. + +You can pass `FluentProvider.customStyleHooks_unstable` a set of custom style hooks. The custom style hooks are considered unstable and the API may change at any time. +Each custom style hook can update root or slot class names. + +```tsx +const myButtonStyles = makeStyles({ + root: { + //... + }, +}); + +const myCustomStyles: FluentProviderCustomStyleHooks = { + useButtonStyles_unstable: state => { + const myButtonStyles = useMyButtonStyles(); + const buttonState = state as ButtonState; + buttonState.root.className = mergeClasses(buttonState.root.className, myButtonStyles.root); + }, +}; + +; +``` + +To override existing styles, use `makeStyles` to create your custom styles and then call `mergeClasses` with state `className` and the your new styles. +To completely replace existing styles, overwrite the state `className`. + +When `FluentProvider` are nested, custom style hooks will be shallow merged. diff --git a/apps/public-docsite/package.json b/apps/public-docsite/package.json index 2e46d5d8e43892..67c52b363614a0 100644 --- a/apps/public-docsite/package.json +++ b/apps/public-docsite/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@fluentui/common-styles": "^1.2.21", "@fluentui/eslint-plugin": "*", - "@fluentui/react-monaco-editor": "^1.7.64", + "@fluentui/react-monaco-editor": "^1.7.65", "write-file-webpack-plugin": "^4.1.0", "@fluentui/scripts-tasks": "*", "@fluentui/scripts-webpack": "*" @@ -34,17 +34,17 @@ "@fluentui/font-icons-mdl2": "^8.5.12", "@fluentui/public-docsite-resources": "^8.1.41", "@fluentui/public-docsite-setup": "^0.3.17", - "@fluentui/react": "^8.106.5", - "@fluentui/react-docsite-components": "^8.11.29", + "@fluentui/react": "^8.106.6", + "@fluentui/react-docsite-components": "^8.11.30", "@fluentui/react-examples": "^8.34.4", - "@fluentui/react-experiments": "^8.14.59", - "@fluentui/fluent2-theme": "^8.106.0", + "@fluentui/react-experiments": "^8.14.60", + "@fluentui/fluent2-theme": "^8.106.1", "@fluentui/react-file-type-icons": "^8.8.12", "@fluentui/react-icons-mdl2": "^1.3.36", "@fluentui/react-icons-mdl2-branded": "^1.2.37", "@fluentui/set-version": "^8.2.5", "@fluentui/theme": "^2.6.24", - "@fluentui/theme-samples": "^8.7.64", + "@fluentui/theme-samples": "^8.7.65", "@fluentui/utilities": "^8.13.8", "@microsoft/load-themed-styles": "^1.10.26", "office-ui-fabric-core": "^11.0.0", diff --git a/apps/react-18-tests-v8/package.json b/apps/react-18-tests-v8/package.json index 8d2d9bcd3f70ba..abb7afe70c0dd4 100644 --- a/apps/react-18-tests-v8/package.json +++ b/apps/react-18-tests-v8/package.json @@ -21,7 +21,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/react-hooks": "^8.6.19", "@types/react": "18.0.14", "@types/react-dom": "18.0.6", diff --git a/apps/react-18-tests-v8/src/components/Panel.Basic.Example.tsx b/apps/react-18-tests-v8/src/components/Panel.Basic.Example.tsx new file mode 100644 index 00000000000000..6a000361629c32 --- /dev/null +++ b/apps/react-18-tests-v8/src/components/Panel.Basic.Example.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { Panel } from '@fluentui/react/lib/Panel'; +import { useBoolean } from '@fluentui/react-hooks'; + +const handleOpen = () => console.log('onOpen'); +const handleOpened = () => console.log('onOpened'); + +export const PanelBasicExample: React.FunctionComponent = () => { + const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false); + + return ( +
+ + +

Content goes here.

+
+
+ ); +}; diff --git a/apps/ssr-tests/package.json b/apps/ssr-tests/package.json index 59cfd1b98ba808..1218aedd16d4c6 100644 --- a/apps/ssr-tests/package.json +++ b/apps/ssr-tests/package.json @@ -13,7 +13,7 @@ }, "license": "MIT", "devDependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@microsoft/load-themed-styles": "^1.10.26", "@types/mocha": "7.0.2", "@fluentui/public-docsite-resources": "^8.1.41", diff --git a/apps/stress-test/package.json b/apps/stress-test/package.json index 0765cc3d38b128..1280314bce3972 100644 --- a/apps/stress-test/package.json +++ b/apps/stress-test/package.json @@ -10,7 +10,7 @@ "type-check": "tsc -b tsconfig.type.json" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/react-components": "^9.18.0", "@fluentui/react-icons": "^2.0.175", "@fluentui/web-components": "^2.5.12", diff --git a/apps/theming-designer/package.json b/apps/theming-designer/package.json index f8120387943155..fe6ddb03dcc30d 100644 --- a/apps/theming-designer/package.json +++ b/apps/theming-designer/package.json @@ -19,9 +19,9 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/merge-styles": "^8.5.6", - "@fluentui/react-docsite-components": "^8.11.29", + "@fluentui/react-docsite-components": "^8.11.30", "@fluentui/foundation-legacy": "^8.2.32", "@fluentui/scheme-utilities": "^8.3.25", "@fluentui/set-version": "^8.2.5", diff --git a/apps/ts-minbar-test-react/package.json b/apps/ts-minbar-test-react/package.json index 1e5f1ab04a8eda..71135c25d8a3e9 100644 --- a/apps/ts-minbar-test-react/package.json +++ b/apps/ts-minbar-test-react/package.json @@ -5,7 +5,7 @@ "description": "Testing Fluent UI React compatibility with Typescript 3.9", "license": "MIT", "dependencies": { - "@fluentui/react": "^8.106.5" + "@fluentui/react": "^8.106.6" }, "scripts": { "type-check": "tsc -p .", diff --git a/apps/vr-tests/package.json b/apps/vr-tests/package.json index 88e87bb0d8557e..3e979d140cf952 100644 --- a/apps/vr-tests/package.json +++ b/apps/vr-tests/package.json @@ -22,8 +22,8 @@ "dependencies": { "@fluentui/example-data": "^8.4.6", "@fluentui/font-icons-mdl2": "^8.5.12", - "@fluentui/react": "^8.106.5", - "@fluentui/react-experiments": "^8.14.59", + "@fluentui/react": "^8.106.6", + "@fluentui/react-experiments": "^8.14.60", "@fluentui/react-hooks": "^8.6.19", "@fluentui/react-icons-mdl2": "^1.3.36", "@fluentui/storybook": "^1.0.0", diff --git a/change/@fluentui-react-card-0a817d2d-d46b-4895-b3bb-29566bf747fa.json b/change/@fluentui-react-card-0a817d2d-d46b-4895-b3bb-29566bf747fa.json new file mode 100644 index 00000000000000..ca93d656472dcf --- /dev/null +++ b/change/@fluentui-react-card-0a817d2d-d46b-4895-b3bb-29566bf747fa.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "docs: improve readme description", + "packageName": "@fluentui/react-card", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-image-8d658a2f-4b6b-49a2-9dbc-3d36b80f40b9.json b/change/@fluentui-react-image-8d658a2f-4b6b-49a2-9dbc-3d36b80f40b9.json new file mode 100644 index 00000000000000..16b55ab06ee9b2 --- /dev/null +++ b/change/@fluentui-react-image-8d658a2f-4b6b-49a2-9dbc-3d36b80f40b9.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: Adding calls to custom style hooks derived from context.", + "packageName": "@fluentui/react-image", + "email": "gcox@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-portal-d5ac98ba-22cc-41a1-939d-77041a362442.json b/change/@fluentui-react-portal-d5ac98ba-22cc-41a1-939d-77041a362442.json new file mode 100644 index 00000000000000..f5bf6f7f6a46e0 --- /dev/null +++ b/change/@fluentui-react-portal-d5ac98ba-22cc-41a1-939d-77041a362442.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: accept a className in mountNode in Portal", + "packageName": "@fluentui/react-portal", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-progress-f5145f44-c47b-4c22-a051-e884f22b88ce.json b/change/@fluentui-react-progress-f5145f44-c47b-4c22-a051-e884f22b88ce.json new file mode 100644 index 00000000000000..0b5d974af43105 --- /dev/null +++ b/change/@fluentui-react-progress-f5145f44-c47b-4c22-a051-e884f22b88ce.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: Smooth out animation of indeterminate progress bar", + "packageName": "@fluentui/react-progress", + "email": "behowell@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tabster-c7f8c111-451a-4f17-bb36-8e8aa2fe1e8a.json b/change/@fluentui-react-tabster-c7f8c111-451a-4f17-bb36-8e8aa2fe1e8a.json new file mode 100644 index 00000000000000..4e23aed0925b57 --- /dev/null +++ b/change/@fluentui-react-tabster-c7f8c111-451a-4f17-bb36-8e8aa2fe1e8a.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Add JSDoc for tabster focus indicator selector", + "packageName": "@fluentui/react-tabster", + "email": "tigeroakes@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-text-4ec8a8d0-658f-4318-9259-eb36057dbf99.json b/change/@fluentui-react-text-4ec8a8d0-658f-4318-9259-eb36057dbf99.json new file mode 100644 index 00000000000000..8fa5f8d88243a0 --- /dev/null +++ b/change/@fluentui-react-text-4ec8a8d0-658f-4318-9259-eb36057dbf99.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: Adding calls to custom style hooks derived from context.", + "packageName": "@fluentui/react-text", + "email": "gcox@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/azure-themes/CHANGELOG.json b/packages/azure-themes/CHANGELOG.json index dfee429b77d10e..40fdd6514e2e7f 100644 --- a/packages/azure-themes/CHANGELOG.json +++ b/packages/azure-themes/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/azure-themes", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/azure-themes_v8.5.69", + "version": "8.5.69", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/azure-themes", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/azure-themes_v8.5.68", diff --git a/packages/azure-themes/CHANGELOG.md b/packages/azure-themes/CHANGELOG.md index e16f40aafef329..50bbb2041edd03 100644 --- a/packages/azure-themes/CHANGELOG.md +++ b/packages/azure-themes/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/azure-themes -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.5.69](https://github.com/microsoft/fluentui/tree/@fluentui/azure-themes_v8.5.69) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/azure-themes_v8.5.68..@fluentui/azure-themes_v8.5.69) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.5.68](https://github.com/microsoft/fluentui/tree/@fluentui/azure-themes_v8.5.68) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/azure-themes/package.json b/packages/azure-themes/package.json index 1b18c8276a772a..7361a6bff1e4e3 100644 --- a/packages/azure-themes/package.json +++ b/packages/azure-themes/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/azure-themes", - "version": "8.5.68", + "version": "8.5.69", "description": "Azure themes for Fluent UI React", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -28,7 +28,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/set-version": "^8.2.5", "tslib": "^2.1.0" } diff --git a/packages/cra-template/CHANGELOG.json b/packages/cra-template/CHANGELOG.json index 3b7ed9ac3ae239..3de5e26824e831 100644 --- a/packages/cra-template/CHANGELOG.json +++ b/packages/cra-template/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/cra-template", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/cra-template_v8.4.66", + "version": "8.4.66", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/cra-template", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/cra-template_v8.4.65", diff --git a/packages/cra-template/CHANGELOG.md b/packages/cra-template/CHANGELOG.md index ef5f5655e8126e..ac26e4587c2045 100644 --- a/packages/cra-template/CHANGELOG.md +++ b/packages/cra-template/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/cra-template -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.4.66](https://github.com/microsoft/fluentui/tree/@fluentui/cra-template_v8.4.66) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/cra-template_v8.4.65..@fluentui/cra-template_v8.4.66) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.4.65](https://github.com/microsoft/fluentui/tree/@fluentui/cra-template_v8.4.65) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/cra-template/package.json b/packages/cra-template/package.json index e5d88de90854bd..56ec608e664538 100644 --- a/packages/cra-template/package.json +++ b/packages/cra-template/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/cra-template", - "version": "8.4.65", + "version": "8.4.66", "description": "Create React App template for Fluent UI React (@fluentui/react)", "repository": { "type": "git", @@ -18,7 +18,7 @@ "template.json" ], "devDependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/scripts-projects-test": "*", "@fluentui/scripts-monorepo": "*" } diff --git a/packages/fluent2-theme/CHANGELOG.json b/packages/fluent2-theme/CHANGELOG.json index b82448a91eec99..95e7de85133a23 100644 --- a/packages/fluent2-theme/CHANGELOG.json +++ b/packages/fluent2-theme/CHANGELOG.json @@ -1,6 +1,33 @@ { "name": "@fluentui/fluent2-theme", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:48 GMT", + "tag": "@fluentui/fluent2-theme_v8.106.1", + "version": "8.106.1", + "comments": { + "patch": [ + { + "author": "matejera@microsoft.com", + "package": "@fluentui/fluent2-theme", + "commit": "41d1b1dc1c750b397a78a4f660f1b2b0292fbdb4", + "comment": "Updating spin button focused and disabled styles." + }, + { + "author": "matejera@microsoft.com", + "package": "@fluentui/fluent2-theme", + "commit": "349c420b582843a2be7f0553afcb497570955590", + "comment": "Fixing bugs in the spinner size. All 4 sizes were hard coded to 32px. Now Medium and large are 32px & 36px respectivley.xSmall back to 12px and small to 16px." + }, + { + "author": "beachball", + "package": "@fluentui/fluent2-theme", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:24 GMT", "tag": "@fluentui/fluent2-theme_v8.106.0", diff --git a/packages/fluent2-theme/CHANGELOG.md b/packages/fluent2-theme/CHANGELOG.md index 31ab1b5c9b66d4..77422381b93930 100644 --- a/packages/fluent2-theme/CHANGELOG.md +++ b/packages/fluent2-theme/CHANGELOG.md @@ -1,9 +1,20 @@ # Change Log - @fluentui/fluent2-theme -This log was last generated on Fri, 10 Mar 2023 07:38:24 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:48 GMT and should not be manually modified. +## [8.106.1](https://github.com/microsoft/fluentui/tree/@fluentui/fluent2-theme_v8.106.1) + +Tue, 14 Mar 2023 07:38:48 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/fluent2-theme_v8.106.0..@fluentui/fluent2-theme_v8.106.1) + +### Patches + +- Updating spin button focused and disabled styles. ([PR #27188](https://github.com/microsoft/fluentui/pull/27188) by matejera@microsoft.com) +- Fixing bugs in the spinner size. All 4 sizes were hard coded to 32px. Now Medium and large are 32px & 36px respectivley.xSmall back to 12px and small to 16px. ([PR #27182](https://github.com/microsoft/fluentui/pull/27182) by matejera@microsoft.com) +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.106.0](https://github.com/microsoft/fluentui/tree/@fluentui/fluent2-theme_v8.106.0) Fri, 10 Mar 2023 07:38:24 GMT diff --git a/packages/fluent2-theme/package.json b/packages/fluent2-theme/package.json index 333e141e4d2223..c84fb2e1dba310 100644 --- a/packages/fluent2-theme/package.json +++ b/packages/fluent2-theme/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/fluent2-theme", - "version": "8.106.0", + "version": "8.106.1", "description": "A Fluent2 theme for Fluent UI React 8.x", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -28,7 +28,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/set-version": "^8.2.5", "tslib": "^2.1.0" } diff --git a/packages/fluent2-theme/src/componentStyles/SearchBox.styles.ts b/packages/fluent2-theme/src/componentStyles/SearchBox.styles.ts index 7d9a3d11dbaf75..2492f412dfd0a9 100644 --- a/packages/fluent2-theme/src/componentStyles/SearchBox.styles.ts +++ b/packages/fluent2-theme/src/componentStyles/SearchBox.styles.ts @@ -5,7 +5,7 @@ export function getSearchBoxStyles( props: ISearchBoxStyleProps, ): IStyleFunctionOrObject { const { hasFocus, underlined, disabled, theme } = props; - const { palette } = theme; + const { palette, semanticColors } = theme; const restBottomBorder = `1px solid ${palette.neutralPrimary}`; @@ -25,12 +25,16 @@ export function getSearchBoxStyles( getFluent2InputFocusStyles(theme), ], - disabled && ['is-disabled', getFluent2InputDisabledStyles(theme)], + disabled && [ + 'is-disabled', + getFluent2InputDisabledStyles(theme), + { borderBottom: `1px solid ${semanticColors.disabledBorder}` }, + ], ], field: [ disabled && { '::placeholder': { - color: palette.neutralQuaternaryAlt, + color: semanticColors.disabledText, }, }, ], @@ -41,7 +45,7 @@ export function getSearchBoxStyles( disabled && [ 'is-disabled', { - color: palette.neutralQuaternaryAlt, + color: semanticColors.disabledText, }, ], ], diff --git a/packages/fluent2-theme/src/componentStyles/SpinButton.styles.ts b/packages/fluent2-theme/src/componentStyles/SpinButton.styles.ts index beb8a1ad5fb531..42928f6bfd56d4 100644 --- a/packages/fluent2-theme/src/componentStyles/SpinButton.styles.ts +++ b/packages/fluent2-theme/src/componentStyles/SpinButton.styles.ts @@ -1,20 +1,32 @@ import type { ISpinButtonStyleProps, ISpinButtonStyles, IStyleFunctionOrObject } from '@fluentui/react'; -import { FontWeights, getInputFocusStyle } from '@fluentui/react'; +import { FontWeights } from '@fluentui/react'; export function getSpinButtonStyles( props: ISpinButtonStyleProps, ): IStyleFunctionOrObject { const { theme, isFocused, disabled } = props; const { semanticColors } = theme; - const SpinButtonRootBorderColor = semanticColors.inputBorder; - const SpinButtonRootBorderColorHovered = semanticColors.inputBorderHovered; - const SpinButtonRootBorderColorFocused = semanticColors.inputFocusBorderAlt; + const SpinButtonRootBorderColorFocused = semanticColors.disabledBorder; // sorry for the broken semantics + let SpinButtonRootBorderColor = semanticColors.inputBorder; + let SpinButtonBorderBottomColor = isFocused ? theme.palette.themePrimary : theme.palette.neutralPrimary; + + if (disabled) { + SpinButtonBorderBottomColor = semanticColors.disabledBorder; + SpinButtonRootBorderColor = semanticColors.disabledBorder; + } const styles: Partial = { label: { fontWeight: FontWeights.regular, }, + input: { + backgroundColor: 'unset', + }, spinButtonWrapper: [ + { + borderBottomColor: SpinButtonBorderBottomColor, + backgroundColor: 'unset', + }, { // setting border using pseudo-element here in order to prevent: // input and chevron buttons to overlap border under certain resolutions @@ -29,19 +41,31 @@ export function getSpinButtonStyles( borderWidth: '1px', borderStyle: 'solid', borderColor: SpinButtonRootBorderColor, - borderBottomColor: theme.palette.neutralPrimary, + borderBottomColor: SpinButtonBorderBottomColor, borderRadius: theme.effects.roundedCorner4, }, }, !disabled && [ { - ':hover :': { ':after': { borderColor: SpinButtonRootBorderColorHovered } }, - }, - isFocused && { - selectors: { - '&&': getInputFocusStyle(SpinButtonRootBorderColorFocused, theme.effects.roundedCorner4), + ':hover :': { + ':after': { + borderStyle: 'solid', + borderColor: SpinButtonRootBorderColorFocused, + borderBottom: theme.palette.themePrimary, + borderWidth: '1px', + }, }, }, + isFocused && [ + { + ':hover:after, :after': { + borderStyle: 'solid', + borderColor: SpinButtonRootBorderColorFocused, + borderBottomColor: SpinButtonBorderBottomColor, + borderWidth: '1px', + }, + }, + ], ], ], }; diff --git a/packages/fluent2-theme/src/componentStyles/Spinner.styles.ts b/packages/fluent2-theme/src/componentStyles/Spinner.styles.ts index 50bcc422364e98..fdde25a8c11a44 100644 --- a/packages/fluent2-theme/src/componentStyles/Spinner.styles.ts +++ b/packages/fluent2-theme/src/componentStyles/Spinner.styles.ts @@ -1,18 +1,27 @@ -import type { ISpinnerStyleProps, ISpinnerStyles, IStyleFunctionOrObject } from '@fluentui/react'; +import { ISpinnerStyleProps, ISpinnerStyles, IStyleFunctionOrObject, SpinnerSize } from '@fluentui/react'; import { FontWeights } from '@fluentui/react'; export function getSpinnerStyles( props: ISpinnerStyleProps, ): IStyleFunctionOrObject { - const { theme } = props; + const { theme, size } = props; const styles: Partial = { label: [theme.fonts.mediumPlus, { color: theme.semanticColors.bodyText, fontWeight: FontWeights.semibold }], - circle: { - borderWidth: 3, - height: 32, - width: 32, - }, + circle: [ + { + borderWidth: 3, + }, + size === SpinnerSize.large && { + height: 36, + width: 36, + }, + size === SpinnerSize.medium && { + // default + height: 32, + width: 32, + }, + ], }; return styles; diff --git a/packages/fluent2-theme/src/componentStyles/TextField.styles.ts b/packages/fluent2-theme/src/componentStyles/TextField.styles.ts index 478b8c8394376f..b7ea18a12db11a 100644 --- a/packages/fluent2-theme/src/componentStyles/TextField.styles.ts +++ b/packages/fluent2-theme/src/componentStyles/TextField.styles.ts @@ -51,7 +51,7 @@ export function getTextFieldStyles( }, focused && getFluent2InputFocusStyles(theme, underlined, hasErrorMessage), disabled && getFluent2InputDisabledStyles(theme), - disabled && { borderBottom: `1px solid ${palette.neutralQuaternaryAlt}` }, + disabled && { borderBottom: `1px solid ${semanticColors.disabledText}` }, ], }; diff --git a/packages/fluent2-theme/src/componentStyles/inputStyleHelpers.utils.ts b/packages/fluent2-theme/src/componentStyles/inputStyleHelpers.utils.ts index f7275a241a59f7..32f1c333dd139e 100644 --- a/packages/fluent2-theme/src/componentStyles/inputStyleHelpers.utils.ts +++ b/packages/fluent2-theme/src/componentStyles/inputStyleHelpers.utils.ts @@ -24,10 +24,11 @@ export const getFluent2InputFocusStyles = ( }; export const getFluent2InputDisabledStyles = (theme: ITheme): IRawStyle => { + const { semanticColors } = theme; return { borderRadius: theme?.effects.roundedCorner4, - border: `1px solid ${theme.palette.neutralQuaternaryAlt}`, - color: theme.palette.neutralQuaternaryAlt, + border: `1px solid ${semanticColors.disabledBorder}`, + color: semanticColors.disabledText, backgroundColor: 'unset', }; }; diff --git a/packages/react-cards/CHANGELOG.json b/packages/react-cards/CHANGELOG.json index e2cf0b5c9cad3b..7e498d3d276777 100644 --- a/packages/react-cards/CHANGELOG.json +++ b/packages/react-cards/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react-cards", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-cards_v0.205.65", + "version": "0.205.65", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-cards", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-cards_v0.205.64", diff --git a/packages/react-cards/CHANGELOG.md b/packages/react-cards/CHANGELOG.md index 6da0b048403c05..bd92b047b2f713 100644 --- a/packages/react-cards/CHANGELOG.md +++ b/packages/react-cards/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react-cards -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [0.205.65](https://github.com/microsoft/fluentui/tree/@fluentui/react-cards_v0.205.65) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-cards_v0.205.64..@fluentui/react-cards_v0.205.65) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [0.205.64](https://github.com/microsoft/fluentui/tree/@fluentui/react-cards_v0.205.64) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-cards/package.json b/packages/react-cards/package.json index 7e477b865f3cf9..bcfa1cb58630b2 100644 --- a/packages/react-cards/package.json +++ b/packages/react-cards/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-cards", - "version": "0.205.64", + "version": "0.205.65", "description": "Deprecated experimental Card container components for Fluent UI React.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -34,7 +34,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/foundation-legacy": "^8.2.32", "@fluentui/set-version": "^8.2.5", "@microsoft/load-themed-styles": "^1.10.26", diff --git a/packages/react-charting/CHANGELOG.json b/packages/react-charting/CHANGELOG.json index 6f48596e8e5f75..83d53f0dfba298 100644 --- a/packages/react-charting/CHANGELOG.json +++ b/packages/react-charting/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react-charting", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-charting_v5.16.5", + "version": "5.16.5", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-charting", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-charting_v5.16.4", diff --git a/packages/react-charting/CHANGELOG.md b/packages/react-charting/CHANGELOG.md index 082befd0996cba..bc27c9037c3d39 100644 --- a/packages/react-charting/CHANGELOG.md +++ b/packages/react-charting/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react-charting -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [5.16.5](https://github.com/microsoft/fluentui/tree/@fluentui/react-charting_v5.16.5) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-charting_v5.16.4..@fluentui/react-charting_v5.16.5) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [5.16.4](https://github.com/microsoft/fluentui/tree/@fluentui/react-charting_v5.16.4) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-charting/package.json b/packages/react-charting/package.json index 6946327453ad8f..180ecc72518e8f 100644 --- a/packages/react-charting/package.json +++ b/packages/react-charting/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-charting", - "version": "5.16.4", + "version": "5.16.5", "description": "Experimental React charting components for building experiences for Microsoft 365.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -28,7 +28,7 @@ }, "devDependencies": { "@fluentui/eslint-plugin": "*", - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@types/react-addons-test-utils": "0.14.18", "@fluentui/jest-serializer-merge-styles": "^8.0.23", "@fluentui/scripts-jest": "*", @@ -62,7 +62,7 @@ "tslib": "^2.1.0" }, "peerDependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@types/react": ">=16.8.0 <19.0.0", "@types/react-dom": ">=16.8.0 <19.0.0", "react": ">=16.8.0 <19.0.0", diff --git a/packages/react-components/react-card/README.md b/packages/react-components/react-card/README.md index c697abd4dc2002..cddf8d9466625c 100644 --- a/packages/react-components/react-card/README.md +++ b/packages/react-components/react-card/README.md @@ -1,10 +1,8 @@ -# @fluentui/react-card [ALPHA] +# @fluentui/react-card **React Card components for [Fluent UI React](https://react.fluentui.dev)** -**⚠️ Please note that functionality is still being added to this package. Due to lockstep versioning, the version of this package is aligned with the others in _react-components_.** - -These are not production-ready components and **should never be used in a product**. This space is useful for testing new components whose APIs might change before final release. +A card is a container that holds information and actions related to a single concept or object, like a document or a contact. ## Usage diff --git a/packages/react-components/react-card/stories/Card/index.stories.tsx b/packages/react-components/react-card/stories/Card/index.stories.tsx index 2ccc5e28c5d138..8efb93eb9a05b0 100644 --- a/packages/react-components/react-card/stories/Card/index.stories.tsx +++ b/packages/react-components/react-card/stories/Card/index.stories.tsx @@ -12,7 +12,7 @@ export { FocusMode } from './CardFocusMode.stories'; export { Templates } from './CardTemplates.stories'; export default { - title: 'Preview Components/Card/Card', + title: 'Components/Card/Card', component: Card, parameters: { docs: { diff --git a/packages/react-components/react-card/stories/CardFooter/index.stories.tsx b/packages/react-components/react-card/stories/CardFooter/index.stories.tsx index 7eacd7def16570..39ef32621d165f 100644 --- a/packages/react-components/react-card/stories/CardFooter/index.stories.tsx +++ b/packages/react-components/react-card/stories/CardFooter/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './CardFooterDescription.md'; export { Default } from './CardFooterDefault.stories'; export default { - title: 'Preview Components/Card/CardFooter', + title: 'Components/Card/CardFooter', component: CardFooter, parameters: { docs: { diff --git a/packages/react-components/react-card/stories/CardHeader/index.stories.tsx b/packages/react-components/react-card/stories/CardHeader/index.stories.tsx index 887292929fd633..d6269ffd81cf8c 100644 --- a/packages/react-components/react-card/stories/CardHeader/index.stories.tsx +++ b/packages/react-components/react-card/stories/CardHeader/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './CardHeaderDescription.md'; export { Default } from './CardHeaderDefault.stories'; export default { - title: 'Preview Components/Card/CardHeader', + title: 'Components/Card/CardHeader', component: CardHeader, parameters: { docs: { diff --git a/packages/react-components/react-card/stories/CardPreview/index.stories.tsx b/packages/react-components/react-card/stories/CardPreview/index.stories.tsx index 5a1f03a54e2612..b38e5386674af4 100644 --- a/packages/react-components/react-card/stories/CardPreview/index.stories.tsx +++ b/packages/react-components/react-card/stories/CardPreview/index.stories.tsx @@ -4,7 +4,7 @@ import descriptionMd from './CardPreviewDescription.md'; export { Default } from './CardPreviewDefault.stories'; export default { - title: 'Preview Components/Card/CardPreview', + title: 'Components/Card/CardPreview', component: CardPreview, parameters: { docs: { diff --git a/packages/react-components/react-datepicker-compat/src/components/DatePicker/renderDatePicker.tsx b/packages/react-components/react-datepicker-compat/src/components/DatePicker/renderDatePicker.tsx index d69fe87e6e256a..6fb4c6bb409eac 100644 --- a/packages/react-components/react-datepicker-compat/src/components/DatePicker/renderDatePicker.tsx +++ b/packages/react-components/react-datepicker-compat/src/components/DatePicker/renderDatePicker.tsx @@ -17,8 +17,11 @@ export const renderDatePicker_unstable = (state: DatePickerState) => { {popoverTriggerChildProps => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const rootProps: any = { root: popoverTriggerChildProps }; + // onKeyDown is not needed as DatePicker handles closing the popover with ESC internally. onKeyDown also + // causes issues when typing in the input, not letting the user type SPACE and bugs with BACKSPACE. + const { onKeyDown, ...inputTriggerProps } = popoverTriggerChildProps; + const rootProps = { root: { ...inputTriggerProps } }; + return ( diff --git a/packages/react-components/react-datepicker-compat/src/components/DatePicker/useDatePicker.tsx b/packages/react-components/react-datepicker-compat/src/components/DatePicker/useDatePicker.tsx index a0a7303dbad077..fb8b1b2c52f76a 100644 --- a/packages/react-components/react-datepicker-compat/src/components/DatePicker/useDatePicker.tsx +++ b/packages/react-components/react-datepicker-compat/src/components/DatePicker/useDatePicker.tsx @@ -229,7 +229,7 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref { + const onInputFocus = React.useCallback((): void => { if (disableAutoFocus) { return; } @@ -335,24 +335,11 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref { - // let shouldFocus = true; - // // If the user has specified that the callout shouldn't use initial focus, then respect - // // that and don't attempt to set focus. That will default to true within the callout - // // so we need to check if it's undefined here. - // if (props.calloutProps && props.calloutProps.setInitialFocus !== undefined) { - // shouldFocus = props.calloutProps.setInitialFocus; - // } - // if (calendar.current && shouldFocus) { - // calendar.current.focus(); - // } - // }, [props.calloutProps]); - - const onTextFieldBlur = React.useCallback((): void => { + const onInputBlur = React.useCallback((): void => { validateTextInput(); }, [validateTextInput]); - const onTextFieldChanged = React.useCallback( + const onInputChange = React.useCallback( (ev: React.FormEvent, data: InputOnChangeData): void => { const { value: newValue } = data; @@ -361,15 +348,13 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref): void => { switch (ev.key) { case Enter: @@ -411,7 +396,7 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref { + const onInputClick = React.useCallback((): void => { // default openOnClick to !props.disableAutoFocus for legacy support of disableAutoFocus behavior const openOnClick = props.openOnClick || !props.disableAutoFocus; if (openOnClick && !isCalendarShown && !props.disabled) { @@ -440,42 +425,7 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref JSX.Element | null, - // ) => JSX.Element | null, - // ) => { - // return ( - // <> - // {inputProps?.description ? defaultRender?.(inputProps) : null} - //
- // {statusMessage} - //
- // - // ); - // }; - - // const renderReadOnlyInput: ITextFieldProps['onRenderInput'] = inputProps => { - // const divProps = getNativeProps(inputProps!, divProperties); - - // // Talkback on Android treats readonly inputs as disabled, so swipe gestures to open the Calendar - // // don't register. Workaround is rendering a div with role="combobox" (passed in via TextField props). - // return ( - //
- // {formattedDate || ( - // // Putting the placeholder in a separate span fixes specificity issues for the text color - // {placeholder} - // )} - //
- // ); - // }; - - // const nativeProps = getNativeProps>(props, divProperties, ['value']); - // // const iconProps = textFieldProps && textFieldProps.iconProps; - const textFieldId = - textFieldProps && textFieldProps.id && textFieldProps.id !== id ? textFieldProps.id : id + '-label'; + const inputId = inputProps && inputProps.id && inputProps.id !== id ? inputProps.id : id + '-label'; const inputAppearance: InputProps['appearance'] = underlined ? 'underline' @@ -507,15 +457,21 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref} />, disabled, - id: textFieldId, + id: inputId, placeholder, readOnly: !allowTextInput, required: isRequired, role: 'combobox', tabIndex, - ...textFieldProps, + ...inputProps, }, }); + inputShorthand.onBlur = onInputBlur; + inputShorthand.onClick = onInputClick; + inputShorthand.onFocus = onInputFocus; + inputShorthand.onKeyDown = onInputKeyDown; + inputShorthand.onChange = mergeCallbacks(onInputChange, props.input?.onChange); + inputShorthand.value = formattedDate; const inputFieldShorthand = resolveShorthand(props.inputField, { defaultProps: { @@ -526,12 +482,6 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref = React.forwardRef((props, ref) => { const state = useImage_unstable(props, ref); + useImageStyles_unstable(state); + const { useImageStyles_unstable: useCustomStyles } = useCustomStyleHooks_unstable(); + useCustomStyles(state); + return renderImage_unstable(state); }); diff --git a/packages/react-components/react-migration-v8-v9/package.json b/packages/react-components/react-migration-v8-v9/package.json index 26022f8950d43a..8372aa57f2561e 100644 --- a/packages/react-components/react-migration-v8-v9/package.json +++ b/packages/react-components/react-migration-v8-v9/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@ctrl/tinycolor": "3.3.4", - "@fluentui/fluent2-theme": "^8.106.0", - "@fluentui/react": "^8.106.5", + "@fluentui/fluent2-theme": "^8.106.1", + "@fluentui/react": "^8.106.6", "@fluentui/react-components": "^9.18.0", "@fluentui/react-icons": "^2.0.175", "@fluentui/react-theme": "^9.1.5", diff --git a/packages/react-components/react-portal/etc/react-portal.api.md b/packages/react-components/react-portal/etc/react-portal.api.md index b5f176c23dcfb4..1cfe2be65b02c6 100644 --- a/packages/react-components/react-portal/etc/react-portal.api.md +++ b/packages/react-components/react-portal/etc/react-portal.api.md @@ -15,11 +15,15 @@ export const Portal: React_2.FC; // @public (undocumented) export type PortalProps = { children?: React_2.ReactNode; - mountNode?: HTMLElement | null; + mountNode?: HTMLElement | null | { + element?: HTMLElement | null; + className?: string; + }; }; // @public (undocumented) -export type PortalState = Pick & Required> & { +export type PortalState = Pick & { + mountNode: HTMLElement | null | undefined; virtualParentRootRef: React_2.MutableRefObject; }; diff --git a/packages/react-components/react-portal/src/components/Portal/Portal.types.ts b/packages/react-components/react-portal/src/components/Portal/Portal.types.ts index 590d42aafe59bd..32cc3590138de0 100644 --- a/packages/react-components/react-portal/src/components/Portal/Portal.types.ts +++ b/packages/react-components/react-portal/src/components/Portal/Portal.types.ts @@ -11,13 +11,14 @@ export type PortalProps = { * * @default a new element on document.body without any styling */ - mountNode?: HTMLElement | null; + mountNode?: HTMLElement | null | { element?: HTMLElement | null; className?: string }; }; -export type PortalState = Pick & - Required> & { - /** - * Ref to the root span element as virtual parent - */ - virtualParentRootRef: React.MutableRefObject; - }; +export type PortalState = Pick & { + mountNode: HTMLElement | null | undefined; + + /** + * Ref to the root span element as virtual parent + */ + virtualParentRootRef: React.MutableRefObject; +}; diff --git a/packages/react-components/react-portal/src/components/Portal/usePortal.test.ts b/packages/react-components/react-portal/src/components/Portal/usePortal.test.ts new file mode 100644 index 00000000000000..37e408db53a4ae --- /dev/null +++ b/packages/react-components/react-portal/src/components/Portal/usePortal.test.ts @@ -0,0 +1,33 @@ +import { toMountNodeProps } from './usePortal'; + +describe('toMountNodeProps', () => { + it('handles HTMLElement', () => { + const element = document.createElement('div'); + + expect(toMountNodeProps(element)).toMatchObject({ + element, + }); + }); + + it('handles "null"', () => { + expect(toMountNodeProps(null)).toMatchObject({ + element: null, + }); + }); + + it('handles "undefined"', () => { + expect(toMountNodeProps(null)).toMatchObject({}); + }); + + it('handles objects', () => { + const element = document.createElement('div'); + + expect(toMountNodeProps({ element })).toMatchObject({ + element, + }); + expect(toMountNodeProps({ element, className: 'foo' })).toMatchObject({ + element, + className: 'foo', + }); + }); +}); diff --git a/packages/react-components/react-portal/src/components/Portal/usePortal.ts b/packages/react-components/react-portal/src/components/Portal/usePortal.ts index cb1c0943918881..51eaaf54cabab8 100644 --- a/packages/react-components/react-portal/src/components/Portal/usePortal.ts +++ b/packages/react-components/react-portal/src/components/Portal/usePortal.ts @@ -1,8 +1,29 @@ +import { isHTMLElement } from '@fluentui/react-utilities'; import * as React from 'react'; -import { usePortalMountNode } from './usePortalMountNode'; + import { setVirtualParent } from '../../virtualParent/index'; +import { usePortalMountNode } from './usePortalMountNode'; import type { PortalProps, PortalState } from './Portal.types'; +export function toMountNodeProps(mountNode: PortalProps['mountNode']): { + element?: HTMLElement | null; + className?: string; +} { + if (isHTMLElement(mountNode)) { + return { element: mountNode }; + } + + if (typeof mountNode === 'object') { + if (mountNode === null) { + return { element: null }; + } + + return mountNode; + } + + return {}; +} + /** * Create the state required to render Portal. * @@ -11,14 +32,14 @@ import type { PortalProps, PortalState } from './Portal.types'; * @param props - props from this instance of Portal */ export const usePortal_unstable = (props: PortalProps): PortalState => { - const { children, mountNode } = props; + const { element, className } = toMountNodeProps(props.mountNode); const virtualParentRootRef = React.useRef(null); - const fallbackMountNode = usePortalMountNode({ disabled: !!mountNode }); + const fallbackElement = usePortalMountNode({ disabled: !!element, className }); const state: PortalState = { - children, - mountNode: mountNode ?? fallbackMountNode, + children: props.children, + mountNode: element ?? fallbackElement, virtualParentRootRef, }; diff --git a/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.test.ts b/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.test.ts new file mode 100644 index 00000000000000..6dae9ad8e98bd5 --- /dev/null +++ b/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.test.ts @@ -0,0 +1,23 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { usePortalMountNode } from './usePortalMountNode'; + +describe('usePortalMountNode', () => { + it('creates an element and attaches it to "document.body"', () => { + const { result } = renderHook(() => usePortalMountNode({})); + + expect(result.current).toBeInstanceOf(HTMLDivElement); + expect(document.body.contains(result.current)).toBeTruthy(); + }); + + it('applies classes to an element', () => { + const { result } = renderHook(() => usePortalMountNode({ className: 'foo' })); + + expect(result.current?.classList).toContain('foo'); + }); + + it('does not create an element if is disabled', () => { + const { result } = renderHook(() => usePortalMountNode({ disabled: true })); + + expect(result.current).toBeNull(); + }); +}); diff --git a/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.ts b/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.ts index a627b31264db68..ffc2e73f903add 100644 --- a/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.ts +++ b/packages/react-components/react-portal/src/components/Portal/usePortalMountNode.ts @@ -7,13 +7,15 @@ import { makeStyles, mergeClasses } from '@griffel/react'; import { useFocusVisible } from '@fluentui/react-tabster'; import { useDisposable } from 'use-disposable'; -const useInsertionEffect = (React as never)['useInsertion' + 'Effect'] as typeof React.useLayoutEffect; +const useInsertionEffect = (React as never)['useInsertion' + 'Effect'] as typeof React.useLayoutEffect | undefined; export type UsePortalMountNodeOptions = { /** * Since hooks cannot be called conditionally use this flag to disable creating the node */ disabled?: boolean; + + className?: string; }; const useStyles = makeStyles({ @@ -23,10 +25,8 @@ const useStyles = makeStyles({ }, }); -const reactMajorVersion = Number(React.version.split('.')[0]); - /** - * Creates a new element on a document.body to mount portals + * Creates a new element on a "document.body" to mount portals. */ export const usePortalMountNode = (options: UsePortalMountNodeOptions): HTMLElement | null => { const { targetDocument, dir } = useFluent(); @@ -34,7 +34,7 @@ export const usePortalMountNode = (options: UsePortalMountNodeOptions): HTMLElem const classes = useStyles(); const themeClassName = useThemeClassName(); - const className = mergeClasses(themeClassName, classes.root); + const className = mergeClasses(themeClassName, classes.root, options.className); const element = useDisposable(() => { if (targetDocument === undefined || options.disabled) { @@ -46,7 +46,7 @@ export const usePortalMountNode = (options: UsePortalMountNodeOptions): HTMLElem return [newElement, () => newElement.remove()]; }, [targetDocument]); - if (reactMajorVersion >= 18) { + if (useInsertionEffect) { // eslint-disable-next-line react-hooks/rules-of-hooks useInsertionEffect(() => { if (!element) { diff --git a/packages/react-components/react-progress/src/components/ProgressBar/useProgressBarStyles.ts b/packages/react-components/react-progress/src/components/ProgressBar/useProgressBarStyles.ts index ae72c534ae7c46..23b6ebd63d956e 100644 --- a/packages/react-components/react-progress/src/components/ProgressBar/useProgressBarStyles.ts +++ b/packages/react-components/react-progress/src/components/ProgressBar/useProgressBarStyles.ts @@ -1,6 +1,5 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; -import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import type { ProgressBarState, ProgressBarSlots } from './ProgressBar.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; @@ -20,18 +19,10 @@ const barThicknessValues = { const indeterminateProgressBar = { '0%': { - left: '-100% /* @noflip */', + left: '-33%', // matches indeterminate bar width }, '100%': { - left: '100% /* @noflip */', - }, -}; -const indeterminateProgressBarRTL = { - '100%': { - right: '-100% /* @noflip */', - }, - '0%': { - right: '100% /* @noflip */', + left: '100%', }, }; @@ -90,6 +81,7 @@ const useBarStyles = makeStyles({ )`, animationName: indeterminateProgressBar, animationDuration: '3s', + animationTimingFunction: 'linear', animationIterationCount: 'infinite', '@media screen and (prefers-reduced-motion: reduce)': { animationDuration: '0.01ms', @@ -97,10 +89,6 @@ const useBarStyles = makeStyles({ }, }, - rtl: { - animationName: indeterminateProgressBarRTL, - }, - brand: { backgroundColor: tokens.colorCompoundBrandBackground, }, @@ -123,7 +111,6 @@ export const useProgressBarStyles_unstable = (state: ProgressBarState): Progress const { color, max, shape, thickness, value } = state; const rootStyles = useRootStyles(); const barStyles = useBarStyles(); - const { dir } = useFluent(); state.root.className = mergeClasses( progressBarClassNames.root, @@ -139,7 +126,6 @@ export const useProgressBarStyles_unstable = (state: ProgressBarState): Progress barStyles.base, barStyles.brand, value === undefined && barStyles.indeterminate, - value === undefined && dir === 'rtl' && barStyles.rtl, value !== undefined && value > ZERO_THRESHOLD && barStyles.nonZeroDeterminate, color && value !== undefined && barStyles[color], state.bar.className, diff --git a/packages/react-components/react-tabster/etc/react-tabster.api.md b/packages/react-components/react-tabster/etc/react-tabster.api.md index e7a5482fe9ed45..76d7e373a639d2 100644 --- a/packages/react-components/react-tabster/etc/react-tabster.api.md +++ b/packages/react-components/react-tabster/etc/react-tabster.api.md @@ -19,7 +19,6 @@ export function createCustomFocusIndicatorStyle[0]; export interface CreateCustomFocusIndicatorStyleOptions { + /** + * Control if the indicator appears when the corresponding element is focused, + * or any child is focused within the corresponding element. + * @default 'focus' + */ selector?: 'focus' | 'focus-within'; /** * Enables the browser default outline style diff --git a/packages/react-components/react-text/package.json b/packages/react-components/react-text/package.json index 5ccd06ff9f6b4a..79b237dc2d6304 100644 --- a/packages/react-components/react-text/package.json +++ b/packages/react-components/react-text/package.json @@ -32,8 +32,9 @@ "@fluentui/scripts-tasks": "*" }, "dependencies": { - "@griffel/react": "^1.5.2", + "@fluentui/react-shared-contexts": "^9.3.0", "@fluentui/react-theme": "^9.1.5", + "@griffel/react": "^1.5.2", "@fluentui/react-utilities": "^9.7.0", "@swc/helpers": "^0.4.14" }, diff --git a/packages/react-components/react-text/src/components/Text/Text.tsx b/packages/react-components/react-text/src/components/Text/Text.tsx index 697c6c1d57f655..f9ca2632f0f947 100644 --- a/packages/react-components/react-text/src/components/Text/Text.tsx +++ b/packages/react-components/react-text/src/components/Text/Text.tsx @@ -4,6 +4,7 @@ import { renderText_unstable } from './renderText'; import { useTextStyles_unstable } from './useTextStyles'; import type { TextProps } from './Text.types'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useCustomStyleHooks_unstable } from '@fluentui/react-shared-contexts'; /** * Typography and styling abstraction component used to ensure consistency of text. @@ -13,6 +14,9 @@ export const Text: ForwardRefComponent = React.forwardRef((props, ref useTextStyles_unstable(state); + const { useTextStyles_unstable: useCustomStyles } = useCustomStyleHooks_unstable(); + useCustomStyles(state); + return renderText_unstable(state); // Work around some small mismatches in inferred types which don't matter in practice }) as ForwardRefComponent; diff --git a/packages/react-components/react-theme/stories/Theme/typography/ThemeTypography.stories.tsx b/packages/react-components/react-theme/stories/Theme/typography/ThemeTypography.stories.tsx index 7fdded721696f2..61088d3411b618 100644 --- a/packages/react-components/react-theme/stories/Theme/typography/ThemeTypography.stories.tsx +++ b/packages/react-components/react-theme/stories/Theme/typography/ThemeTypography.stories.tsx @@ -1,21 +1,37 @@ import * as React from 'react'; -import { makeStyles, Subtitle2Stronger, Text, typographyStyles } from '@fluentui/react-components'; +import { + makeStyles, + Subtitle2Stronger, + Text, + Theme, + typographyStyles, + webLightTheme, +} from '@fluentui/react-components'; import type { TypographyStyles } from '@fluentui/react-components'; -type TypographyTokens = [token: keyof TypographyStyles, tokenName: string, values: string[]][]; +type TypographyTokens = [token: keyof TypographyStyles, tokenName: string, entries: [string, string][]][]; const useStyles = makeStyles({ container: { rowGap: '24px', columnGap: '48px', display: 'grid', - gridTemplateColumns: 'auto auto 1fr', + gridTemplateColumns: 'auto auto auto 1fr', alignItems: 'start', }, + value: { + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflowX: 'hidden', + maxWidth: '10.5em', + }, ...typographyStyles, }); +// FIXME: hardcoded theme +const theme = webLightTheme; + const tokenOrder: (keyof TypographyStyles)[] = [ 'caption2', 'caption2Strong', @@ -39,7 +55,7 @@ const tokenOrder: (keyof TypographyStyles)[] = [ const tokens: TypographyTokens = tokenOrder.map(token => [ token, token.replace(/([A-Z\d])/g, ' $1').replace(/^(.)/, firstChar => firstChar.toUpperCase()), - Object.values(typographyStyles[token]).map(v => v.replace(/var\(--(.+)\)/, '$1')), + Object.entries(typographyStyles[token]).map(([k, v]) => [k, v.replace(/var\(--(.+)\)/, '$1')]), ]); export const Typography = () => { @@ -49,15 +65,24 @@ export const Typography = () => {
Name Tokens + Default Values Example - {tokens.map(([token, tokenName, values]) => ( + {tokens.map(([token, tokenName, entries]) => ( {token}
- {values.map(value => ( -
{value}
+ {entries.map(([key, value]) => ( +
{value}
+ ))} +
+ +
+ {entries.map(([key, value]) => ( +
+ {key}: {theme[value as keyof Theme]} +
))}
diff --git a/packages/react-components/theme-designer/package.json b/packages/react-components/theme-designer/package.json index 79e40709911ba1..6cf0a510c126b6 100644 --- a/packages/react-components/theme-designer/package.json +++ b/packages/react-components/theme-designer/package.json @@ -37,7 +37,6 @@ "@swc/helpers": "^0.4.14", "@fluentui/react-components": "^9.18.0", "@fluentui/react-icons": "^2.0.175", - "@fluent-blocks/colors": "9.2.0", "codesandbox-import-utils": "2.2.3", "@types/dedent": "0.7.0", "@fluentui/react-alert": "9.0.0-beta.38", diff --git a/packages/react-components/theme-designer/src/colors/csswg.ts b/packages/react-components/theme-designer/src/colors/csswg.ts new file mode 100644 index 00000000000000..a3febbcb0c3575 --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/csswg.ts @@ -0,0 +1,774 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +// The following is a combination of several files retrieved from CSSWG’s +// CSS Color 4 module. It was modified to support TypeScript types adapted for +// the Fluent Blocks `colors` package and formatted to meet its style criteria. +import { Vec2, Vec3, Vec4 } from './types'; + +// [willshown]: Adjusted to export a TypeScript module. Retrieved on 24 May 2021 +// from https://drafts.csswg.org/css-color-4/multiply-matrices.js + +/** + * Simple matrix (and vector) multiplication + * Warning: No error handling for incompatible dimensions! + * @author Lea Verou 2020 MIT License + */ + +type MatrixIO = number[][] | number[]; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isFlat(A: any): A is number[] { + return !Array.isArray(A[0]); +} + +// A is m x n. B is n x p. product is m x p. +export default function multiplyMatrices(AMatrixOrVector: MatrixIO, BMatrixOrVector: MatrixIO): MatrixIO { + const m = AMatrixOrVector.length; + + const A: number[][] = isFlat(AMatrixOrVector) + ? // A is vector, convert to [[a, b, c, ...]] + [AMatrixOrVector] + : AMatrixOrVector; + + const B: number[][] = isFlat(BMatrixOrVector) + ? // B is vector, convert to [[a], [b], [c], ...]] + BMatrixOrVector.map(x => [x]) + : BMatrixOrVector; + + const p = B[0].length; + const B_cols = B[0].map((_, i) => B.map(x => x[i])); // transpose B + let product: MatrixIO = A.map(row => + B_cols.map(col => { + if (!Array.isArray(row)) { + return col.reduce((a, c) => a + c * row, 0); + } + + return row.reduce((a, c, i) => a + c * (col[i] || 0), 0); + }), + ); + + if (m === 1) { + product = product[0]; // Avoid [[a, b, c, ...]] + } + + if (p === 1) { + return (product as number[][]).map(x => x[0]); // Avoid [[a], [b], [c], ...]] + } + + return product; +} + +// Sample code for color conversions +// Conversion can also be done using ICC profiles and a Color Management System +// For clarity, a library is used for matrix multiplication (multiply-matrices.js) + +// [willshown]: Adjusted to export a TypeScript module. Retrieved on 24 May 2021 +// from https://drafts.csswg.org/css-color-4/conversions.js + +// sRGB-related functions + +export function lin_sRGB(RGB: Vec3) { + // convert an array of sRGB values + // where in-gamut values are in the range [0 - 1] + // to linear light (un-companded) form. + // https://en.wikipedia.org/wiki/SRGB + // Extended transfer function: + // for negative values, linear portion is extended on reflection of axis, + // then reflected power function is used. + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs < 0.04045) { + return val / 12.92; + } + + return sign * Math.pow((abs + 0.055) / 1.055, 2.4); + }) as Vec3; +} + +export function gam_sRGB(RGB: Vec3) { + // convert an array of linear-light sRGB values in the range 0.0-1.0 + // to gamma corrected form + // https://en.wikipedia.org/wiki/SRGB + // Extended transfer function: + // For negative values, linear portion extends on reflection + // of axis, then uses reflected pow below that + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs > 0.0031308) { + return sign * (1.055 * Math.pow(abs, 1 / 2.4) - 0.055); + } + + return 12.92 * val; + }) as Vec3; +} + +export function lin_sRGB_to_XYZ(rgb: Vec3) { + // convert an array of linear-light sRGB values to CIE XYZ + // using sRGB's own white, D65 (no chromatic adaptation) + + const M = [ + [0.41239079926595934, 0.357584339383878, 0.1804807884018343], + [0.21263900587151027, 0.715168678767756, 0.07219231536073371], + [0.01933081871559182, 0.11919477979462598, 0.9505321522496607], + ]; + return multiplyMatrices(M, rgb) as Vec3; +} + +export function XYZ_to_lin_sRGB(XYZ: Vec3) { + // convert XYZ to linear-light sRGB + + const M = [ + [3.2409699419045226, -1.537383177570094, -0.4986107602930034], + [-0.9692436362808796, 1.8759675015077202, 0.04155505740717559], + [0.05563007969699366, -0.20397695888897652, 1.0569715142428786], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// display-p3-related functions + +export function lin_P3(RGB: Vec3) { + // convert an array of display-p3 RGB values in the range 0.0 - 1.0 + // to linear light (un-companded) form. + + return lin_sRGB(RGB) as Vec3; // same as sRGB +} + +export function gam_P3(RGB: Vec3) { + // convert an array of linear-light display-p3 RGB in the range 0.0-1.0 + // to gamma corrected form + + return gam_sRGB(RGB) as Vec3; // same as sRGB +} + +export function lin_P3_to_XYZ(rgb: Vec3) { + // convert an array of linear-light display-p3 values to CIE XYZ + // using D65 (no chromatic adaptation) + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + const M = [ + [0.4865709486482162, 0.26566769316909306, 0.1982172852343625], + [0.2289745640697488, 0.6917385218365064, 0.079286914093745], + [0.0, 0.04511338185890264, 1.043944368900976], + ]; + // 0 was computed as -3.972075516933488e-17 + + return multiplyMatrices(M, rgb) as Vec3; +} + +export function XYZ_to_lin_P3(XYZ: Vec3) { + // convert XYZ to linear-light P3 + const M = [ + [2.493496911941425, -0.9313836179191239, -0.40271078445071684], + [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577], + [0.03584583024378447, -0.07617238926804182, 0.9568845240076872], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// prophoto-rgb functions + +export function lin_ProPhoto(RGB: Vec3) { + // convert an array of prophoto-rgb values + // where in-gamut colors are in the range [0.0 - 1.0] + // to linear light (un-companded) form. + // Transfer curve is gamma 1.8 with a small linear portion + // Extended transfer function + const Et2 = 16 / 512; + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs <= Et2) { + return val / 16; + } + + return sign * Math.pow(val, 1.8); + }) as Vec3; +} + +export function gam_ProPhoto(RGB: Vec3) { + // convert an array of linear-light prophoto-rgb in the range 0.0-1.0 + // to gamma corrected form + // Transfer curve is gamma 1.8 with a small linear portion + // TODO for negative values, extend linear portion on reflection of axis, then add pow below that + const Et = 1 / 512; + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs >= Et) { + return sign * Math.pow(abs, 1 / 1.8); + } + + return 16 * val; + }) as Vec3; +} + +export function lin_ProPhoto_to_XYZ(rgb: Vec3) { + // convert an array of linear-light prophoto-rgb values to CIE XYZ + // using D50 (so no chromatic adaptation needed afterwards) + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + const M = [ + [0.7977604896723027, 0.13518583717574031, 0.0313493495815248], + [0.2880711282292934, 0.7118432178101014, 0.00008565396060525902], + [0.0, 0.0, 0.8251046025104601], + ]; + + return multiplyMatrices(M, rgb) as Vec3; +} + +export function XYZ_to_lin_ProPhoto(XYZ: Vec3) { + // convert XYZ to linear-light prophoto-rgb + const M = [ + [1.3457989731028281, -0.25558010007997534, -0.05110628506753401], + [-0.5446224939028347, 1.5082327413132781, 0.02053603239147973], + [0.0, 0.0, 1.2119675456389454], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// a98-rgb functions + +export function lin_a98rgb(RGB: Vec3) { + // convert an array of a98-rgb values in the range 0.0 - 1.0 + // to linear light (un-companded) form. + // negative values are also now accepted + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + return sign * Math.pow(abs, 563 / 256); + }) as Vec3; +} + +export function gam_a98rgb(RGB: Vec3) { + // convert an array of linear-light a98-rgb in the range 0.0-1.0 + // to gamma corrected form + // negative values are also now accepted + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + return sign * Math.pow(abs, 256 / 563); + }) as Vec3; +} + +export function lin_a98rgb_to_XYZ(rgb: Vec3) { + // convert an array of linear-light a98-rgb values to CIE XYZ + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + // has greater numerical precision than section 4.3.5.3 of + // https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf + // but the values below were calculated from first principles + // from the chromaticity coordinates of R G B W + // see matrixmaker.html + const M = [ + [0.5766690429101305, 0.1855582379065463, 0.1882286462349947], + [0.29734497525053605, 0.6273635662554661, 0.07529145849399788], + [0.02703136138641234, 0.07068885253582723, 0.9913375368376388], + ]; + + return multiplyMatrices(M, rgb) as Vec3; +} + +export function XYZ_to_lin_a98rgb(XYZ: Vec3) { + // convert XYZ to linear-light a98-rgb + const M = [ + [2.0415879038107465, -0.5650069742788596, -0.34473135077832956], + [-0.9692436362808795, 1.8759675015077202, 0.04155505740717557], + [0.013444280632031142, -0.11836239223101838, 1.0151749943912054], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// Rec. 2020-related functions + +export function lin_2020(RGB: Vec3) { + // convert an array of rec2020 RGB values in the range 0.0 - 1.0 + // to linear light (un-companded) form. + // ITU-R BT.2020-2 p.4 + + const α = 1.09929682680944; + const β = 0.018053968510807; + + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs < β * 4.5) { + return val / 4.5; + } + + return sign * Math.pow((abs + α - 1) / α, 1 / 0.45); + }) as Vec3; +} + +export function gam_2020(RGB: Vec3) { + // convert an array of linear-light rec2020 RGB in the range 0.0-1.0 + // to gamma corrected form + // ITU-R BT.2020-2 p.4 + + const α = 1.09929682680944; + const β = 0.018053968510807; + + return RGB.map(val => { + const sign = val < 0 ? -1 : 1; + const abs = Math.abs(val); + + if (abs > β) { + return sign * (α * Math.pow(abs, 0.45) - (α - 1)); + } + + return 4.5 * val; + }) as Vec3; +} + +export function lin_2020_to_XYZ(rgb: Vec3) { + // convert an array of linear-light rec2020 values to CIE XYZ + // using D65 (no chromatic adaptation) + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + const M = [ + [0.6369580483012914, 0.14461690358620832, 0.1688809751641721], + [0.2627002120112671, 0.6779980715188708, 0.05930171646986196], + [0.0, 0.028072693049087428, 1.060985057710791], + ]; + // 0 is actually calculated as 4.994106574466076e-17 + + return multiplyMatrices(M, rgb) as Vec3; +} + +export function XYZ_to_lin_2020(XYZ: Vec3) { + // convert XYZ to linear-light rec2020 + const M = [ + [1.7166511879712674, -0.35567078377639233, -0.25336628137365974], + [-0.6666843518324892, 1.6164812366349395, 0.01576854581391113], + [0.017639857445310783, -0.042770613257808524, 0.9421031212354738], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// Chromatic adaptation + +export function D65_to_D50(XYZ: Vec3) { + // Bradford chromatic adaptation from D65 to D50 + // The matrix below is the result of three operations: + // - convert from XYZ to retinal cone domain + // - scale components from one reference white to another + // - convert back to XYZ + // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + const M = [ + [1.0479298208405488, 0.022946793341019088, -0.05019222954313557], + [0.029627815688159344, 0.990434484573249, -0.01707382502938514], + [-0.009243058152591178, 0.015055144896577895, 0.7518742899580008], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +export function D50_to_D65(XYZ: Vec3) { + // Bradford chromatic adaptation from D50 to D65 + const M = [ + [0.9554734527042182, -0.023098536874261423, 0.0632593086610217], + [-0.028369706963208136, 1.0099954580058226, 0.021041398966943008], + [0.012314001688319899, -0.020507696433477912, 1.3303659366080753], + ]; + + return multiplyMatrices(M, XYZ) as Vec3; +} + +// Lab and LCH + +export function XYZ_to_Lab(XYZ: Vec3) { + // Assuming XYZ is relative to D50, convert to CIE Lab + // from CIE standard, which now defines these as a rational fraction + const ε = 216 / 24389; // 6^3/29^3 + const κ = 24389 / 27; // 29^3/3^3 + const white = [0.96422, 1.0, 0.82521]; // D50 reference white + + // compute xyz, which is XYZ scaled relative to reference white + const xyz = XYZ.map((value, i) => value / white[i]); + + // now compute f + const f = xyz.map(value => (value > ε ? Math.cbrt(value) : (κ * value + 16) / 116)); + + return [ + 116 * f[1] - 16, // L + 500 * (f[0] - f[1]), // a + 200 * (f[1] - f[2]), // b + ] as Vec3; +} + +export function Lab_to_XYZ(Lab: Vec3) { + // Convert Lab to D50-adapted XYZ + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + const κ = 24389 / 27; // 29^3/3^3 + const ε = 216 / 24389; // 6^3/29^3 + const white = [0.96422, 1.0, 0.82521]; // D50 reference white + const f = []; + + // compute f, starting with the luminance-related term + f[1] = (Lab[0] + 16) / 116; + f[0] = Lab[1] / 500 + f[1]; + f[2] = f[1] - Lab[2] / 200; + + // compute xyz + const xyz = [ + Math.pow(f[0], 3) > ε ? Math.pow(f[0], 3) : (116 * f[0] - 16) / κ, + Lab[0] > κ * ε ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / κ, + Math.pow(f[2], 3) > ε ? Math.pow(f[2], 3) : (116 * f[2] - 16) / κ, + ]; + + // Compute XYZ by scaling xyz by reference white + return xyz.map((value, i) => value * white[i]) as Vec3; +} + +export function Lab_to_LCH(Lab: Vec3) { + // Convert to polar form + const hue = (Math.atan2(Lab[2], Lab[1]) * 180) / Math.PI; + return [ + Lab[0], // L is still L + Math.sqrt(Math.pow(Lab[1], 2) + Math.pow(Lab[2], 2)), // Chroma + hue >= 0 ? hue : hue + 360, // Hue, in degrees [0 to 360) + ] as Vec3; +} + +export function LCH_to_Lab(LCH: Vec3) { + // Convert from polar form + return [ + LCH[0], // L is still L + LCH[1] * Math.cos((LCH[2] * Math.PI) / 180), // a + LCH[1] * Math.sin((LCH[2] * Math.PI) / 180), // b + ] as Vec3; +} + +/** + * Converts an RGB color value to HSV. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSV_color_space. + * Assumes r, g, and b are contained in the set [0, 1] and + * returns h, s, and v in the set [0, 1]. + * + * @param rgb The red, green, and blue color values + * @return Array The HSV representation + */ +export function rgbToHsv(rgb: Vec3) { + const [r, g, b] = rgb; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h: number; + const v = max; + + const d = max - min; + const s = max === 0 ? 0 : d / max; + + if (max === min) { + h = 0; // achromatic + } else { + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + + h = h! / 6; + } + + return [h, s, v] as Vec3; +} + +// utility functions for color conversions + +// [willshown]: Adjusted to export a TypeScript module. +// Retrieved on 24 May 2021 from https://drafts.csswg.org/css-color-4/utilities.js + +export function sRGB_to_luminance(RGB: Vec3) { + // convert an array of gamma-corrected sRGB values + // in the 0.0 to 1.0 range + // to linear-light sRGB, then to CIE XYZ + // and return luminance (the Y value) + + const XYZ = lin_sRGB_to_XYZ(lin_sRGB(RGB)); + return XYZ[1]; +} + +export function contrast(RGB1: Vec3, RGB2: Vec3) { + // return WCAG 2.1 contrast ratio + // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio + // for two sRGB values + // given as arrays of 0.0 to 1.0 + + const L1 = sRGB_to_luminance(RGB1); + const L2 = sRGB_to_luminance(RGB2); + + if (L1 > L2) { + return (L1 + 0.05) / (L2 + 0.05); + } + + return (L2 + 0.05) / (L1 + 0.05); +} + +export function sRGB_to_LCH(RGB: Vec3) { + // convert an array of gamma-corrected sRGB values + // in the 0.0 to 1.0 range + // to linear-light sRGB, then to CIE XYZ, + // then adapt from D65 to D50, + // then convert XYZ to CIE Lab + // and finally, convert to CIE LCH + + return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_sRGB_to_XYZ(lin_sRGB(RGB))))); +} + +export function sRGB_to_LAB(RGB: Vec3) { + // convert an array of gamma-corrected sRGB values + // in the 0.0 to 1.0 range + // to linear-light sRGB, then to CIE XYZ, + // then adapt from D65 to D50, + // then convert XYZ to CIE Lab + + return XYZ_to_Lab(D65_to_D50(lin_sRGB_to_XYZ(lin_sRGB(RGB)))); +} + +export function P3_to_LCH(RGB: Vec3) { + // convert an array of gamma-corrected display-p3 values + // in the 0.0 to 1.0 range + // to linear-light display-p3, then to CIE XYZ, + // then adapt from D65 to D50, + // then convert XYZ to CIE Lab + // and finally, convert to CIE LCH + + return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_P3_to_XYZ(lin_P3(RGB))))); +} + +export function r2020_to_LCH(RGB: Vec3) { + // convert an array of gamma-corrected rec.2020 values + // in the 0.0 to 1.0 range + // to linear-light sRGB, then to CIE XYZ, + // then adapt from D65 to D50, + // then convert XYZ to CIE Lab + // and finally, convert to CIE LCH + + return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_2020_to_XYZ(lin_2020(RGB))))); +} + +export function LCH_to_sRGB(LCH: Vec3) { + // convert an array of CIE LCH values + // to CIE Lab, and then to XYZ, + // adapt from D50 to D65, + // then convert XYZ to linear-light sRGB + // and finally to gamma corrected sRGB + // for in-gamut colors, components are in the 0.0 to 1.0 range + // out of gamut colors may have negative components + // or components greater than 1.0 + // so check for that :) + + return gam_sRGB(XYZ_to_lin_sRGB(D50_to_D65(Lab_to_XYZ(LCH_to_Lab(LCH))))); +} + +export function LAB_to_sRGB(LAB: Vec3) { + // convert an array of CIE Lab values to XYZ, + // adapt from D50 to D65, + // then convert XYZ to linear-light sRGB + // and finally to gamma corrected sRGB + // for in-gamut colors, components are in the 0.0 to 1.0 range + // out of gamut colors may have negative components + // or components greater than 1.0 + // so check for that :) + + return gam_sRGB(XYZ_to_lin_sRGB(D50_to_D65(Lab_to_XYZ(LAB)))); +} + +export function LCH_to_P3(LCH: Vec3) { + // convert an array of CIE LCH values + // to CIE Lab, and then to XYZ, + // adapt from D50 to D65, + // then convert XYZ to linear-light display-p3 + // and finally to gamma corrected display-p3 + // for in-gamut colors, components are in the 0.0 to 1.0 range + // out of gamut colors may have negative components + // or components greater than 1.0 + // so check for that :) + + return gam_P3(XYZ_to_lin_P3(D50_to_D65(Lab_to_XYZ(LCH_to_Lab(LCH))))); +} + +export function LCH_to_r2020(LCH: Vec3) { + // convert an array of CIE LCH values + // to CIE Lab, and then to XYZ, + // adapt from D50 to D65, + // then convert XYZ to linear-light rec.2020 + // and finally to gamma corrected rec.2020 + // for in-gamut colors, components are in the 0.0 to 1.0 range + // out of gamut colors may have negative components + // or components greater than 1.0 + // so check for that :) + + return gam_2020(XYZ_to_lin_2020(D50_to_D65(Lab_to_XYZ(LCH_to_Lab(LCH))))); +} + +// this is straight from the CSS Color 4 spec + +export function hslToRgb(hue: number, sat: number, light: number) { + // For simplicity, this algorithm assumes that the hue has been normalized + // to a number in the half-open range [0, 6), and the saturation and lightness + // have been normalized to the range [0, 1]. It returns an array of three numbers + // representing the red, green, and blue channels of the colors, + // normalized to the range [0, 1] + const t2 = light <= 0.5 ? light * (sat + 1) : light + sat - light * sat; + const t1 = light * 2 - t2; + const r = hueToChannel(t1, t2, hue + 2); + const g = hueToChannel(t1, t2, hue); + const b = hueToChannel(t1, t2, hue - 2); + return [r, g, b] as Vec3; +} + +export function hueToChannel(t1: number, t2: number, hue: number): number { + if (hue < 0) { + hue += 6; + } + if (hue >= 6) { + hue -= 6; + } + + if (hue < 1) { + return (t2 - t1) * hue + t1; + } else if (hue < 3) { + return t2; + } else if (hue < 4) { + return (t2 - t1) * (4 - hue) + t1; + } else { + return t1; + } +} + +// These are the naive algorithms from CS Color 4 + +export function naive_CMYK_to_sRGB(CMYK: Vec4) { + // CMYK is an array of four values + // in the range [0.0, 1.0] + // the optput is an array of [RGB] + // also in the [0.0, 1.0] range + // because the naive algorithm does not generate out of gamut colors + // neither does it generate accurate simulations of practical CMYK colors + + const cyan = CMYK[0]; + const magenta = CMYK[1]; + const yellow = CMYK[2]; + const black = CMYK[3]; + + const red = 1 - Math.min(1, cyan * (1 - black) + black); + const green = 1 - Math.min(1, magenta * (1 - black) + black); + const blue = 1 - Math.min(1, yellow * (1 - black) + black); + + return [red, green, blue] as Vec3; +} + +export function naive_sRGB_to_CMYK(RGB: Vec3) { + // RGB is an arravy of three values + // in the range [0.0, 1.0] + // the output is an array of [CMYK] + // also in the [0.0, 1.0] range + // with maximum GCR and (I think) 200% TAC + // the naive algorithm does not generate out of gamut colors + // neither does it generate accurate simulations of practical CMYK colors + + const red = RGB[0]; + const green = RGB[1]; + const blue = RGB[2]; + + const black = 1 - Math.max(red, green, blue); + const cyan = black === 1.0 ? 0 : (1 - red - black) / (1 - black); + const magenta = black === 1.0 ? 0 : (1 - green - black) / (1 - black); + const yellow = black === 1.0 ? 0 : (1 - blue - black) / (1 - black); + + return [cyan, magenta, yellow, black] as Vec4; +} + +// Chromaticity utilities + +export function XYZ_to_xy(XYZ: Vec3) { + // Convert an array of three XYZ values + // to x,y chromaticity coordinates + + const X = XYZ[0]; + const Y = XYZ[1]; + const Z = XYZ[2]; + const sum = X + Y + Z; + return [X / sum, Y / sum] as Vec2; +} + +export function xy_to_uv(xy: Vec2) { + // convert an x,y chromaticity pair + // to u*,v* chromaticities + + const x = xy[0]; + const y = xy[1]; + const denom = -2 * x + 12 * y + 3; + return [(4 * x) / denom, (9 * y) / denom] as Vec2; +} + +export function XYZ_to_uv(XYZ: Vec3) { + // Convert an array of three XYZ values + // to u*,v* chromaticity coordinates + + const X = XYZ[0]; + const Y = XYZ[1]; + const Z = XYZ[2]; + const denom = X + 15 * Y + 3 * Z; + return [(4 * X) / denom, (9 * Y) / denom] as Vec2; +} + +// [willshown]: Truncated to export only relevant functions and adjusted to export a TypeScript +// module, some additional adjustments to remove alpha support. Retrieved on 24 May 2021 +// from https://raw.githubusercontent.com/LeaVerou/css.land/master/lch/lch.js + +function is_LCH_inside_sRGB(l: number, c: number, h: number): boolean { + const ε = 0.000005; + const rgb = LCH_to_sRGB([+l, +c, +h]); + return rgb.reduce((a: boolean, b: number) => a && b >= 0 - ε && b <= 1 + ε, true); +} + +export function snap_into_gamut(Lab: Vec3): Vec3 { + // Moves an LCH color into the sRGB gamut + // by holding the l and h steady, + // and adjusting the c via binary-search + // until the color is on the sRGB boundary. + + // .0001 chosen fairly arbitrarily as "close enough" + const ε = 0.0001; + + const LCH = Lab_to_LCH(Lab); + const l = LCH[0]; + let c = LCH[1]; + const h = LCH[2]; + + if (is_LCH_inside_sRGB(l, c, h)) { + return Lab; + } + + let hiC = c; + let loC = 0; + c /= 2; + + while (hiC - loC > ε) { + if (is_LCH_inside_sRGB(l, c, h)) { + loC = c; + } else { + hiC = c; + } + c = (hiC + loC) / 2; + } + + return LCH_to_Lab([l, c, h]); +} diff --git a/packages/react-components/theme-designer/src/colors/geometry.ts b/packages/react-components/theme-designer/src/colors/geometry.ts new file mode 100644 index 00000000000000..19ebec95762ceb --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/geometry.ts @@ -0,0 +1,206 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Curve, CurvePath, Vec3 } from './types'; + +const curveResolution = 128; + +// Many of these functions are ported from ThreeJS, which is distributed under +// the MIT license. Retrieved from https://github.com/mrdoob/three.js on +// 14 October 2021. + +function distanceTo(v1: Vec3, v2: Vec3) { + return Math.sqrt(distanceToSquared(v1, v2)); +} + +function distanceToSquared(v1: Vec3, v2: Vec3) { + const dx = v1[0] - v2[0]; + const dy = v1[1] - v2[1]; + const dz = v1[2] - v2[2]; + return dx * dx + dy * dy + dz * dz; +} + +function equals(v1: Vec3, v2: Vec3) { + return v1[0] === v2[0] && v1[1] === v2[1] && v1[2] === v2[2]; +} + +function QuadraticBezierP0(t: number, p: number): number { + const k = 1 - t; + return k * k * p; +} + +function QuadraticBezierP1(t: number, p: number): number { + return 2 * (1 - t) * t * p; +} + +function QuadraticBezierP2(t: number, p: number): number { + return t * t * p; +} + +function QuadraticBezier(t: number, p0: number, p1: number, p2: number): number { + return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); +} + +function getPointOnCurve(curve: Curve, t: number) { + const [v0, v1, v2] = curve.points; + return [ + QuadraticBezier(t, v0[0], v1[0], v2[0]), + QuadraticBezier(t, v0[1], v1[1], v2[1]), + QuadraticBezier(t, v0[2], v1[2], v2[2]), + ] as Vec3; +} + +function getPointsOnCurve(curve: Curve, divisions: number): Vec3[] { + const points = []; + for (let d = 0; d <= divisions; d++) { + points.push(getPointOnCurve(curve, d / divisions)); + } + return points; +} + +function getCurvePathLength(curvePath: CurvePath) { + const lengths = getCurvePathLengths(curvePath); + return lengths[lengths.length - 1]; +} + +function getCurvePathLengths(curvePath: CurvePath) { + if (curvePath.cacheLengths && curvePath.cacheLengths.length === curvePath.curves.length) { + return curvePath.cacheLengths; + } + // Get length of sub-curve + // Push sums into cached array + const lengths = []; + let sums = 0; + for (let i = 0, l = curvePath.curves.length; i < l; i++) { + sums += getCurveLength(curvePath.curves[i]); + lengths.push(sums); + } + curvePath.cacheLengths = lengths; + return lengths; +} + +function getCurveLength(curve: Curve) { + const lengths = getCurveLengths(curve); + return lengths[lengths.length - 1]; +} + +function getCurveLengths(curve: Curve, divisions = curveResolution) { + if (curve.cacheArcLengths && curve.cacheArcLengths.length === divisions + 1) { + return curve.cacheArcLengths; + } + + const cache = []; + let current; + let last = getPointOnCurve(curve, 0); + let sum = 0; + + cache.push(0); + + for (let p = 1; p <= divisions; p++) { + current = getPointOnCurve(curve, p / divisions); + sum += distanceTo(current, last); + cache.push(sum); + last = current; + } + + curve.cacheArcLengths = cache; + + return cache; // { sums: cache, sum: sum }; Sum is in the last element. +} + +function getCurveUtoTMapping(curve: Curve, u: number, distance?: number) { + const arcLengths = getCurveLengths(curve); + let i = 0; + const il = arcLengths.length; + let targetArcLength; // The targeted u distance value to get + + if (distance) { + targetArcLength = distance; + } else { + targetArcLength = u * arcLengths[il - 1]; + } + + // binary search for the index with largest value smaller than target u distance + + let low = 0; + let high = il - 1; + let comparison; + + while (low <= high) { + i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + comparison = arcLengths[i] - targetArcLength; + + if (comparison < 0) { + low = i + 1; + } else if (comparison > 0) { + high = i - 1; + } else { + high = i; + break; + } + } + + i = high; + + if (arcLengths[i] === targetArcLength) { + return i / (il - 1); + } + + // we could get finer grain at lengths, or use simple interpolation between two points + const lengthBefore = arcLengths[i]; + const lengthAfter = arcLengths[i + 1]; + + const segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; + + // add that fractional amount to t + const t = (i + segmentFraction) / (il - 1); + + return t; +} + +function getPointOnCurveAt(curve: Curve, u: number) { + return getPointOnCurve(curve, getCurveUtoTMapping(curve, u)); +} + +export function getPointOnCurvePath(curvePath: CurvePath, t: number): Vec3 | null { + const d = t * getCurvePathLength(curvePath); + const curveLengths = getCurvePathLengths(curvePath); + let i = 0; + + while (i < curveLengths.length) { + if (curveLengths[i] >= d) { + const diff = curveLengths[i] - d; + const curve = curvePath.curves[i]; + + const segmentLength = getCurveLength(curve); + const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + + return getPointOnCurveAt(curve, u); + } + i++; + } + return null; +} + +export function getPointsOnCurvePath(curvePath: CurvePath, divisions = curveResolution): Vec3[] { + const points = []; + let last; + + for (let i = 0, curves = curvePath.curves; i < curves.length; i++) { + const curve = curves[i]; + const pts = getPointsOnCurve(curve, divisions); + + for (const point of pts) { + if (last && equals(last, point)) { + // ensures no consecutive points are duplicates + continue; + } + + points.push(point); + last = point; + } + } + + return points; +} diff --git a/packages/react-components/theme-designer/src/colors/index.ts b/packages/react-components/theme-designer/src/colors/index.ts new file mode 100644 index 00000000000000..7cecca373ae30c --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/index.ts @@ -0,0 +1,5 @@ +export * from './csswg'; +export * from './geometry'; +export * from './palettes'; +export * from './templates'; +export * from './types'; diff --git a/packages/react-components/theme-designer/src/colors/palettes.ts b/packages/react-components/theme-designer/src/colors/palettes.ts new file mode 100644 index 00000000000000..6a10438d54c18b --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/palettes.ts @@ -0,0 +1,176 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { LAB_to_sRGB, LCH_to_Lab, Lab_to_LCH, sRGB_to_LCH, snap_into_gamut } from './csswg'; +import { getPointsOnCurvePath } from './geometry'; +import { CurvedHelixPath, Palette, Vec3 } from './types'; + +// This file contains functions that combine geometry and color math to create +// and work with palette curves. + +/** + * When distributing output shades along the curve, for each shade’s lightness a + * logarithmically distributed value is averaged with a linearly distributed + * value to this degree between zero and one, zero meaning use the logarithmic + * value, one meaning use the linear value. + */ +const defaultLinearity = 0.75; + +function getLinearSpace(min: number, max: number, n: number) { + const result = []; + const delta = (max - min) / n; + for (let i = 0; i < n; i++) { + result[i] = min + delta * i; + } + return result; +} + +const getLogSpace = (min: number, max: number, n: number) => { + const a = min <= 0 ? 0 : Math.log(min); + const b = Math.log(max); + const delta = (b - a) / n; + + const result = [Math.pow(Math.E, a)]; + for (let i = 1; i < n; i += 1) { + result.push(Math.pow(Math.E, a + delta * i)); + } + result.push(Math.pow(Math.E, b)); + return result; +}; + +function paletteShadesFromCurvePoints( + curvePoints: Vec3[], + nShades: number, + range = [0, 100], + linearity = defaultLinearity, +): Vec3[] { + if (curvePoints.length <= 2) { + return []; + } + + const paletteShades = []; + + const logLightness = getLogSpace(Math.log10(range[0]), Math.log10(range[1]), nShades); + + const linearLightness = getLinearSpace(range[0], range[1], nShades); + + let c = 0; + + for (let i = 0; i < nShades; i++) { + const l = Math.min( + range[1], + Math.max(range[0], logLightness[i] * (1 - linearity) + linearLightness[i] * linearity), + ); + + while (l > curvePoints[c + 1][0]) { + c++; + } + + const [l1, a1, b1] = curvePoints[c]; + const [l2, a2, b2] = curvePoints[c + 1]; + + const u = (l - l1) / (l2 - l1); + + paletteShades[i] = [l1 + (l2 - l1) * u, a1 + (a2 - a1) * u, b1 + (b2 - b1) * u] as Vec3; + } + + return paletteShades.map(snap_into_gamut); +} + +export function paletteShadesFromCurve( + curve: CurvedHelixPath, + nShades = 16, + range = [0, 100], + linearity = defaultLinearity, + curveDepth = 24, +): Vec3[] { + return paletteShadesFromCurvePoints( + getPointsOnCurvePath(curve, Math.ceil((curveDepth * (1 + Math.abs(curve.torsion || 1))) / 2)).map( + (curvePoint: Vec3) => getPointOnHelix(curvePoint, curve.torsion, curve.torsionT0), + ), + nShades, + range, + linearity, + ); +} + +export function sRGB_to_hex(rgb: Vec3): string { + return `#${rgb + .map(x => { + const channel = x < 0 ? 0 : Math.floor(x >= 1.0 ? 255 : x * 256); + return channel.toString(16).padStart(2, '0'); + }) + .join('')}`; +} + +export function Lab_to_hex(lab: Vec3): string { + return sRGB_to_hex(LAB_to_sRGB(lab)); +} + +export function hex_to_sRGB(hex: string): Vec3 { + const aRgbHex = hex.match(/#?(..)(..)(..)/); + return aRgbHex + ? [parseInt(aRgbHex[1], 16) / 255, parseInt(aRgbHex[2], 16) / 255, parseInt(aRgbHex[3], 16) / 255] + : [0, 0, 0]; +} + +export function hex_to_LCH(hex: string): Vec3 { + return sRGB_to_LCH(hex_to_sRGB(hex)); +} + +function paletteShadesToHex(paletteShades: Vec3[]): string[] { + return paletteShades.map(Lab_to_hex); +} + +function getPointOnHelix(pointOnCurve: Vec3, torsion = 0, torsionT0 = 50): Vec3 { + const t = pointOnCurve[0]; + const [l, c, h] = Lab_to_LCH(pointOnCurve); + const hueOffset = torsion * (t - torsionT0); + return LCH_to_Lab([l, c, h + hueOffset]); +} + +// function getPointOnCurvedHelixPathWithinGamut(curvedHelixPath: CurvedHelixPath, t: number): Vec3 { +// return snap_into_gamut( +// getPointOnHelix(getPointOnCurvePath(curvedHelixPath, t)!, curvedHelixPath.torsion, curvedHelixPath.torsionT0), +// ); +// } + +export function curvePathFromPalette({ keyColor, darkCp, lightCp, hueTorsion }: Palette): CurvedHelixPath { + const blackPosition = [0, 0, 0]; + const whitePosition = [100, 0, 0]; + const keyColorPosition = LCH_to_Lab(keyColor); + const [l, a, b] = keyColorPosition; + + const darkControlPosition = [l * (1 - darkCp), a, b]; + const lightControlPosition = [l + (100 - l) * lightCp, a, b]; + + return { + curves: [ + { points: [blackPosition, darkControlPosition, keyColorPosition] }, + { points: [keyColorPosition, lightControlPosition, whitePosition] }, + ], + torsion: hueTorsion, + torsionT0: l, + } as CurvedHelixPath; +} + +export function cssGradientFromCurve( + curve: CurvedHelixPath, + nShades = 16, + range = [0, 100], + linearity = defaultLinearity, + curveDepth = 24, +) { + const hexes = paletteShadesToHex(paletteShadesFromCurve(curve, nShades, range, linearity, curveDepth)); + return `linear-gradient(to right, ${hexes.join(', ')})`; +} + +export function hexColorsFromPalette( + palette: Palette, + nShades = 16, + range = [0, 100], + linearity = defaultLinearity, + curveDepth = 24, +): string[] { + return paletteShadesToHex( + paletteShadesFromCurve(curvePathFromPalette(palette), nShades, range, linearity, curveDepth), + ); +} diff --git a/packages/react-components/theme-designer/src/colors/templates.ts b/packages/react-components/theme-designer/src/colors/templates.ts new file mode 100644 index 00000000000000..08eb5231596174 --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/templates.ts @@ -0,0 +1,17 @@ +import { NamedPalette, NamedTheme } from './types'; + +export const paletteTemplate = (id: string): NamedPalette & { id: string } => ({ + id, + name: '', + keyColor: [44.51, 39.05, 288.84], + darkCp: 2 / 3, + lightCp: 1 / 3, + hueTorsion: 0, +}); + +export const themeTemplate = (id: string): NamedTheme & { id: string } => ({ + id, + name: '', + backgrounds: {}, + foregrounds: {}, +}); diff --git a/packages/react-components/theme-designer/src/colors/types.ts b/packages/react-components/theme-designer/src/colors/types.ts new file mode 100644 index 00000000000000..dee880d18a740a --- /dev/null +++ b/packages/react-components/theme-designer/src/colors/types.ts @@ -0,0 +1,62 @@ +export type Vec2 = [number, number]; +export type Vec3 = [number, number, number]; +export type Vec4 = [number, number, number, number]; + +export type Curve = { + points: [Vec3, Vec3, Vec3]; + cacheArcLengths?: number[]; +}; + +export interface CurvePath { + curves: Curve[]; + cacheLengths?: number[]; +} + +export interface CurvedHelixPath extends CurvePath { + torsion?: number; + torsionT0?: number; +} + +export type Palette = { + keyColor: Vec3; + darkCp: number; + lightCp: number; + hueTorsion: number; +}; + +export type NamedPalette = Palette & { name: string }; + +export type PaletteConfig = { + range: [number, number]; + nShades: number; + linearity?: number; + shadeNames?: Record; +}; + +export type Theme = { + backgrounds: { + [paletteId: string]: PaletteConfig; + }; + foregrounds: { + [paletteId: string]: PaletteConfig; + }; +}; + +export type NamedTheme = Theme & { name: string }; + +export type TokenPackageType = 'csscp' | 'json'; + +export interface ThemeCollectionInclude { + [paletteId: string]: number[]; +} + +export type TokenPackageConfig = { + type: TokenPackageType; + selector: string; + include: { + [themeId: string]: { + backgrounds: ThemeCollectionInclude; + foregrounds: ThemeCollectionInclude; + }; + }; +}; diff --git a/packages/react-components/theme-designer/src/components/ColorTokens/getAccessibilityChecker.ts b/packages/react-components/theme-designer/src/components/ColorTokens/getAccessibilityChecker.ts index 5236d01f9ae9a7..7907878e91b07f 100644 --- a/packages/react-components/theme-designer/src/components/ColorTokens/getAccessibilityChecker.ts +++ b/packages/react-components/theme-designer/src/components/ColorTokens/getAccessibilityChecker.ts @@ -1,4 +1,4 @@ -import { contrast, hex_to_sRGB, Vec3 } from '@fluent-blocks/colors'; +import { contrast, hex_to_sRGB, Vec3 } from '../../colors'; import { Theme } from '@fluentui/react-components'; import { accessiblePairs } from './AccessiblePairs'; diff --git a/packages/react-components/theme-designer/src/components/Palette/Palette.tsx b/packages/react-components/theme-designer/src/components/Palette/Palette.tsx index a92d544eec1170..d56cd992c06a6b 100644 --- a/packages/react-components/theme-designer/src/components/Palette/Palette.tsx +++ b/packages/react-components/theme-designer/src/components/Palette/Palette.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { makeStyles, mergeClasses } from '@griffel/react'; import { Button, Caption1, Text } from '@fluentui/react-components'; import { Brands, BrandVariants } from '@fluentui/react-theme'; -import { contrast, hex_to_sRGB } from '@fluent-blocks/colors'; +import { contrast, hex_to_sRGB } from '../../colors'; import { bundleIcon, CopyFilled, CopyRegular } from '@fluentui/react-icons'; import { AppContext } from '../../ThemeDesigner'; import { useContextSelector } from '@fluentui/react-context-selector'; diff --git a/packages/react-components/theme-designer/src/utils/getBrandTokensFromPalette.ts b/packages/react-components/theme-designer/src/utils/getBrandTokensFromPalette.ts index c4aaf778844250..3bb62c703f18f0 100644 --- a/packages/react-components/theme-designer/src/utils/getBrandTokensFromPalette.ts +++ b/packages/react-components/theme-designer/src/utils/getBrandTokensFromPalette.ts @@ -1,5 +1,5 @@ import { BrandVariants } from '@fluentui/react-theme'; -import { Palette, hexColorsFromPalette, hex_to_LCH } from '@fluent-blocks/colors'; +import { Palette, hexColorsFromPalette, hex_to_LCH } from '../colors'; type Options = { darkCp?: number; diff --git a/packages/react-date-time/CHANGELOG.json b/packages/react-date-time/CHANGELOG.json index c77c809b8d4108..3c58f9840f77dd 100644 --- a/packages/react-date-time/CHANGELOG.json +++ b/packages/react-date-time/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react-date-time", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-date-time_v8.7.65", + "version": "8.7.65", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-date-time", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-date-time_v8.7.64", diff --git a/packages/react-date-time/CHANGELOG.md b/packages/react-date-time/CHANGELOG.md index b15b5ce8fef08d..3159f80f7ef289 100644 --- a/packages/react-date-time/CHANGELOG.md +++ b/packages/react-date-time/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react-date-time -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.7.65](https://github.com/microsoft/fluentui/tree/@fluentui/react-date-time_v8.7.65) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-date-time_v8.7.64..@fluentui/react-date-time_v8.7.65) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.7.64](https://github.com/microsoft/fluentui/tree/@fluentui/react-date-time_v8.7.64) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-date-time/package.json b/packages/react-date-time/package.json index 794d252c4109d1..5ad4c0c776390f 100644 --- a/packages/react-date-time/package.json +++ b/packages/react-date-time/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-date-time", - "version": "8.7.64", + "version": "8.7.65", "description": "Date and time related React components for building experiences for Microsoft 365.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -28,7 +28,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/set-version": "^8.2.5", "tslib": "^2.1.0" }, diff --git a/packages/react-docsite-components/CHANGELOG.json b/packages/react-docsite-components/CHANGELOG.json index 624e43e482483d..ce4f36fa4923e3 100644 --- a/packages/react-docsite-components/CHANGELOG.json +++ b/packages/react-docsite-components/CHANGELOG.json @@ -1,6 +1,27 @@ { "name": "@fluentui/react-docsite-components", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-docsite-components_v8.11.30", + "version": "8.11.30", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-docsite-components", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + }, + { + "author": "beachball", + "package": "@fluentui/react-docsite-components", + "comment": "Bump @fluentui/react-monaco-editor to v1.7.65", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-docsite-components_v8.11.29", diff --git a/packages/react-docsite-components/CHANGELOG.md b/packages/react-docsite-components/CHANGELOG.md index 271e6536f97a15..9c0488ac04bac1 100644 --- a/packages/react-docsite-components/CHANGELOG.md +++ b/packages/react-docsite-components/CHANGELOG.md @@ -1,9 +1,19 @@ # Change Log - @fluentui/react-docsite-components -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.11.30](https://github.com/microsoft/fluentui/tree/@fluentui/react-docsite-components_v8.11.30) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-docsite-components_v8.11.29..@fluentui/react-docsite-components_v8.11.30) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) +- Bump @fluentui/react-monaco-editor to v1.7.65 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.11.29](https://github.com/microsoft/fluentui/tree/@fluentui/react-docsite-components_v8.11.29) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-docsite-components/package.json b/packages/react-docsite-components/package.json index f362056453f219..87ea97c45373cc 100644 --- a/packages/react-docsite-components/package.json +++ b/packages/react-docsite-components/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-docsite-components", - "version": "8.11.29", + "version": "8.11.30", "description": "Fluent UI React components for building documentation sites.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -35,14 +35,14 @@ "react-dom": ">=16.8.0 <19.0.0" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/theme": "^2.6.24", "@microsoft/load-themed-styles": "^1.10.26", "@fluentui/example-data": "^8.4.6", "@fluentui/public-docsite-setup": "^0.3.17", "@fluentui/react-hooks": "^8.6.19", "@fluentui/set-version": "^8.2.5", - "@fluentui/react-monaco-editor": "^1.7.64", + "@fluentui/react-monaco-editor": "^1.7.65", "color-check": "0.0.2", "markdown-to-jsx": "^7.0.0", "office-ui-fabric-core": "^11.0.0", diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index c0dce742617eee..1a431937e90b61 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -25,18 +25,18 @@ "@fluentui/scripts-tasks": "*" }, "dependencies": { - "@fluentui/azure-themes": "^8.5.68", + "@fluentui/azure-themes": "^8.5.69", "@fluentui/date-time-utilities": "^8.5.5", "@fluentui/dom-utilities": "^2.2.5", "@fluentui/example-data": "^8.4.6", "@fluentui/font-icons-mdl2": "^8.5.12", "@fluentui/foundation-legacy": "^8.2.32", "@fluentui/merge-styles": "^8.5.6", - "@fluentui/react": "^8.106.5", - "@fluentui/react-cards": "^0.205.64", - "@fluentui/react-charting": "^5.16.4", - "@fluentui/react-docsite-components": "^8.11.29", - "@fluentui/react-experiments": "^8.14.59", + "@fluentui/react": "^8.106.6", + "@fluentui/react-cards": "^0.205.65", + "@fluentui/react-charting": "^5.16.5", + "@fluentui/react-docsite-components": "^8.11.30", + "@fluentui/react-experiments": "^8.14.60", "@fluentui/react-file-type-icons": "^8.8.12", "@fluentui/react-focus": "^8.8.18", "@fluentui/react-hooks": "^8.6.19", @@ -44,7 +44,7 @@ "@fluentui/scheme-utilities": "^8.3.25", "@fluentui/style-utilities": "^8.9.5", "@fluentui/theme": "^2.6.24", - "@fluentui/theme-samples": "^8.7.64", + "@fluentui/theme-samples": "^8.7.65", "@fluentui/utilities": "^8.13.8", "@microsoft/load-themed-styles": "^1.10.26", "d3-fetch": "3.0.1", diff --git a/packages/react-experiments/CHANGELOG.json b/packages/react-experiments/CHANGELOG.json index f7e0d944b6e705..a3565a38884911 100644 --- a/packages/react-experiments/CHANGELOG.json +++ b/packages/react-experiments/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react-experiments", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-experiments_v8.14.60", + "version": "8.14.60", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-experiments", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-experiments_v8.14.59", diff --git a/packages/react-experiments/CHANGELOG.md b/packages/react-experiments/CHANGELOG.md index 53dc7ef2c9c538..9475833c16db58 100644 --- a/packages/react-experiments/CHANGELOG.md +++ b/packages/react-experiments/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react-experiments -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.14.60](https://github.com/microsoft/fluentui/tree/@fluentui/react-experiments_v8.14.60) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-experiments_v8.14.59..@fluentui/react-experiments_v8.14.60) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.14.59](https://github.com/microsoft/fluentui/tree/@fluentui/react-experiments_v8.14.59) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-experiments/package.json b/packages/react-experiments/package.json index e68e4e3131f799..54f4ccdc0d4717 100644 --- a/packages/react-experiments/package.json +++ b/packages/react-experiments/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-experiments", - "version": "8.14.59", + "version": "8.14.60", "description": "Experimental React components for building experiences for Microsoft 365.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -39,7 +39,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/theme": "^2.6.24", "@microsoft/load-themed-styles": "^1.10.26", "@fluentui/example-data": "^8.4.6", diff --git a/packages/react-monaco-editor/CHANGELOG.json b/packages/react-monaco-editor/CHANGELOG.json index 29ec94a8643ac4..b5a31da21489ee 100644 --- a/packages/react-monaco-editor/CHANGELOG.json +++ b/packages/react-monaco-editor/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react-monaco-editor", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/react-monaco-editor_v1.7.65", + "version": "1.7.65", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/react-monaco-editor", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react-monaco-editor_v1.7.64", diff --git a/packages/react-monaco-editor/CHANGELOG.md b/packages/react-monaco-editor/CHANGELOG.md index 6f5f0056cc1690..26f88eed3aa868 100644 --- a/packages/react-monaco-editor/CHANGELOG.md +++ b/packages/react-monaco-editor/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react-monaco-editor -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [1.7.65](https://github.com/microsoft/fluentui/tree/@fluentui/react-monaco-editor_v1.7.65) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-monaco-editor_v1.7.64..@fluentui/react-monaco-editor_v1.7.65) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [1.7.64](https://github.com/microsoft/fluentui/tree/@fluentui/react-monaco-editor_v1.7.64) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react-monaco-editor/package.json b/packages/react-monaco-editor/package.json index 52a4ddf0c8de87..720ebc189b9e88 100644 --- a/packages/react-monaco-editor/package.json +++ b/packages/react-monaco-editor/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react-monaco-editor", - "version": "1.7.64", + "version": "1.7.65", "description": "Live React example editing using monaco", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -30,7 +30,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@microsoft/load-themed-styles": "^1.10.26", "@fluentui/example-data": "^8.4.6", "@fluentui/monaco-editor": "^1.3.5", diff --git a/packages/react/CHANGELOG.json b/packages/react/CHANGELOG.json index d2257bde675f46..0c21b8d21be3f2 100644 --- a/packages/react/CHANGELOG.json +++ b/packages/react/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/react", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:48 GMT", + "tag": "@fluentui/react_v8.106.6", + "version": "8.106.6", + "comments": { + "patch": [ + { + "author": "mgodbolt@microsoft.com", + "package": "@fluentui/react", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5", + "comment": "fix: Panel async and events to work in concurrent mode" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/react_v8.106.5", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index b12d6caec5d437..42d4a6bb20742d 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/react -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:48 GMT and should not be manually modified. +## [8.106.6](https://github.com/microsoft/fluentui/tree/@fluentui/react_v8.106.6) + +Tue, 14 Mar 2023 07:38:48 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react_v8.106.5..@fluentui/react_v8.106.6) + +### Patches + +- fix: Panel async and events to work in concurrent mode ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by mgodbolt@microsoft.com) + ## [8.106.5](https://github.com/microsoft/fluentui/tree/@fluentui/react_v8.106.5) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/react/package.json b/packages/react/package.json index 72a172a15af793..24719fb994ce93 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/react", - "version": "8.106.5", + "version": "8.106.6", "description": "Reusable React components for building web experiences.", "main": "lib-commonjs/index.js", "module": "lib/index.js", diff --git a/packages/react/src/components/Panel/Panel.base.tsx b/packages/react/src/components/Panel/Panel.base.tsx index 4166b5130eaf8e..8438e2ad06a36b 100644 --- a/packages/react/src/components/Panel/Panel.base.tsx +++ b/packages/react/src/components/Panel/Panel.base.tsx @@ -88,8 +88,6 @@ export class PanelBase extends React.Component impleme const { allowTouchBodyScroll = false } = this.props; this._allowTouchBodyScroll = allowTouchBodyScroll; - this._async = new Async(this); - this._events = new EventGroup(this); initializeComponentRef(this); warnDeprecations(COMPONENT_NAME, props, { @@ -107,6 +105,9 @@ export class PanelBase extends React.Component impleme } public componentDidMount(): void { + this._async = new Async(this); + this._events = new EventGroup(this); + this._events.on(window, 'resize', this._updateFooterPosition); if (this._shouldListenForOuterClick(this.props)) { @@ -428,7 +429,7 @@ export class PanelBase extends React.Component impleme this._animationCallback = this._async.setTimeout(() => { this.setState({ visibility: newVisibilityState }); - this._onTransitionComplete(); + this._onTransitionComplete(newVisibilityState); }, 200); }; @@ -442,14 +443,13 @@ export class PanelBase extends React.Component impleme this.dismiss(ev); }; - private _onTransitionComplete = (): void => { + private _onTransitionComplete = (newVisibilityState: PanelVisibilityState): void => { this._updateFooterPosition(); - - if (this.state.visibility === PanelVisibilityState.open && this.props.onOpened) { + if (newVisibilityState === PanelVisibilityState.open && this.props.onOpened) { this.props.onOpened(); } - if (this.state.visibility === PanelVisibilityState.closed && this.props.onDismissed) { + if (newVisibilityState === PanelVisibilityState.closed && this.props.onDismissed) { this.props.onDismissed(); } }; diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 190def5e4c5b95..a9683f4846b1e7 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -22,13 +22,13 @@ "@fluentui/scripts-tasks": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/theme": "^2.6.24", "@storybook/addon-knobs": "6.4.0", "@storybook/addon-essentials": "6.5.15", "@storybook/addons": "6.5.15", - "@fluentui/azure-themes": "^8.5.68", - "@fluentui/theme-samples": "^8.7.64", + "@fluentui/azure-themes": "^8.5.69", + "@fluentui/theme-samples": "^8.7.65", "tslib": "^2.1.0" }, "peerDependencies": { diff --git a/packages/theme-samples/CHANGELOG.json b/packages/theme-samples/CHANGELOG.json index 08deffc05e91fb..c5fec325d8a00a 100644 --- a/packages/theme-samples/CHANGELOG.json +++ b/packages/theme-samples/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/theme-samples", "entries": [ + { + "date": "Tue, 14 Mar 2023 07:38:49 GMT", + "tag": "@fluentui/theme-samples_v8.7.65", + "version": "8.7.65", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/theme-samples", + "comment": "Bump @fluentui/react to v8.106.6", + "commit": "56f81e3cd545ee3c307825335d4643b06a877ce5" + } + ] + } + }, { "date": "Fri, 10 Mar 2023 07:38:25 GMT", "tag": "@fluentui/theme-samples_v8.7.64", diff --git a/packages/theme-samples/CHANGELOG.md b/packages/theme-samples/CHANGELOG.md index fe644cfdee6ccb..03dbcf9c9b29b2 100644 --- a/packages/theme-samples/CHANGELOG.md +++ b/packages/theme-samples/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/theme-samples -This log was last generated on Fri, 10 Mar 2023 07:38:25 GMT and should not be manually modified. +This log was last generated on Tue, 14 Mar 2023 07:38:49 GMT and should not be manually modified. +## [8.7.65](https://github.com/microsoft/fluentui/tree/@fluentui/theme-samples_v8.7.65) + +Tue, 14 Mar 2023 07:38:49 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/theme-samples_v8.7.64..@fluentui/theme-samples_v8.7.65) + +### Patches + +- Bump @fluentui/react to v8.106.6 ([PR #26749](https://github.com/microsoft/fluentui/pull/26749) by beachball) + ## [8.7.64](https://github.com/microsoft/fluentui/tree/@fluentui/theme-samples_v8.7.64) Fri, 10 Mar 2023 07:38:25 GMT diff --git a/packages/theme-samples/package.json b/packages/theme-samples/package.json index 24bba31a40fa5a..c0c47b0b31549b 100644 --- a/packages/theme-samples/package.json +++ b/packages/theme-samples/package.json @@ -1,6 +1,6 @@ { "name": "@fluentui/theme-samples", - "version": "8.7.64", + "version": "8.7.65", "description": "Sample themes for use with Fabric components.", "main": "lib-commonjs/index.js", "module": "lib/index.js", @@ -27,7 +27,7 @@ "@fluentui/scripts-webpack": "*" }, "dependencies": { - "@fluentui/react": "^8.106.5", + "@fluentui/react": "^8.106.6", "@fluentui/set-version": "^8.2.5", "@fluentui/scheme-utilities": "^8.3.25", "tslib": "^2.1.0" diff --git a/rfcs/react-components/components/portal-mount-node.md b/rfcs/react-components/components/portal-mount-node.md new file mode 100644 index 00000000000000..df7ee1f224c022 --- /dev/null +++ b/rfcs/react-components/components/portal-mount-node.md @@ -0,0 +1,84 @@ +# RFC: Extend `mountNode` prop in `Portal` + +[@layeshifter](https://github.com/layershifter) + +## Summary + +This RFC proposes extending the `mountNode` prop in the `Portal` component and its underlying components such as `Tooltip` and `Popup` to accept an object. + +## Background + +The `Portal` component has a `mountNode` prop that allows customizing the element to which the `Portal` will be attached. However, there is no way to customize classes applied to that element. Customization is needed to apply styles such as custom `z-index`es. We need to be able to customize the `mountNode` element to apply this type of custom styles ([microsoft/fluentui#26758](https://github.com/microsoft/fluentui/issues/26758)). + +## Problem statement + +Currently, there is no way to customize classes applied to that element. + +## Detailed Design or Proposal + +The proposal is to allow passing objects to the `mountNode` prop. This can be achieved by extending the `mountNode` prop to accept an object, which can be one of the following: + +```tsx +function App() { + return ( + <> + {/* Current usage, already exists */} + + + {/* Proposed usages */} + + + + + ); +} +``` + +### Pros and Cons + +- 👍 Similar to `positioning` prop. +- 👍 Not a breaking change. +- 👎 May create the impression that `mountNode` is a slot. + +## Discarded Solutions + +### Deprecate `mountNode`, add `portal` prop + +```tsx +function App() { + return ( + <> + + + + + ); +} +``` + +### Pros and Cons + +- 👍 Similar to the `positioning` prop. +- 👎 Can create an impression that `portal` is a slot. +- 👎 Creates a breaking change in the future. +- 👎 `` is not obvious as ``. + +### Add `mountNodeClassName` prop + +```tsx +function App() { + return ( + <> + {/* Current usage, already exists */} + + + {/* Proposed usages */} + + + ); +} +``` + +### Pros and Cons + +- 👎 Does not scale: what if we will need to add another property to manage? diff --git a/yarn.lock b/yarn.lock index c14ab5aa8f65c6..fab731dfed6faa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1630,11 +1630,6 @@ dependencies: "@floating-ui/core" "^1.2.0" -"@fluent-blocks/colors@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@fluent-blocks/colors/-/colors-9.2.0.tgz#8aeb30f93f5f827b2842b9cff43541a87e304e18" - integrity sha512-NgK+n4IHRj35ttJjN3UBF8oqk2ZT8xwCdT52+nTXvVSL5yHDtKwQlgb+zvrNFhYNajbqEeqYdj4QA3XSkytLww== - "@fluentui/dom-utilities@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@fluentui/dom-utilities/-/dom-utilities-1.1.1.tgz#b0bbab665fe726f245800bb9e7883b1ceb54248b"