diff --git a/packages/react-components/theme-designer/src/components/AccessibilityChecker/AccessibilityChecker.stories.tsx b/packages/react-components/theme-designer/src/components/AccessibilityChecker/AccessibilityChecker.stories.tsx index 9c5f3811a47886..02ffd923c7d4a5 100644 --- a/packages/react-components/theme-designer/src/components/AccessibilityChecker/AccessibilityChecker.stories.tsx +++ b/packages/react-components/theme-designer/src/components/AccessibilityChecker/AccessibilityChecker.stories.tsx @@ -1,9 +1,9 @@ import { AccessibilityChecker } from './AccessibilityChecker'; import { getBrandTokensFromPalette } from '../../utils/getBrandTokensFromPalette'; -import { createDarkTheme } from '@fluentui/react-components'; +import { createLightTheme } from '@fluentui/react-components'; export default { component: AccessibilityChecker }; const brand = getBrandTokensFromPalette('#006bc7'); -const lightTheme = createDarkTheme(brand); +const lightTheme = createLightTheme(brand); export const Default = { args: { theme: lightTheme } }; diff --git a/packages/react-components/theme-designer/src/components/ColorTokens/ColorTokens.stories.tsx b/packages/react-components/theme-designer/src/components/ColorTokens/ColorTokens.stories.tsx index 11263348a9684e..1c5c902ae910db 100644 --- a/packages/react-components/theme-designer/src/components/ColorTokens/ColorTokens.stories.tsx +++ b/packages/react-components/theme-designer/src/components/ColorTokens/ColorTokens.stories.tsx @@ -1,9 +1,7 @@ import { ColorTokens } from './ColorTokens'; import { getBrandTokensFromPalette } from '../../utils/getBrandTokensFromPalette'; -import { createDarkTheme } from '@fluentui/react-components'; export default { component: ColorTokens }; const brand = getBrandTokensFromPalette('#006bc7'); -const lightTheme = createDarkTheme(brand); -export const Default = { args: { theme: lightTheme } }; +export const Default = { args: { brand: brand, isDark: false } }; diff --git a/packages/react-components/theme-designer/src/components/Content/Content.stories.tsx b/packages/react-components/theme-designer/src/components/Content/Content.stories.tsx index 6733f60a6eb375..91efeda4bb4261 100644 --- a/packages/react-components/theme-designer/src/components/Content/Content.stories.tsx +++ b/packages/react-components/theme-designer/src/components/Content/Content.stories.tsx @@ -1,10 +1,9 @@ import { Content } from './Content'; import { getBrandTokensFromPalette } from '../../utils/getBrandTokensFromPalette'; -import { createLightTheme, createDarkTheme } from '@fluentui/react-components'; +import { createLightTheme } from '@fluentui/react-components'; export default { component: Content }; const brand = getBrandTokensFromPalette('#006BC7'); const lightTheme = createLightTheme(brand); -const darkTheme = createDarkTheme(brand); -export const Default = { args: { brand: brand, darkTheme: darkTheme, lightTheme: lightTheme } }; +export const Default = { args: { brand: brand, theme: lightTheme, isDark: false } }; diff --git a/packages/react-components/theme-designer/src/components/ExportButton/ExportLink.tsx b/packages/react-components/theme-designer/src/components/ExportButton/ExportLink.tsx index 65944269fad35d..2e62472d5694e8 100644 --- a/packages/react-components/theme-designer/src/components/ExportButton/ExportLink.tsx +++ b/packages/react-components/theme-designer/src/components/ExportButton/ExportLink.tsx @@ -9,292 +9,302 @@ export interface ExportLinkProps { overrides: Partial; } +const defaultFileToPreview = encodeURIComponent('/index.tsx'); + export const ExportLink: React.FC = props => { const { brand, isDark, overrides } = props; - const defaultFileToPreview = encodeURIComponent('/index.tsx'); - const codeSandboxParameters = getParameters({ - files: { - 'example.tsx': { - isBinary: false, - content: dedent` - import * as React from 'react'; - import { makeStyles, makeStaticStyles, mergeClasses, shorthands } from '@griffel/react'; - import { - tokens, - Body1, - Title3, - TabList, - Tab, - Input, - Button, - Caption1, - Menu, - MenuTrigger, - MenuList, - MenuButton, - MenuItemCheckbox, - MenuPopover, - Slider, - Badge, - Switch, - Radio, - RadioGroup, - Checkbox, - Avatar, - Theme, - } from '@fluentui/react-components'; - import { - SearchRegular, - bundleIcon, - CutRegular, - CutFilled, - ClipboardPasteRegular, - ClipboardPasteFilled, - EditRegular, - EditFilled, - ChevronRightRegular, - MeetNowRegular, - MeetNowFilled, - CalendarLtrFilled, - CalendarLtrRegular, - } from '@fluentui/react-icons'; - export interface ContentProps { - className?: string; - theme: Theme; - } + const content = dedent` + import * as React from 'react'; + import { makeStyles, makeStaticStyles, mergeClasses, shorthands } from '@griffel/react'; + import { + tokens, + Body1, + Title3, + TabList, + Tab, + Input, + Button, + Caption1, + Menu, + MenuTrigger, + MenuList, + MenuButton, + MenuItemCheckbox, + MenuPopover, + Slider, + Badge, + Switch, + Radio, + RadioGroup, + Checkbox, + Avatar, + Theme, + } from '@fluentui/react-components'; + import { + SearchRegular, + bundleIcon, + CutRegular, + CutFilled, + ClipboardPasteRegular, + ClipboardPasteFilled, + EditRegular, + EditFilled, + ChevronRightRegular, + MeetNowRegular, + MeetNowFilled, + CalendarLtrFilled, + CalendarLtrRegular, + } from '@fluentui/react-icons'; + + export interface ContentProps { + className?: string; + theme: Theme; + } + + const useStaticStyles = makeStaticStyles({ + body: { + position: "fixed", + margin: "0px", + top: "0px", + left: "0px", + height: "100vh" + } + }); + + const useStyles = makeStyles({ + root: { + height: "100vh", + display: 'grid', + alignItems: 'start', + justifyContent: 'center', + gridTemplateColumns: 'repeat(3, 1fr)', + gridTemplateRows: 'auto', + gridColumnGap: tokens.spacingHorizontalXXXL, + }, + col1: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'left', + flexDirection: 'column', + flexGrow: 1, + ...shorthands.gap(tokens.spacingVerticalL), + }, + col2: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + ...shorthands.gap(tokens.spacingVerticalL), + }, + col3: { + display: 'grid', + gridTemplateColumns: '1fr 1fr', + gridTemplateRows: 'repeat(4, auto)', + gridRowGap: tokens.spacingVerticalS, + gridColumnGap: tokens.spacingHorizontalS, + justifyContent: 'center', + alignItems: 'center', + }, + twoCol: { + gridColumnStart: 1, + gridColumnEnd: 3, + }, + icons: { + display: 'grid', + gridTemplateColumns: 'auto auto', + gridTemplateRows: 'auto auto', + gridRowGap: tokens.spacingVerticalS, + gridColumnGap: tokens.spacingHorizontalS, + justifyContent: 'center', + }, + twoRow: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + }); + + export const Column1 = () => { + const styles = useStyles(); + useStaticStyles(); + return ( +
+ Make an impression + + Make a big impression with this clean, modern, and mobile-friendly site. Use it to communicate + information to people inside or outside your team. Share your ideas, results, and more in this + visually compelling format. + + +
+ ); + }; - const useStaticStyles = makeStaticStyles({ - body: { - position: "fixed", - margin: "0px", - top: "0px", - left: "0px", - height: "100vh" + export const DemoMenu = () => { + const CutIcon = bundleIcon(CutFilled, CutRegular); + const PasteIcon = bundleIcon(ClipboardPasteFilled, ClipboardPasteRegular); + const EditIcon = bundleIcon(EditFilled, EditRegular); + return ( + + + Select + + + + } name="edit" value="cut"> + Cut + + } name="edit" value="paste"> + Paste + + } name="edit" value="edit"> + Edit + + + + + ); + }; + + export const Column2 = () => { + const styles = useStyles(); + return ( +
+ + Home + Pages + Documents + + } size="small" /> } - }); + /> + +
+ ); + }; - const useStyles = makeStyles({ - root: { - height: "100vh", - display: 'grid', - alignItems: 'start', - justifyContent: 'center', - gridTemplateColumns: 'repeat(3, 1fr)', - gridTemplateRows: 'auto', - gridColumnGap: tokens.spacingHorizontalXXXL, - }, - col1: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'left', - flexDirection: 'column', - flexGrow: 1, - ...shorthands.gap(tokens.spacingVerticalL), - }, - col2: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - ...shorthands.gap(tokens.spacingVerticalL), - }, - col3: { - display: 'grid', - gridTemplateColumns: '1fr 1fr', - gridTemplateRows: 'repeat(4, auto)', - gridRowGap: tokens.spacingVerticalS, - gridColumnGap: tokens.spacingHorizontalS, - justifyContent: 'center', - alignItems: 'center', - }, - twoCol: { - gridColumnStart: 1, - gridColumnEnd: 3, - }, - icons: { - display: 'grid', - gridTemplateColumns: 'auto auto', - gridTemplateRows: 'auto auto', - gridRowGap: tokens.spacingVerticalS, - gridColumnGap: tokens.spacingHorizontalS, - justifyContent: 'center', - }, - twoRow: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }, - }); + export const DemoIcons = () => { + const styles = useStyles(); + const MeetNowIcon = bundleIcon(MeetNowFilled, MeetNowRegular); + const CalendarLtrIcon = bundleIcon(CalendarLtrFilled, CalendarLtrRegular); + return ( +
+ } /> + } /> + } /> + } /> +
+ ); + }; - export const Column1 = () => { - const styles = useStyles(); - useStaticStyles(); - return ( -
- Make an impression - - Make a big impression with this clean, modern, and mobile-friendly site. Use it to communicate - information to people inside or outside your team. Share your ideas, results, and more in this - visually compelling format. - - -
- ); - }; + export const Column3 = () => { + const styles = useStyles(); + return ( +
+ + + + +
+ + +
+
+ + +
+
+ + + + +
+
+ ); + }; - export const DemoMenu = () => { - const CutIcon = bundleIcon(CutFilled, CutRegular); - const PasteIcon = bundleIcon(ClipboardPasteFilled, ClipboardPasteRegular); - const EditIcon = bundleIcon(EditFilled, EditRegular); - return ( - - - Select - - - - } name="edit" value="cut"> - Cut - - } name="edit" value="paste"> - Paste - - } name="edit" value="edit"> - Edit - - - - - ); - }; + export const Example: React.FC = props => { + const styles = useStyles(); + return ( +
+ Examples +
+ + + +
+
+ ); + }; + `; - export const Column2 = () => { - const styles = useStyles(); - return ( -
- - Home - Pages - Documents - - } size="small" /> - } - /> - -
- ); - }; + const indexHtmlContent = '
'; - export const DemoIcons = () => { - const styles = useStyles(); - const MeetNowIcon = bundleIcon(MeetNowFilled, MeetNowRegular); - const CalendarLtrIcon = bundleIcon(CalendarLtrFilled, CalendarLtrRegular); - return ( -
- } /> - } /> - } /> - } /> -
- ); - }; + const createIndexContent = dedent` + import * as ReactDOM from 'react-dom'; + import { FluentProvider, ${isDark ? 'createDarkTheme' : 'createLightTheme'} } from '@fluentui/react-components'; + import type { BrandVariants, Theme } from '@fluentui/react-theme'; + import { Example } from './example'; - export const Column3 = () => { - const styles = useStyles(); - return ( -
- - - - -
- - -
-
- - -
-
- - - - -
-
- ); - }; + const brand: BrandVariants = ${JSON.stringify(brand)}; - export const Example: React.FC = props => { - const styles = useStyles(); - return ( -
- Examples -
- - - -
-
- ); - }; - `, - }, - 'index.html': { - isBinary: false, - content: '
', - }, - 'index.tsx': { - isBinary: false, - content: dedent` - import * as ReactDOM from 'react-dom'; - import { FluentProvider, ${ - isDark ? 'createDarkTheme' : 'createLightTheme' - } } from '@fluentui/react-components'; - import type { BrandVariants, Theme } from '@fluentui/react-theme'; - import { Example } from './example'; + const overrides: Partial = ${JSON.stringify(overrides)}; - const brand: BrandVariants = ${JSON.stringify(brand)}; + const theme: Theme = { ...${isDark ? 'createDarkTheme' : 'createLightTheme'}(brand), ...overrides }; - const overrides: Partial = ${JSON.stringify(overrides)}; + ReactDOM.render( + + + , + document.getElementById('root'), + ); + `; - const theme: Theme = { ...${isDark ? 'createDarkTheme' : 'createLightTheme'}(brand), ...overrides }; + const packageContent = dedent` + {"dependencies":{"@fluentui/react-components":"rc","react":"^17","react-dom":"^17","react-scripts":"latest"}} + `; - ReactDOM.render( - - - , - document.getElementById('root'), - ); - `, - }, - 'package.json': { - isBinary: false, - content: dedent` - {"dependencies":{"@fluentui/react-components":"rc","react":"^17","react-dom":"^17","react-scripts":"latest"}} - `, + const link = React.useMemo(() => { + const codeSandboxParameters = getParameters({ + files: { + 'example.tsx': { + isBinary: false, + content: content, + }, + 'index.html': { + isBinary: false, + content: indexHtmlContent, + }, + 'index.tsx': { + isBinary: false, + content: createIndexContent, + }, + 'package.json': { + isBinary: false, + content: packageContent, + }, }, - }, - }); + }); - const link = `https://codesandbox.io/api/v1/sandboxes/define?parameters=${codeSandboxParameters}&query=file%3D${defaultFileToPreview}`; + return `https://codesandbox.io/api/v1/sandboxes/define?parameters=${codeSandboxParameters}&query=file%3D${defaultFileToPreview}`; + }, [content, createIndexContent, packageContent]); return (
- + Preview theme in CodeSandbox
diff --git a/packages/react-components/theme-designer/src/components/Sidebar/EditTab.tsx b/packages/react-components/theme-designer/src/components/Sidebar/EditTab.tsx index a5675e29d99015..663b629f984df4 100644 --- a/packages/react-components/theme-designer/src/components/Sidebar/EditTab.tsx +++ b/packages/react-components/theme-designer/src/components/Sidebar/EditTab.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { makeStyles, shorthands } from '@griffel/react'; import { Label, Input, Switch, Slider, tokens } from '@fluentui/react-components'; +import { useDebounce } from '../../utils/useDebounce'; import type { CustomAttributes, DispatchTheme } from '../../useThemeDesignerReducer'; @@ -52,7 +53,7 @@ export const EditTab: React.FC = props => { const initialForm: CustomAttributes = formState; - const formReducer = (state: CustomAttributes, action: { attributes: CustomAttributes; type: string }) => { + const formReducer = (state: CustomAttributes, action: { attributes: CustomAttributes }) => { setFormState(action.attributes); dispatchState({ ...form, type: 'Custom', customAttributes: action.attributes, overrides: {} }); return action.attributes; @@ -60,20 +61,26 @@ export const EditTab: React.FC = props => { const [form, dispatchForm] = React.useReducer(formReducer, initialForm); + const [lastForm, setLastForm] = React.useState(initialForm); + const debouncedForm = useDebounce(lastForm, 10); + React.useEffect(() => { + dispatchForm({ attributes: debouncedForm }); + }, [debouncedForm]); + const toggleTheme = () => { - dispatchForm({ attributes: { ...form, isDark: !form.isDark }, type: 'isDark' }); + setLastForm({ ...form, ...form, isDark: !form.isDark }); }; const handleOnChange = (e: React.ChangeEvent) => { - dispatchForm({ attributes: { ...form, keyColor: e.target.value }, type: 'keyColor' }); + setLastForm({ ...form, keyColor: e.target.value }); }; const handleHueTorsionChange = (e: React.ChangeEvent) => { - dispatchForm({ attributes: { ...form, hueTorsion: parseInt(e.target.value, 10) / 10 }, type: 'hueTorsion' }); + setLastForm({ ...form, hueTorsion: parseInt(e.target.value, 10) }); }; const handleLightCpChange = (e: React.ChangeEvent) => { - dispatchForm({ attributes: { ...form, lightCp: parseInt(e.target.value, 10) / 100 }, type: 'lightCp' }); + setLastForm({ ...form, lightCp: parseInt(e.target.value, 10) }); }; const handleDarkCpChange = (e: React.ChangeEvent) => { - dispatchForm({ attributes: { ...form, darkCp: parseInt(e.target.value, 10) / 100 }, type: 'darkCp' }); + setLastForm({ ...form, darkCp: parseInt(e.target.value, 10) }); }; return ( @@ -95,32 +102,11 @@ export const EditTab: React.FC = props => { - + - + - + ); diff --git a/packages/react-components/theme-designer/src/utils/useDebounce.ts b/packages/react-components/theme-designer/src/utils/useDebounce.ts index f7f054dcc717cd..ce9b2f0a745ba2 100644 --- a/packages/react-components/theme-designer/src/utils/useDebounce.ts +++ b/packages/react-components/theme-designer/src/utils/useDebounce.ts @@ -1,6 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -export function useDebounce(value: string, delay: number) { +export function useDebounce(value: any, delay: number) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = document.defaultView?.setTimeout(() => {