UNPKG

storybook-addon-grid

Version:
186 lines (178 loc) 5.98 kB
// src/manager.tsx import { addons, types } from "storybook/manager-api"; // src/constants.ts var ADDON_ID = "storybook-addon-grid"; var PARAM_KEY = "grid"; // src/Tool.tsx import React3 from "react"; import { useAddonState as useAddonState2, useParameter as useParameter2, useStorybookApi } from "storybook/manager-api"; import { IconButton } from "storybook/internal/components"; import { STORY_RENDERED } from "storybook/internal/core-events"; // src/Grids.tsx import React2 from "react"; import { createPortal } from "react-dom"; import { useAddonState, useParameter } from "storybook/manager-api"; import { CacheProvider, createCache } from "storybook/theming"; // src/ui.tsx import React from "react"; import { Global, styled } from "storybook/theming"; var MAX_COLUMNS = 24; var Wrapper = styled.div({ position: "relative", zIndex: 1, opacity: 0, pointerEvents: "none", '&[data-visible="true"]': { opacity: 1 } }); var Grid = styled.div( ({ gap, gutter, maxWidth, columns }) => { let gutterRight = "0", gutterLeft = "0"; if (Array.isArray(gutter)) { [gutterLeft, gutterRight] = gutter; } else if (gutter != null) { gutterLeft = gutterRight = gutter; } return { position: "fixed", inset: "0", display: "grid", gridTemplateColumns: `repeat(min(${columns}, ${MAX_COLUMNS}), 1fr)`, gridTemplateRows: "100%", gridColumnGap: gap, width: "100%", height: "100%", margin: "0 auto", maxWidth, padding: `0 ${gutterRight} 0 ${gutterLeft}`, boxSizing: "border-box" }; } ); var Column = styled.div(({ color }) => ({ width: "100%", height: "100%", backgroundColor: color })); function Grids({ visible, columns = 12, gap = "20px", color = "rgba(255, 0, 0, 0.1)", gutter = "50px", maxWidth = "1024px" }) { let columnDivs = React.useMemo( () => Array.from({ length: typeof columns === "number" ? columns : MAX_COLUMNS }).map((_, index) => <Column key={index} color={color} />), [columns, color] ); let gridNodes = <Grid gap={gap} gutter={gutter} maxWidth={maxWidth} columns={columns}>{columnDivs}</Grid>; return <> <Global styles={{ [`#root`]: { position: "relative", zIndex: 0 } }} /> <Wrapper data-addon-id={ADDON_ID} data-visible={visible}>{gridNodes}</Wrapper> </>; } // src/Grids.tsx function ManagerRenderedGrids() { let { columns, gap, color, gutter, maxWidth, disable } = useParameter(PARAM_KEY, {}); let [state] = useAddonState(ADDON_ID); return <Grids columns={columns} color={color} gap={gap} visible={disable != null ? !disable : state?.visible ?? false} gutter={gutter} maxWidth={maxWidth} />; } var styleCache = /* @__PURE__ */ new WeakMap(); var ManagerRenderedGridsContainer = React2.memo( function ManagerRenderedGridsContainer2() { let previewIframe = document.querySelector( "#storybook-preview-iframe" ); if (!previewIframe) return null; let iframeDocument = previewIframe.contentWindow?.document; if (!iframeDocument) return null; let head = iframeDocument.head; if (!head || !iframeDocument.body) return null; if (!styleCache.has(head)) styleCache.set( head, createCache({ key: ADDON_ID, container: head }) ); return createPortal( <CacheProvider value={styleCache.get(head)}><ManagerRenderedGrids /></CacheProvider>, iframeDocument.body ); } ); // src/Tool.tsx var shortcut = ["control", "G"]; var Tool = React3.memo(ToolComponent); function ToolComponent() { let parameters = useParameter2(PARAM_KEY, {}); let [state, setState] = useAddonState2(ADDON_ID, { visible: false }); let [ready, setReady] = React3.useState(false); let api = useStorybookApi(); let toggleGrid = React3.useCallback(() => { setState( (prev) => ({ visible: !prev?.visible || false }) ); }, []); React3.useEffect(() => { api.setAddonShortcut(ADDON_ID, { label: "Toggle Column Guides", action: toggleGrid, actionName: "toggle-column-grid", defaultShortcut: shortcut, showInMenu: true }); }, [api, toggleGrid]); React3.useEffect(() => { let mounted = true; function handler() { mounted && setReady(true); } api.once(STORY_RENDERED, handler); return () => { mounted = false; }; }, [api]); let disabled = typeof parameters.disable === "boolean" ? parameters.disable : false; let isActive = disabled ? !disabled : state.visible; return <React3.Fragment key={ADDON_ID}> <IconButton title="Toggle Column Guides" disabled={disabled} active={isActive} onClick={toggleGrid}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"> <path d="M3 6.2c0-1.12 0-1.68.218-2.108a2 2 0 0 1 .874-.874C4.52 3 5.08 3 6.2 3h.6c1.12 0 1.68 0 2.108.218a2 2 0 0 1 .874.874C10 4.52 10 5.08 10 6.2v11.6c0 1.12 0 1.68-.218 2.108a2 2 0 0 1-.874.874C8.48 21 7.92 21 6.8 21h-.6c-1.12 0-1.68 0-2.108-.218a2 2 0 0 1-.874-.874C3 19.48 3 18.92 3 17.8V6.2Z" /> <path d="M14 6.2c0-1.12 0-1.68.218-2.108a2 2 0 0 1 .874-.874C15.52 3 16.08 3 17.2 3h.6c1.12 0 1.68 0 2.108.218a2 2 0 0 1 .874.874C21 4.52 21 5.08 21 6.2v11.6c0 1.12 0 1.68-.218 2.108a2 2 0 0 1-.874.874C19.48 21 18.92 21 17.8 21h-.6c-1.12 0-1.68 0-2.108-.218a2 2 0 0 1-.874-.874C14 19.48 14 18.92 14 17.8V6.2Z" /> </svg></IconButton> {ready && !disabled ? <ManagerRenderedGridsContainer /> : null} </React3.Fragment>; } // src/manager.tsx addons.register(ADDON_ID, () => { addons.add(ADDON_ID, { type: types.TOOL, title: "Column Grid", paramKey: PARAM_KEY, match: ({ viewMode, tabId }) => !!(viewMode && viewMode.match(/^(story|docs)$/)) && !tabId, render() { return <Tool />; } }); });