@fluentui/react-storybook-addon 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -2
- package/dist/index.d.ts +100 -0
- package/lib/components/DirectionSwitch.js +26 -0
- package/lib/components/DirectionSwitch.js.map +1 -0
- package/lib/components/ReactStrictMode.js +22 -0
- package/lib/components/ReactStrictMode.js.map +1 -0
- package/lib/components/ThemePicker.js +60 -0
- package/lib/components/ThemePicker.js.map +1 -0
- package/lib/constants.js +4 -0
- package/lib/constants.js.map +1 -0
- package/lib/decorators/withAriaLive.js +18 -0
- package/lib/decorators/withAriaLive.js.map +1 -0
- package/lib/decorators/withFluentProvider.js +48 -0
- package/lib/decorators/withFluentProvider.js.map +1 -0
- package/lib/decorators/withReactStrictMode.js +16 -0
- package/lib/decorators/withReactStrictMode.js.map +1 -0
- package/lib/docs/CopyAsMarkdownButton.js +177 -0
- package/lib/docs/CopyAsMarkdownButton.js.map +1 -0
- package/lib/docs/DirSwitch.js +51 -0
- package/lib/docs/DirSwitch.js.map +1 -0
- package/lib/docs/FluentCanvas.js +21 -0
- package/lib/docs/FluentCanvas.js.map +1 -0
- package/lib/docs/FluentDocsContainer.js +24 -0
- package/lib/docs/FluentDocsContainer.js.map +1 -0
- package/lib/docs/FluentDocsPage.js +308 -0
- package/lib/docs/FluentDocsPage.js.map +1 -0
- package/lib/docs/FluentStory.js +18 -0
- package/lib/docs/FluentStory.js.map +1 -0
- package/lib/docs/ThemePicker.js +61 -0
- package/lib/docs/ThemePicker.js.map +1 -0
- package/lib/docs/Toc.js +130 -0
- package/lib/docs/Toc.js.map +1 -0
- package/lib/docs/index.js +4 -0
- package/lib/docs/index.js.map +1 -0
- package/lib/docs/utils.js +74 -0
- package/lib/docs/utils.js.map +1 -0
- package/lib/hooks.js +16 -0
- package/lib/hooks.js.map +1 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -0
- package/lib/preset/manager.js +25 -0
- package/lib/preset/manager.js.map +1 -0
- package/lib/preset/preview.js +26 -0
- package/lib/preset/preview.js.map +1 -0
- package/lib/theme.js +31 -0
- package/lib/theme.js.map +1 -0
- package/lib/utils/isDecoratorDisabled.js +6 -0
- package/lib/utils/isDecoratorDisabled.js.map +1 -0
- package/lib-commonjs/components/DirectionSwitch.js +37 -0
- package/lib-commonjs/components/DirectionSwitch.js.map +1 -0
- package/lib-commonjs/components/ReactStrictMode.js +33 -0
- package/lib-commonjs/components/ReactStrictMode.js.map +1 -0
- package/lib-commonjs/components/ThemePicker.js +71 -0
- package/lib-commonjs/components/ThemePicker.js.map +1 -0
- package/lib-commonjs/constants.js +28 -0
- package/lib-commonjs/constants.js.map +1 -0
- package/lib-commonjs/decorators/withAriaLive.js +29 -0
- package/lib-commonjs/decorators/withAriaLive.js.map +1 -0
- package/lib-commonjs/decorators/withFluentProvider.js +59 -0
- package/lib-commonjs/decorators/withFluentProvider.js.map +1 -0
- package/lib-commonjs/decorators/withReactStrictMode.js +27 -0
- package/lib-commonjs/decorators/withReactStrictMode.js.map +1 -0
- package/lib-commonjs/docs/CopyAsMarkdownButton.js +185 -0
- package/lib-commonjs/docs/CopyAsMarkdownButton.js.map +1 -0
- package/lib-commonjs/docs/DirSwitch.js +60 -0
- package/lib-commonjs/docs/DirSwitch.js.map +1 -0
- package/lib-commonjs/docs/FluentCanvas.js +29 -0
- package/lib-commonjs/docs/FluentCanvas.js.map +1 -0
- package/lib-commonjs/docs/FluentDocsContainer.js +33 -0
- package/lib-commonjs/docs/FluentDocsContainer.js.map +1 -0
- package/lib-commonjs/docs/FluentDocsPage.js +319 -0
- package/lib-commonjs/docs/FluentDocsPage.js.map +1 -0
- package/lib-commonjs/docs/FluentStory.js +26 -0
- package/lib-commonjs/docs/FluentStory.js.map +1 -0
- package/lib-commonjs/docs/ThemePicker.js +70 -0
- package/lib-commonjs/docs/ThemePicker.js.map +1 -0
- package/lib-commonjs/docs/Toc.js +149 -0
- package/lib-commonjs/docs/Toc.js.map +1 -0
- package/lib-commonjs/docs/index.js +28 -0
- package/lib-commonjs/docs/index.js.map +1 -0
- package/lib-commonjs/docs/utils.js +88 -0
- package/lib-commonjs/docs/utils.js.map +1 -0
- package/lib-commonjs/hooks.js +37 -0
- package/lib-commonjs/hooks.js.map +1 -0
- package/lib-commonjs/index.js +34 -0
- package/lib-commonjs/index.js.map +1 -0
- package/lib-commonjs/preset/manager.js +29 -0
- package/lib-commonjs/preset/manager.js.map +1 -0
- package/lib-commonjs/preset/preview.js +47 -0
- package/lib-commonjs/preset/preview.js.map +1 -0
- package/lib-commonjs/theme.js +49 -0
- package/lib-commonjs/theme.js.map +1 -0
- package/lib-commonjs/utils/isDecoratorDisabled.js +16 -0
- package/lib-commonjs/utils/isDecoratorDisabled.js.map +1 -0
- package/package.json +20 -27
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "withFluentProvider", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return withFluentProvider;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _reactprovider = require("@fluentui/react-provider");
|
|
14
|
+
const _reacttheme = require("@fluentui/react-theme");
|
|
15
|
+
const _theme = require("../theme");
|
|
16
|
+
const _constants = require("../constants");
|
|
17
|
+
const _isDecoratorDisabled = require("../utils/isDecoratorDisabled");
|
|
18
|
+
const themes = {
|
|
19
|
+
'web-light': _reacttheme.webLightTheme,
|
|
20
|
+
'web-dark': _reacttheme.webDarkTheme,
|
|
21
|
+
'teams-light': _reacttheme.teamsLightTheme,
|
|
22
|
+
'teams-dark': _reacttheme.teamsDarkTheme,
|
|
23
|
+
'teams-high-contrast': _reacttheme.teamsHighContrastTheme,
|
|
24
|
+
'teams-light-v21': _reacttheme.teamsLightV21Theme,
|
|
25
|
+
'teams-dark-v21': _reacttheme.teamsDarkV21Theme
|
|
26
|
+
};
|
|
27
|
+
const findTheme = (themeId)=>{
|
|
28
|
+
return themeId ? themes[themeId] : null;
|
|
29
|
+
};
|
|
30
|
+
const withFluentProvider = (StoryFn, context)=>{
|
|
31
|
+
const { globals, parameters } = context;
|
|
32
|
+
const { mode } = parameters;
|
|
33
|
+
if ((0, _isDecoratorDisabled.isDecoratorDisabled)(context, 'FluentProvider')) {
|
|
34
|
+
return StoryFn();
|
|
35
|
+
}
|
|
36
|
+
const isVrTest = mode === 'vr-test';
|
|
37
|
+
var _parameters_dir, _ref;
|
|
38
|
+
const dir = (_ref = (_parameters_dir = parameters.dir) !== null && _parameters_dir !== void 0 ? _parameters_dir : globals[_constants.DIR_ID]) !== null && _ref !== void 0 ? _ref : 'ltr';
|
|
39
|
+
const globalTheme = findTheme(globals[_constants.THEME_ID]);
|
|
40
|
+
const paramTheme = findTheme(parameters.fluentTheme);
|
|
41
|
+
var _ref1;
|
|
42
|
+
const theme = (_ref1 = paramTheme !== null && paramTheme !== void 0 ? paramTheme : globalTheme) !== null && _ref1 !== void 0 ? _ref1 : themes[_theme.defaultTheme.id];
|
|
43
|
+
return /*#__PURE__*/ _react.createElement(_reactprovider.FluentProvider, {
|
|
44
|
+
theme: theme,
|
|
45
|
+
dir: dir
|
|
46
|
+
}, isVrTest ? StoryFn() : /*#__PURE__*/ _react.createElement(FluentExampleContainer, {
|
|
47
|
+
theme: theme
|
|
48
|
+
}, StoryFn()));
|
|
49
|
+
};
|
|
50
|
+
const FluentExampleContainer = (props)=>{
|
|
51
|
+
const { theme } = props;
|
|
52
|
+
const backgroundColor = theme.colorNeutralBackground2;
|
|
53
|
+
return /*#__PURE__*/ _react.createElement("div", {
|
|
54
|
+
style: {
|
|
55
|
+
padding: '48px 24px',
|
|
56
|
+
backgroundColor
|
|
57
|
+
}
|
|
58
|
+
}, props.children);
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/decorators/withFluentProvider.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { FluentProvider } from '@fluentui/react-provider';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport {\n Theme,\n teamsDarkTheme,\n teamsDarkV21Theme,\n teamsHighContrastTheme,\n teamsLightTheme,\n teamsLightV21Theme,\n webDarkTheme,\n webLightTheme,\n} from '@fluentui/react-theme';\nimport { defaultTheme, ThemeIds } from '../theme';\nimport { DIR_ID, THEME_ID } from '../constants';\nimport { FluentStoryContext } from '../hooks';\nimport { isDecoratorDisabled } from '../utils/isDecoratorDisabled';\n\nconst themes: Record<ThemeIds, Theme> = {\n 'web-light': webLightTheme,\n 'web-dark': webDarkTheme,\n 'teams-light': teamsLightTheme,\n 'teams-dark': teamsDarkTheme,\n 'teams-high-contrast': teamsHighContrastTheme,\n 'teams-light-v21': teamsLightV21Theme,\n 'teams-dark-v21': teamsDarkV21Theme,\n} as const;\n\nconst findTheme = (themeId?: ThemeIds) => {\n return themeId ? themes[themeId] : null;\n};\n\nexport const withFluentProvider = (StoryFn: () => JSXElement, context: FluentStoryContext): JSXElement => {\n const { globals, parameters } = context;\n const { mode } = parameters;\n\n if (isDecoratorDisabled(context, 'FluentProvider')) {\n return StoryFn();\n }\n\n const isVrTest = mode === 'vr-test';\n const dir = parameters.dir ?? globals[DIR_ID] ?? 'ltr';\n const globalTheme = findTheme(globals[THEME_ID]);\n const paramTheme = findTheme(parameters.fluentTheme);\n const theme = paramTheme ?? globalTheme ?? themes[defaultTheme.id];\n\n return (\n <FluentProvider theme={theme} dir={dir}>\n {isVrTest ? StoryFn() : <FluentExampleContainer theme={theme}>{StoryFn()}</FluentExampleContainer>}\n </FluentProvider>\n );\n};\n\nconst FluentExampleContainer: React.FC<{ children: React.ReactNode; theme: Theme }> = props => {\n const { theme } = props;\n\n const backgroundColor = theme.colorNeutralBackground2;\n return <div style={{ padding: '48px 24px', backgroundColor }}>{props.children}</div>;\n};\n"],"names":["withFluentProvider","themes","webLightTheme","webDarkTheme","teamsLightTheme","teamsDarkTheme","teamsHighContrastTheme","teamsLightV21Theme","teamsDarkV21Theme","findTheme","themeId","StoryFn","context","globals","parameters","mode","isDecoratorDisabled","isVrTest","dir","DIR_ID","globalTheme","THEME_ID","paramTheme","fluentTheme","theme","defaultTheme","id","FluentProvider","FluentExampleContainer","props","backgroundColor","colorNeutralBackground2","div","style","padding","children"],"mappings":";;;;+BAiCaA;;;eAAAA;;;;iEAjCU;+BAEQ;4BAWxB;uBACgC;2BACN;qCAEG;AAEpC,MAAMC,SAAkC;IACtC,aAAaC,yBAAa;IAC1B,YAAYC,wBAAY;IACxB,eAAeC,2BAAe;IAC9B,cAAcC,0BAAc;IAC5B,uBAAuBC,kCAAsB;IAC7C,mBAAmBC,8BAAkB;IACrC,kBAAkBC,6BAAiB;AACrC;AAEA,MAAMC,YAAY,CAACC;IACjB,OAAOA,UAAUT,MAAM,CAACS,QAAQ,GAAG;AACrC;AAEO,MAAMV,qBAAqB,CAACW,SAA2BC;IAC5D,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAE,GAAGF;IAChC,MAAM,EAAEG,IAAI,EAAE,GAAGD;IAEjB,IAAIE,IAAAA,wCAAmB,EAACJ,SAAS,mBAAmB;QAClD,OAAOD;IACT;IAEA,MAAMM,WAAWF,SAAS;QACdD,iBAAAA;IAAZ,MAAMI,MAAMJ,CAAAA,OAAAA,CAAAA,kBAAAA,WAAWI,GAAG,cAAdJ,6BAAAA,kBAAkBD,OAAO,CAACM,iBAAM,CAAC,cAAjCL,kBAAAA,OAAqC;IACjD,MAAMM,cAAcX,UAAUI,OAAO,CAACQ,mBAAQ,CAAC;IAC/C,MAAMC,aAAab,UAAUK,WAAWS,WAAW;QACrCD;IAAd,MAAME,QAAQF,CAAAA,QAAAA,uBAAAA,wBAAAA,aAAcF,yBAAdE,mBAAAA,QAA6BrB,MAAM,CAACwB,mBAAY,CAACC,EAAE,CAAC;IAElE,qBACE,qBAACC,6BAAc;QAACH,OAAOA;QAAON,KAAKA;OAChCD,WAAWN,0BAAY,qBAACiB;QAAuBJ,OAAOA;OAAQb;AAGrE;AAEA,MAAMiB,yBAAgFC,CAAAA;IACpF,MAAM,EAAEL,KAAK,EAAE,GAAGK;IAElB,MAAMC,kBAAkBN,MAAMO,uBAAuB;IACrD,qBAAO,qBAACC;QAAIC,OAAO;YAAEC,SAAS;YAAaJ;QAAgB;OAAID,MAAMM,QAAQ;AAC/E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "withReactStrictMode", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return withReactStrictMode;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _constants = require("../constants");
|
|
14
|
+
const _isDecoratorDisabled = require("../utils/isDecoratorDisabled");
|
|
15
|
+
const withReactStrictMode = (StoryFn, context)=>{
|
|
16
|
+
if ((0, _isDecoratorDisabled.isDecoratorDisabled)(context, 'ReactStrictMode')) {
|
|
17
|
+
return StoryFn();
|
|
18
|
+
}
|
|
19
|
+
var _context_globals_STRICT_MODE_ID;
|
|
20
|
+
const isActive = (_context_globals_STRICT_MODE_ID = context.globals[_constants.STRICT_MODE_ID]) !== null && _context_globals_STRICT_MODE_ID !== void 0 ? _context_globals_STRICT_MODE_ID : false;
|
|
21
|
+
return /*#__PURE__*/ _react.createElement(StrictModeWrapper, {
|
|
22
|
+
strictMode: isActive
|
|
23
|
+
}, StoryFn());
|
|
24
|
+
};
|
|
25
|
+
const StrictModeWrapper = (props)=>{
|
|
26
|
+
return props.strictMode ? /*#__PURE__*/ _react.createElement(_react.StrictMode, null, props.children) : props.children;
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/decorators/withReactStrictMode.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { JSXElement } from '@fluentui/react-utilities';\n\nimport { STRICT_MODE_ID } from '../constants';\nimport { FluentStoryContext } from '../hooks';\nimport { isDecoratorDisabled } from '../utils/isDecoratorDisabled';\n\nexport const withReactStrictMode = (StoryFn: () => JSXElement, context: FluentStoryContext): JSXElement => {\n if (isDecoratorDisabled(context, 'ReactStrictMode')) {\n return StoryFn();\n }\n\n const isActive = context.globals[STRICT_MODE_ID] ?? false;\n\n return <StrictModeWrapper strictMode={isActive}>{StoryFn()}</StrictModeWrapper>;\n};\n\nconst StrictModeWrapper = (props: { strictMode: boolean; children: React.ReactElement }) => {\n return props.strictMode ? <React.StrictMode>{props.children}</React.StrictMode> : props.children;\n};\n"],"names":["withReactStrictMode","StoryFn","context","isDecoratorDisabled","isActive","globals","STRICT_MODE_ID","StrictModeWrapper","strictMode","props","React","StrictMode","children"],"mappings":";;;;+BAOaA;;;eAAAA;;;;iEAPU;2BAGQ;qCAEK;AAE7B,MAAMA,sBAAsB,CAACC,SAA2BC;IAC7D,IAAIC,IAAAA,wCAAmB,EAACD,SAAS,oBAAoB;QACnD,OAAOD;IACT;QAEiBC;IAAjB,MAAME,WAAWF,CAAAA,kCAAAA,QAAQG,OAAO,CAACC,yBAAc,CAAC,cAA/BJ,6CAAAA,kCAAmC;IAEpD,qBAAO,qBAACK;QAAkBC,YAAYJ;OAAWH;AACnD;AAEA,MAAMM,oBAAoB,CAACE;IACzB,OAAOA,MAAMD,UAAU,iBAAG,qBAACE,OAAMC,UAAU,QAAEF,MAAMG,QAAQ,IAAuBH,MAAMG,QAAQ;AAClG"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "CopyAsMarkdownButton", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return CopyAsMarkdownButton;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _reactbutton = require("@fluentui/react-button");
|
|
14
|
+
const _reactmenu = require("@fluentui/react-menu");
|
|
15
|
+
const _reactspinner = require("@fluentui/react-spinner");
|
|
16
|
+
const _reacttoast = require("@fluentui/react-toast");
|
|
17
|
+
const _reactutilities = require("@fluentui/react-utilities");
|
|
18
|
+
const _react1 = require("@griffel/react");
|
|
19
|
+
const _reacticons = require("@fluentui/react-icons");
|
|
20
|
+
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
21
|
+
const MarkdownIcon = (0, _reacticons.bundleIcon)(_reacticons.MarkdownFilled, _reacticons.MarkdownRegular);
|
|
22
|
+
const useStyles = (0, _react1.makeStyles)({
|
|
23
|
+
button: {
|
|
24
|
+
marginInlineStart: 'auto'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const CopyAsMarkdownButton = ({ storyId = '' })=>{
|
|
28
|
+
const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
29
|
+
const targetWindow = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
|
|
30
|
+
const styles = useStyles();
|
|
31
|
+
const toastId = (0, _reactutilities.useId)('copy-toast');
|
|
32
|
+
const toasterId = (0, _reactutilities.useId)('toaster');
|
|
33
|
+
const { dispatchToast, updateToast } = (0, _reacttoast.useToastController)(toasterId);
|
|
34
|
+
// Cache for the fetched markdown content to avoid redundant network requests
|
|
35
|
+
const markdownContentCache = _react.useRef(null);
|
|
36
|
+
// AbortController to track and cancel fetch requests
|
|
37
|
+
const abortControllerRef = _react.useRef(null);
|
|
38
|
+
// Full URL to the markdown endpoint for this story
|
|
39
|
+
const markdownUrl = _react.useMemo(()=>{
|
|
40
|
+
return targetWindow ? convertStoryIdToMarkdownUrl(targetWindow, storyId) : '';
|
|
41
|
+
}, [
|
|
42
|
+
storyId,
|
|
43
|
+
targetWindow
|
|
44
|
+
]);
|
|
45
|
+
// Cleanup: abort pending requests on unmount
|
|
46
|
+
_react.useEffect(()=>{
|
|
47
|
+
return ()=>{
|
|
48
|
+
var _abortControllerRef_current;
|
|
49
|
+
(_abortControllerRef_current = abortControllerRef.current) === null || _abortControllerRef_current === void 0 ? void 0 : _abortControllerRef_current.abort();
|
|
50
|
+
};
|
|
51
|
+
}, []);
|
|
52
|
+
/**
|
|
53
|
+
* Fetches the markdown content (with caching) and copies it to the clipboard.
|
|
54
|
+
* Shows a toast notification with loading, success, or error states.
|
|
55
|
+
* Skips the request if one is already in progress.
|
|
56
|
+
*/ const copyPageContentToClipboard = _react.useCallback(async ()=>{
|
|
57
|
+
// Skip if a request is already in progress (abort controller exists and not aborted)
|
|
58
|
+
if (abortControllerRef.current && !abortControllerRef.current.signal.aborted) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Ensure we have a window context to use for clipboard and fetch
|
|
62
|
+
if (!targetWindow) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Create new AbortController for this request
|
|
66
|
+
const abortController = new AbortController();
|
|
67
|
+
abortControllerRef.current = abortController;
|
|
68
|
+
// Show loading toast that persists until updated
|
|
69
|
+
dispatchToast(/*#__PURE__*/ _react.createElement(_reacttoast.Toast, null, /*#__PURE__*/ _react.createElement(_reacttoast.ToastTitle, {
|
|
70
|
+
media: /*#__PURE__*/ _react.createElement(_reactspinner.Spinner, null)
|
|
71
|
+
}, "Copying page content...")), {
|
|
72
|
+
toastId,
|
|
73
|
+
intent: 'info',
|
|
74
|
+
timeout: -1
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
// Use cached content if available, otherwise fetch from API
|
|
78
|
+
if (!markdownContentCache.current) {
|
|
79
|
+
markdownContentCache.current = await fetchMarkdownContent(targetWindow, markdownUrl, abortController.signal);
|
|
80
|
+
}
|
|
81
|
+
// Copy to clipboard
|
|
82
|
+
await (targetWindow === null || targetWindow === void 0 ? void 0 : targetWindow.navigator.clipboard.writeText(markdownContentCache.current));
|
|
83
|
+
// Update toast to success
|
|
84
|
+
updateToast({
|
|
85
|
+
content: /*#__PURE__*/ _react.createElement(_reacttoast.Toast, null, /*#__PURE__*/ _react.createElement(_reacttoast.ToastTitle, null, "Page content copied to clipboard!")),
|
|
86
|
+
intent: 'success',
|
|
87
|
+
toastId,
|
|
88
|
+
timeout: 3000
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// Don't show error if request was aborted
|
|
92
|
+
if (abortController.signal.aborted) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
96
|
+
// Update toast to error
|
|
97
|
+
updateToast({
|
|
98
|
+
content: /*#__PURE__*/ _react.createElement(_reacttoast.Toast, null, /*#__PURE__*/ _react.createElement(_reacttoast.ToastTitle, null, "Failed to copy page content: ", errorMessage)),
|
|
99
|
+
intent: 'error',
|
|
100
|
+
toastId,
|
|
101
|
+
timeout: 3000
|
|
102
|
+
});
|
|
103
|
+
} finally{
|
|
104
|
+
// Clear the abort controller ref to allow new requests
|
|
105
|
+
abortControllerRef.current = null;
|
|
106
|
+
}
|
|
107
|
+
}, [
|
|
108
|
+
dispatchToast,
|
|
109
|
+
updateToast,
|
|
110
|
+
toastId,
|
|
111
|
+
markdownUrl,
|
|
112
|
+
targetWindow
|
|
113
|
+
]);
|
|
114
|
+
/** Opens the markdown content in a new browser tab */ const openInNewTab = _react.useCallback(()=>{
|
|
115
|
+
targetWindow === null || targetWindow === void 0 ? void 0 : targetWindow.open(markdownUrl, '_blank');
|
|
116
|
+
}, [
|
|
117
|
+
markdownUrl,
|
|
118
|
+
targetWindow
|
|
119
|
+
]);
|
|
120
|
+
if (!storyId) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return /*#__PURE__*/ _react.createElement(_react.Fragment, null, /*#__PURE__*/ _react.createElement(_reactmenu.Menu, {
|
|
124
|
+
positioning: "below-end"
|
|
125
|
+
}, /*#__PURE__*/ _react.createElement(_reactmenu.MenuTrigger, {
|
|
126
|
+
disableButtonEnhancement: true
|
|
127
|
+
}, (triggerProps)=>/*#__PURE__*/ _react.createElement(_reactbutton.SplitButton, {
|
|
128
|
+
className: styles.button,
|
|
129
|
+
menuButton: triggerProps,
|
|
130
|
+
primaryActionButton: {
|
|
131
|
+
appearance: 'secondary',
|
|
132
|
+
icon: /*#__PURE__*/ _react.createElement(MarkdownIcon, null),
|
|
133
|
+
onClick: copyPageContentToClipboard,
|
|
134
|
+
'aria-label': 'Copy page content as markdown to clipboard'
|
|
135
|
+
},
|
|
136
|
+
"aria-label": "Copy page as markdown"
|
|
137
|
+
}, "Copy Page")), /*#__PURE__*/ _react.createElement(_reactmenu.MenuPopover, null, /*#__PURE__*/ _react.createElement(_reactmenu.MenuList, null, /*#__PURE__*/ _react.createElement(_reactmenu.MenuItem, {
|
|
138
|
+
icon: /*#__PURE__*/ _react.createElement(MarkdownIcon, null),
|
|
139
|
+
onClick: openInNewTab
|
|
140
|
+
}, "View as Markdown")))), /*#__PURE__*/ _react.createElement(_reacttoast.Toaster, {
|
|
141
|
+
toasterId: toasterId
|
|
142
|
+
}));
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Regex pattern to remove the story variant suffix from Storybook story IDs.
|
|
146
|
+
* @example "button--primary" -> "button"
|
|
147
|
+
*/ const STORYBOOK_VARIANT_SUFFIX_PATTERN = /--\w+$/g;
|
|
148
|
+
/**
|
|
149
|
+
* Gets the base URL for fetching markdown content from the Storybook LLM endpoint.
|
|
150
|
+
* Each story's markdown is available at: {BASE_URL}/{storyId}.txt
|
|
151
|
+
* @param targetWindow - The window object to use for location access
|
|
152
|
+
* @returns The base URL constructed from current location origin and pathname
|
|
153
|
+
*/ function getStorybookMarkdownApiBaseUrl(targetWindow) {
|
|
154
|
+
// Remove the [page].html file from pathname and append /llms/
|
|
155
|
+
const basePath = targetWindow.location.pathname.replace(/\/[^/]*\.html$/, '');
|
|
156
|
+
return `${targetWindow.location.origin}${basePath}/llms/`;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Converts a Storybook story ID to a markdown URL.
|
|
160
|
+
* @param targetWindow - The window object to use for location access
|
|
161
|
+
* @param storyId - The Storybook story ID
|
|
162
|
+
* @returns The full URL to the markdown endpoint for the story
|
|
163
|
+
* @example "button--primary" -> "https://storybooks.fluentui.dev/llms/button.txt"
|
|
164
|
+
*/ function convertStoryIdToMarkdownUrl(targetWindow, storyId) {
|
|
165
|
+
return `${getStorybookMarkdownApiBaseUrl(targetWindow)}${storyId.replace(STORYBOOK_VARIANT_SUFFIX_PATTERN, '.txt')}`;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Fetches markdown content from the Storybook API.
|
|
169
|
+
* @param targetWindow - The window object to use for fetch access
|
|
170
|
+
* @param url - The URL to fetch markdown content from
|
|
171
|
+
* @param signal - Optional AbortSignal to cancel the request
|
|
172
|
+
* @returns Promise resolving to the markdown text content
|
|
173
|
+
* @throws Error if the fetch request fails or is aborted
|
|
174
|
+
*/ async function fetchMarkdownContent(targetWindow, url, signal) {
|
|
175
|
+
const response = await targetWindow.fetch(url, {
|
|
176
|
+
headers: {
|
|
177
|
+
'Content-Type': 'text/plain'
|
|
178
|
+
},
|
|
179
|
+
signal
|
|
180
|
+
});
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
throw new Error(`Failed to fetch markdown: ${response.status} ${response.statusText}`);
|
|
183
|
+
}
|
|
184
|
+
return response.text();
|
|
185
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/docs/CopyAsMarkdownButton.tsx"],"sourcesContent":["import * as React from 'react';\nimport { SplitButton, type MenuButtonProps } from '@fluentui/react-button';\nimport { Menu, MenuItem, MenuList, MenuPopover, MenuTrigger } from '@fluentui/react-menu';\nimport { Spinner } from '@fluentui/react-spinner';\nimport { Toast, Toaster, ToastTitle, useToastController } from '@fluentui/react-toast';\nimport { useId } from '@fluentui/react-utilities';\nimport { makeStyles } from '@griffel/react';\nimport { bundleIcon, MarkdownFilled, MarkdownRegular } from '@fluentui/react-icons';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\n\nconst MarkdownIcon = bundleIcon(MarkdownFilled, MarkdownRegular);\n\nconst useStyles = makeStyles({\n button: {\n marginInlineStart: 'auto',\n },\n});\n\nexport interface CopyAsMarkdownProps {\n /** The Storybook story ID used to generate the markdown URL */\n storyId?: string;\n}\n\n/**\n * A button that allows users to copy the current page as markdown to their clipboard or view it in a new tab.\n * The markdown content is fetched from the Storybook API and cached for subsequent requests.\n */\nexport const CopyAsMarkdownButton: React.FC<CopyAsMarkdownProps> = ({ storyId = '' }) => {\n const { targetDocument } = useFluent();\n const targetWindow = targetDocument?.defaultView;\n const styles = useStyles();\n const toastId = useId('copy-toast');\n const toasterId = useId('toaster');\n const { dispatchToast, updateToast } = useToastController(toasterId);\n\n // Cache for the fetched markdown content to avoid redundant network requests\n const markdownContentCache = React.useRef<string | null>(null);\n\n // AbortController to track and cancel fetch requests\n const abortControllerRef = React.useRef<AbortController | null>(null);\n\n // Full URL to the markdown endpoint for this story\n const markdownUrl = React.useMemo(() => {\n return targetWindow ? convertStoryIdToMarkdownUrl(targetWindow, storyId) : '';\n }, [storyId, targetWindow]);\n\n // Cleanup: abort pending requests on unmount\n React.useEffect(() => {\n return () => {\n abortControllerRef.current?.abort();\n };\n }, []);\n\n /**\n * Fetches the markdown content (with caching) and copies it to the clipboard.\n * Shows a toast notification with loading, success, or error states.\n * Skips the request if one is already in progress.\n */\n const copyPageContentToClipboard = React.useCallback(async () => {\n // Skip if a request is already in progress (abort controller exists and not aborted)\n if (abortControllerRef.current && !abortControllerRef.current.signal.aborted) {\n return;\n }\n\n // Ensure we have a window context to use for clipboard and fetch\n if (!targetWindow) {\n return;\n }\n\n // Create new AbortController for this request\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // Show loading toast that persists until updated\n dispatchToast(\n <Toast>\n <ToastTitle media={<Spinner />}>Copying page content...</ToastTitle>\n </Toast>,\n {\n toastId,\n intent: 'info',\n timeout: -1, // Never auto-dismiss\n },\n );\n\n try {\n // Use cached content if available, otherwise fetch from API\n if (!markdownContentCache.current) {\n markdownContentCache.current = await fetchMarkdownContent(targetWindow, markdownUrl, abortController.signal);\n }\n\n // Copy to clipboard\n await targetWindow?.navigator.clipboard.writeText(markdownContentCache.current);\n\n // Update toast to success\n updateToast({\n content: (\n <Toast>\n <ToastTitle>Page content copied to clipboard!</ToastTitle>\n </Toast>\n ),\n intent: 'success',\n toastId,\n timeout: 3000,\n });\n } catch (error) {\n // Don't show error if request was aborted\n if (abortController.signal.aborted) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Update toast to error\n updateToast({\n content: (\n <Toast>\n <ToastTitle>Failed to copy page content: {errorMessage}</ToastTitle>\n </Toast>\n ),\n intent: 'error',\n toastId,\n timeout: 3000,\n });\n } finally {\n // Clear the abort controller ref to allow new requests\n abortControllerRef.current = null;\n }\n }, [dispatchToast, updateToast, toastId, markdownUrl, targetWindow]);\n\n /** Opens the markdown content in a new browser tab */\n const openInNewTab = React.useCallback(() => {\n targetWindow?.open(markdownUrl, '_blank');\n }, [markdownUrl, targetWindow]);\n\n if (!storyId) {\n return null;\n }\n\n return (\n <>\n <Menu positioning=\"below-end\">\n <MenuTrigger disableButtonEnhancement>\n {(triggerProps: MenuButtonProps) => (\n <SplitButton\n className={styles.button}\n menuButton={triggerProps}\n primaryActionButton={{\n appearance: 'secondary',\n icon: <MarkdownIcon />,\n onClick: copyPageContentToClipboard,\n 'aria-label': 'Copy page content as markdown to clipboard',\n }}\n aria-label=\"Copy page as markdown\"\n >\n Copy Page\n </SplitButton>\n )}\n </MenuTrigger>\n\n <MenuPopover>\n <MenuList>\n <MenuItem icon={<MarkdownIcon />} onClick={openInNewTab}>\n View as Markdown\n </MenuItem>\n </MenuList>\n </MenuPopover>\n </Menu>\n <Toaster toasterId={toasterId} />\n </>\n );\n};\n\n/**\n * Regex pattern to remove the story variant suffix from Storybook story IDs.\n * @example \"button--primary\" -> \"button\"\n */\nconst STORYBOOK_VARIANT_SUFFIX_PATTERN = /--\\w+$/g;\n\n/**\n * Gets the base URL for fetching markdown content from the Storybook LLM endpoint.\n * Each story's markdown is available at: {BASE_URL}/{storyId}.txt\n * @param targetWindow - The window object to use for location access\n * @returns The base URL constructed from current location origin and pathname\n */\nfunction getStorybookMarkdownApiBaseUrl(targetWindow: Window): string {\n // Remove the [page].html file from pathname and append /llms/\n const basePath = targetWindow.location.pathname.replace(/\\/[^/]*\\.html$/, '');\n return `${targetWindow.location.origin}${basePath}/llms/`;\n}\n\n/**\n * Converts a Storybook story ID to a markdown URL.\n * @param targetWindow - The window object to use for location access\n * @param storyId - The Storybook story ID\n * @returns The full URL to the markdown endpoint for the story\n * @example \"button--primary\" -> \"https://storybooks.fluentui.dev/llms/button.txt\"\n */\nfunction convertStoryIdToMarkdownUrl(targetWindow: Window, storyId: string): string {\n return `${getStorybookMarkdownApiBaseUrl(targetWindow)}${storyId.replace(STORYBOOK_VARIANT_SUFFIX_PATTERN, '.txt')}`;\n}\n\n/**\n * Fetches markdown content from the Storybook API.\n * @param targetWindow - The window object to use for fetch access\n * @param url - The URL to fetch markdown content from\n * @param signal - Optional AbortSignal to cancel the request\n * @returns Promise resolving to the markdown text content\n * @throws Error if the fetch request fails or is aborted\n */\nasync function fetchMarkdownContent(\n targetWindow: Window,\n url: string,\n signal: AbortSignal | undefined,\n): Promise<string> {\n const response = await targetWindow.fetch(url, {\n headers: {\n 'Content-Type': 'text/plain',\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch markdown: ${response.status} ${response.statusText}`);\n }\n\n return response.text();\n}\n"],"names":["CopyAsMarkdownButton","MarkdownIcon","bundleIcon","MarkdownFilled","MarkdownRegular","useStyles","makeStyles","button","marginInlineStart","storyId","targetDocument","useFluent","targetWindow","defaultView","styles","toastId","useId","toasterId","dispatchToast","updateToast","useToastController","markdownContentCache","React","useRef","abortControllerRef","markdownUrl","useMemo","convertStoryIdToMarkdownUrl","useEffect","current","abort","copyPageContentToClipboard","useCallback","signal","aborted","abortController","AbortController","Toast","ToastTitle","media","Spinner","intent","timeout","fetchMarkdownContent","navigator","clipboard","writeText","content","error","errorMessage","Error","message","String","openInNewTab","open","Menu","positioning","MenuTrigger","disableButtonEnhancement","triggerProps","SplitButton","className","menuButton","primaryActionButton","appearance","icon","onClick","aria-label","MenuPopover","MenuList","MenuItem","Toaster","STORYBOOK_VARIANT_SUFFIX_PATTERN","getStorybookMarkdownApiBaseUrl","basePath","location","pathname","replace","origin","url","response","fetch","headers","ok","status","statusText","text"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "DirSwitch", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return DirSwitch;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _previewapi = require("storybook/preview-api");
|
|
14
|
+
const _reactlabel = require("@fluentui/react-label");
|
|
15
|
+
const _reactswitch = require("@fluentui/react-switch");
|
|
16
|
+
const _reactutilities = require("@fluentui/react-utilities");
|
|
17
|
+
const _reacttheme = require("@fluentui/react-theme");
|
|
18
|
+
const _react1 = require("@griffel/react");
|
|
19
|
+
const _constants = require("../constants");
|
|
20
|
+
const useStyles = (0, _react1.makeStyles)({
|
|
21
|
+
container: {
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
display: 'flex',
|
|
24
|
+
justifyContent: 'start'
|
|
25
|
+
},
|
|
26
|
+
label: {
|
|
27
|
+
..._reacttheme.typographyStyles.subtitle2
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const DirSwitch = ({ dir })=>{
|
|
31
|
+
const switchId = (0, _reactutilities.useId)('dir-switch');
|
|
32
|
+
const styles = useStyles();
|
|
33
|
+
const [currentDir, setCurrentDir] = _react.useState(dir);
|
|
34
|
+
const checked = currentDir === 'rtl';
|
|
35
|
+
const setGlobalDir = (newDir)=>{
|
|
36
|
+
_previewapi.addons.getChannel().emit('updateGlobals', {
|
|
37
|
+
globals: {
|
|
38
|
+
[_constants.DIR_ID]: newDir
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const onChange = _react.useCallback((_, data)=>{
|
|
43
|
+
const newDir = data.checked ? 'rtl' : 'ltr';
|
|
44
|
+
setGlobalDir(newDir);
|
|
45
|
+
setCurrentDir(newDir);
|
|
46
|
+
}, []);
|
|
47
|
+
return /*#__PURE__*/ _react.createElement("div", {
|
|
48
|
+
className: styles.container
|
|
49
|
+
}, /*#__PURE__*/ _react.createElement(_reactlabel.Label, {
|
|
50
|
+
className: styles.label,
|
|
51
|
+
htmlFor: currentDir === 'ltr' ? undefined : switchId
|
|
52
|
+
}, "LTR"), /*#__PURE__*/ _react.createElement(_reactswitch.Switch, {
|
|
53
|
+
checked: checked,
|
|
54
|
+
id: switchId,
|
|
55
|
+
onChange: onChange
|
|
56
|
+
}), /*#__PURE__*/ _react.createElement(_reactlabel.Label, {
|
|
57
|
+
className: styles.label,
|
|
58
|
+
htmlFor: currentDir === 'rtl' ? switchId : undefined
|
|
59
|
+
}, "RTL"));
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/docs/DirSwitch.tsx"],"sourcesContent":["import * as React from 'react';\nimport { addons } from 'storybook/preview-api';\n\nimport { Label } from '@fluentui/react-label';\nimport { Switch, type SwitchProps } from '@fluentui/react-switch';\nimport { useId } from '@fluentui/react-utilities';\nimport { typographyStyles } from '@fluentui/react-theme';\nimport { makeStyles } from '@griffel/react';\n\nimport { DIR_ID } from '../constants';\n\nconst useStyles = makeStyles({\n container: {\n alignItems: 'center',\n display: 'flex',\n justifyContent: 'start',\n },\n label: {\n ...typographyStyles.subtitle2,\n },\n});\n\n/**\n * Dir switch used in the react-components docs header\n */\nexport const DirSwitch: React.FC<{ dir?: 'ltr' | 'rtl' }> = ({ dir }) => {\n const switchId = useId('dir-switch');\n\n const styles = useStyles();\n\n const [currentDir, setCurrentDir] = React.useState(dir);\n const checked = currentDir === 'rtl';\n\n const setGlobalDir = (newDir: 'ltr' | 'rtl'): void => {\n addons.getChannel().emit('updateGlobals', { globals: { [DIR_ID]: newDir } });\n };\n\n const onChange = React.useCallback<NonNullable<SwitchProps['onChange']>>((_, data) => {\n const newDir = data.checked ? 'rtl' : 'ltr';\n setGlobalDir(newDir);\n setCurrentDir(newDir);\n }, []);\n\n return (\n <div className={styles.container}>\n <Label className={styles.label} htmlFor={currentDir === 'ltr' ? undefined : switchId}>\n LTR\n </Label>\n <Switch checked={checked} id={switchId} onChange={onChange} />\n <Label className={styles.label} htmlFor={currentDir === 'rtl' ? switchId : undefined}>\n RTL\n </Label>\n </div>\n );\n};\n"],"names":["DirSwitch","useStyles","makeStyles","container","alignItems","display","justifyContent","label","typographyStyles","subtitle2","dir","switchId","useId","styles","currentDir","setCurrentDir","React","useState","checked","setGlobalDir","newDir","addons","getChannel","emit","globals","DIR_ID","onChange","useCallback","_","data","div","className","Label","htmlFor","undefined","Switch","id"],"mappings":";;;;+BAyBaA;;;eAAAA;;;;iEAzBU;4BACA;4BAED;6BACmB;gCACnB;4BACW;wBACN;2BAEJ;AAEvB,MAAMC,YAAYC,IAAAA,kBAAU,EAAC;IAC3BC,WAAW;QACTC,YAAY;QACZC,SAAS;QACTC,gBAAgB;IAClB;IACAC,OAAO;QACL,GAAGC,4BAAgB,CAACC,SAAS;IAC/B;AACF;AAKO,MAAMT,YAA+C,CAAC,EAAEU,GAAG,EAAE;IAClE,MAAMC,WAAWC,IAAAA,qBAAK,EAAC;IAEvB,MAAMC,SAASZ;IAEf,MAAM,CAACa,YAAYC,cAAc,GAAGC,OAAMC,QAAQ,CAACP;IACnD,MAAMQ,UAAUJ,eAAe;IAE/B,MAAMK,eAAe,CAACC;QACpBC,kBAAM,CAACC,UAAU,GAAGC,IAAI,CAAC,iBAAiB;YAAEC,SAAS;gBAAE,CAACC,iBAAM,CAAC,EAAEL;YAAO;QAAE;IAC5E;IAEA,MAAMM,WAAWV,OAAMW,WAAW,CAAuC,CAACC,GAAGC;QAC3E,MAAMT,SAASS,KAAKX,OAAO,GAAG,QAAQ;QACtCC,aAAaC;QACbL,cAAcK;IAChB,GAAG,EAAE;IAEL,qBACE,qBAACU;QAAIC,WAAWlB,OAAOV,SAAS;qBAC9B,qBAAC6B,iBAAK;QAACD,WAAWlB,OAAON,KAAK;QAAE0B,SAASnB,eAAe,QAAQoB,YAAYvB;OAAU,sBAGtF,qBAACwB,mBAAM;QAACjB,SAASA;QAASkB,IAAIzB;QAAUe,UAAUA;sBAClD,qBAACM,iBAAK;QAACD,WAAWlB,OAAON,KAAK;QAAE0B,SAASnB,eAAe,QAAQH,WAAWuB;OAAW;AAK5F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "FluentCanvas", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return FluentCanvas;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _react1 = require("@griffel/react");
|
|
14
|
+
const useStyles = (0, _react1.makeStyles)({
|
|
15
|
+
canvas: {
|
|
16
|
+
padding: '20px',
|
|
17
|
+
boxSizing: 'border-box',
|
|
18
|
+
overflow: 'auto'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const FluentCanvas = (props)=>{
|
|
22
|
+
const styles = useStyles();
|
|
23
|
+
return /*#__PURE__*/ _react.createElement("div", {
|
|
24
|
+
className: "sbdocs-preview"
|
|
25
|
+
}, /*#__PURE__*/ _react.createElement("div", {
|
|
26
|
+
className: styles.canvas,
|
|
27
|
+
...props
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/docs/FluentCanvas.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport { makeStyles } from '@griffel/react';\n\nconst useStyles = makeStyles({\n canvas: {\n padding: '20px',\n boxSizing: 'border-box',\n overflow: 'auto',\n },\n});\n\n/**\n * Canvas component to wrap stories in a styled container.\n * Provides a similar experience to Storybook's v7 `Canvas` component.\n */\nexport const FluentCanvas = (props: React.ComponentProps<'div'>): JSXElement => {\n const styles = useStyles();\n\n return (\n <div className=\"sbdocs-preview\">\n <div className={styles.canvas} {...props} />\n </div>\n );\n};\n"],"names":["FluentCanvas","useStyles","makeStyles","canvas","padding","boxSizing","overflow","props","styles","div","className"],"mappings":";;;;+BAgBaA;;;eAAAA;;;;iEAhBU;wBAEI;AAE3B,MAAMC,YAAYC,IAAAA,kBAAU,EAAC;IAC3BC,QAAQ;QACNC,SAAS;QACTC,WAAW;QACXC,UAAU;IACZ;AACF;AAMO,MAAMN,eAAe,CAACO;IAC3B,MAAMC,SAASP;IAEf,qBACE,qBAACQ;QAAIC,WAAU;qBACb,qBAACD;QAAIC,WAAWF,OAAOL,MAAM;QAAG,GAAGI,KAAK;;AAG9C"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "FluentDocsContainer", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return FluentDocsContainer;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
13
|
+
const _blocks = require("@storybook/addon-docs/blocks");
|
|
14
|
+
const _reacttheme = require("@fluentui/react-theme");
|
|
15
|
+
const _reactprovider = require("@fluentui/react-provider");
|
|
16
|
+
const _utils = require("./utils");
|
|
17
|
+
const FluentDocsContainer = ({ children, context })=>{
|
|
18
|
+
if ((0, _utils.isDocsEnabled)(context)) {
|
|
19
|
+
return /*#__PURE__*/ _react.createElement(_react.Fragment, null, /*#__PURE__*/ _react.createElement(_reactprovider.FluentProvider, {
|
|
20
|
+
className: "sb-unstyled",
|
|
21
|
+
style: {
|
|
22
|
+
backgroundColor: 'transparent'
|
|
23
|
+
},
|
|
24
|
+
theme: _reacttheme.webLightTheme
|
|
25
|
+
}, /*#__PURE__*/ _react.createElement(_blocks.DocsContainer, {
|
|
26
|
+
context: context
|
|
27
|
+
}, children)));
|
|
28
|
+
}
|
|
29
|
+
// If docs container is not enabled, fall back to Storybook's default DocsContainer
|
|
30
|
+
return /*#__PURE__*/ _react.createElement(_blocks.DocsContainer, {
|
|
31
|
+
context: context
|
|
32
|
+
}, children);
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/docs/FluentDocsContainer.tsx"],"sourcesContent":["import * as React from 'react';\nimport { DocsContainer, type DocsContextProps } from '@storybook/addon-docs/blocks';\nimport { webLightTheme } from '@fluentui/react-theme';\nimport { FluentProvider } from '@fluentui/react-provider';\n\nimport { isDocsEnabled } from './utils';\n\ninterface FluentDocsContainerProps {\n context: DocsContextProps;\n children: React.ReactNode;\n}\n\n/**\n * A container that wraps storybook's native docs container to add extra components to the docs experience\n */\nexport const FluentDocsContainer: React.FC<FluentDocsContainerProps> = ({ children, context }) => {\n if (isDocsEnabled(context)) {\n return (\n <>\n {/** TODO add table of contents */}\n <FluentProvider className=\"sb-unstyled\" style={{ backgroundColor: 'transparent' }} theme={webLightTheme}>\n <DocsContainer context={context}>{children}</DocsContainer>\n </FluentProvider>\n </>\n );\n }\n\n // If docs container is not enabled, fall back to Storybook's default DocsContainer\n return <DocsContainer context={context}>{children}</DocsContainer>;\n};\n"],"names":["FluentDocsContainer","children","context","isDocsEnabled","FluentProvider","className","style","backgroundColor","theme","webLightTheme","DocsContainer"],"mappings":";;;;+BAeaA;;;eAAAA;;;;iEAfU;wBAC8B;4BACvB;+BACC;uBAED;AAUvB,MAAMA,sBAA0D,CAAC,EAAEC,QAAQ,EAAEC,OAAO,EAAE;IAC3F,IAAIC,IAAAA,oBAAa,EAACD,UAAU;QAC1B,qBACE,0DAEE,qBAACE,6BAAc;YAACC,WAAU;YAAcC,OAAO;gBAAEC,iBAAiB;YAAc;YAAGC,OAAOC,yBAAa;yBACrG,qBAACC,qBAAa;YAACR,SAASA;WAAUD;IAI1C;IAEA,mFAAmF;IACnF,qBAAO,qBAACS,qBAAa;QAACR,SAASA;OAAUD;AAC3C"}
|