@dxos/plugin-stack 0.13.0 → 0.14.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/dist/lib/components.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { useAttentionAttributes } from "@dxos/react-ui-attention";
|
|
|
9
9
|
import { Mosaic } from "@dxos/react-ui-mosaic";
|
|
10
10
|
import * as Hooks from "@dxos/react-ui/Hooks";
|
|
11
11
|
import * as Icon from "@dxos/react-ui/Icon";
|
|
12
|
+
import * as Layout from "@dxos/react-ui/Layout";
|
|
12
13
|
import * as Menu from "@dxos/react-ui/Menu";
|
|
13
14
|
import * as ScrollArea from "@dxos/react-ui/ScrollArea";
|
|
14
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -124,8 +125,8 @@ var StackSection = ({ data, ...tileProps }) => {
|
|
|
124
125
|
const isCollapsed = collapsed.has(id);
|
|
125
126
|
const icon = Obj.getIcon(object)?.icon ?? "ph--circle-dashed--regular";
|
|
126
127
|
const title = Obj.getLabel(object, { fallback: "typename" }) ?? t("untitled-section.title");
|
|
127
|
-
const rail = /* @__PURE__ */ jsxs(
|
|
128
|
-
|
|
128
|
+
const rail = /* @__PURE__ */ jsxs(Layout.Grid, {
|
|
129
|
+
rows: ["min", "fill"],
|
|
129
130
|
children: [/* @__PURE__ */ jsx("div", {
|
|
130
131
|
className: "p-1 dx-toolbar-surface",
|
|
131
132
|
children: /* @__PURE__ */ jsxs(Menu.Root, {
|
|
@@ -215,8 +216,10 @@ var StackSection = ({ data, ...tileProps }) => {
|
|
|
215
216
|
classNames: "grid grid-cols-[var(--dx-rail-action)_1fr] dx-attention-surface border border-separator-subtle",
|
|
216
217
|
children: [/* @__PURE__ */ jsx("div", {
|
|
217
218
|
className: "border-e border-separator-subtle",
|
|
218
|
-
children: /* @__PURE__ */ jsx(
|
|
219
|
-
|
|
219
|
+
children: /* @__PURE__ */ jsx(Layout.Flex, {
|
|
220
|
+
column: true,
|
|
221
|
+
align: "center",
|
|
222
|
+
classNames: "sticky top-0",
|
|
220
223
|
children: rail
|
|
221
224
|
})
|
|
222
225
|
}), /* @__PURE__ */ jsxs("div", {
|
|
@@ -225,8 +228,8 @@ var StackSection = ({ data, ...tileProps }) => {
|
|
|
225
228
|
children: [/* @__PURE__ */ jsx("span", {
|
|
226
229
|
className: "sr-only",
|
|
227
230
|
children: title
|
|
228
|
-
}), isCollapsed ? /* @__PURE__ */ jsx(
|
|
229
|
-
|
|
231
|
+
}), isCollapsed ? /* @__PURE__ */ jsx(Layout.Flex, {
|
|
232
|
+
classNames: "h-(--dx-toolbar-size) p-1",
|
|
230
233
|
children: /* @__PURE__ */ jsx("h2", {
|
|
231
234
|
className: "flex items-center font-medium",
|
|
232
235
|
children: title
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.mjs","names":[],"sources":["../../src/components/Stack/StackContext.ts","../../src/components/Stack/Stack.tsx"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport { createContext, useContext } from 'react';\n\nimport { type StackContextType, type StackContextValue } from './Stack.tsx';\n\n// Kept out of `Stack.tsx`: react-refresh only fast-refreshes a module whose exports are all\n// components, so a context and its hook exported beside them force a full page reload on every edit.\n\nexport const StackContext = createContext<StackContextType | undefined>(undefined);\n\nexport const useStackContext = (consumer: string): StackContextType => {\n const context = useContext(StackContext);\n if (!context) {\n throw new Error(`\\`${consumer}\\` must be used within \\`Stack.Root\\`.`);\n }\n return context;\n};\n\n/** Section-level callbacks consumed by stack sections. */\nexport const useStack = (): StackContextValue => useStackContext('useStack');\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport React, {\n type ComponentPropsWithoutRef,\n type ForwardedRef,\n type PropsWithChildren,\n forwardRef,\n useCallback,\n useMemo,\n useState,\n} from 'react';\n\nimport * as Surface from '@dxos/app-framework/Surface';\nimport * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport * as AttentionSigil from '@dxos/app-toolkit/AttentionSigil';\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport { Obj } from '@dxos/echo';\nimport { useAttentionAttributes } from '@dxos/react-ui-attention';\nimport { type DndContainerHandler } from '@dxos/react-ui-dnd';\nimport { Mosaic, type MosaicTileProps } from '@dxos/react-ui-mosaic';\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as Icon from '@dxos/react-ui/Icon';\nimport * as Menu from '@dxos/react-ui/Menu';\nimport * as ScrollArea from '@dxos/react-ui/ScrollArea';\nimport type * as Util from '@dxos/react-ui/Util';\n\nimport { meta } from '#meta';\n\nimport { StackContext, useStack, useStackContext } from './StackContext.ts';\n\n//\n// Types\n//\n\nexport type StackSectionItem = {\n id: string;\n object: Obj.Unknown;\n};\n\n//\n// Context\n//\n\n/** Section-level callbacks consumed by stack sections. */\nexport type StackContextValue = {\n attendableId: string;\n /** Ids of sections that are currently collapsed. */\n collapsed: ReadonlySet<string>;\n onCollapse: (id: string, collapsed: boolean) => void;\n /** Add a new section immediately after the given section. */\n onAdd: (id: string) => void;\n onMoveUp: (id: string) => void;\n onMoveDown: (id: string) => void;\n onDelete: (id: string) => void;\n};\n\nexport type StackContextType = StackContextValue & {\n /** Container id used to scope Mosaic drag/drop. */\n id: string;\n eventHandler: DndContainerHandler;\n /** Scroll viewport element; threaded to the Mosaic container for autoscroll. */\n viewport: HTMLElement | null;\n setViewport: (element: HTMLElement | null) => void;\n};\n\n//\n// Root\n//\n\ntype StackRootProps = PropsWithChildren<\n StackContextValue & {\n /** Container id used to scope Mosaic drag/drop. */\n id: string;\n /** Drag/drop event handler; defaults to a read-only (no-drop) handler. */\n eventHandler?: DndContainerHandler;\n }\n>;\n\nconst StackRoot = ({\n id,\n eventHandler,\n children,\n attendableId,\n collapsed,\n onCollapse,\n onAdd,\n onMoveUp,\n onMoveDown,\n onDelete,\n}: StackRootProps) => {\n const [viewport, setViewport] = useState<HTMLElement | null>(null);\n const value = useMemo<StackContextType>(\n () => ({\n id,\n eventHandler: eventHandler ?? { id, canDrop: () => false },\n viewport,\n setViewport,\n attendableId,\n collapsed,\n onCollapse,\n onAdd,\n onMoveUp,\n onMoveDown,\n onDelete,\n }),\n [id, eventHandler, viewport, attendableId, collapsed, onCollapse, onAdd, onMoveUp, onMoveDown, onDelete],\n );\n\n return <StackContext.Provider value={value}>{children}</StackContext.Provider>;\n};\n\nStackRoot.displayName = 'Stack.Root';\n\n//\n// Content\n//\n\ntype StackContentProps = Util.ThemedClassName<ComponentPropsWithoutRef<'div'>>;\n\nconst StackContent = forwardRef<HTMLDivElement, StackContentProps>(({ children, ...props }, forwardedRef) => {\n const { eventHandler, viewport } = useStackContext('Stack.Content');\n return (\n <Mosaic.Container asChild orientation='vertical' autoScroll={viewport} eventHandler={eventHandler}>\n <ScrollArea.Root {...props} orientation='vertical' ref={forwardedRef}>\n {children}\n </ScrollArea.Root>\n </Mosaic.Container>\n );\n});\n\nStackContent.displayName = 'Stack.Content';\n\n//\n// Viewport\n//\n\ntype StackViewportProps = Util.ThemedClassName<PropsWithChildren>;\n\nconst StackViewport = forwardRef<HTMLDivElement, StackViewportProps>(\n ({ classNames, children }, forwardedRef: ForwardedRef<HTMLDivElement>) => {\n const { setViewport } = useStackContext('Stack.Viewport');\n // Capture the viewport element for autoscroll while still honouring a forwarded ref.\n const setRefs = useCallback(\n (element: HTMLDivElement | null) => {\n setViewport(element);\n if (typeof forwardedRef === 'function') {\n forwardedRef(element);\n } else if (forwardedRef) {\n forwardedRef.current = element;\n }\n },\n [setViewport, forwardedRef],\n );\n\n return (\n <ScrollArea.Viewport classNames={classNames} ref={setRefs}>\n {children}\n </ScrollArea.Viewport>\n );\n },\n);\n\nStackViewport.displayName = 'Stack.Viewport';\n\n//\n// Section\n//\n\ntype StackSectionProps = MosaicTileProps<StackSectionItem>;\n\n/**\n * A stack section rendered as a Mosaic tile: a 40px left rail (drag handle + section menu) beside the\n * main content area, which renders the object's content surface (or its title when collapsed). The\n * rail sticks to the top of the scroll viewport while the section is in view.\n */\n// TODO(burdon): All sections are intrinsic (content-sized) for now. Extrinsic content (e.g. a sketch\n// with no intrinsic height) should later get a Mosaic-native resizable height affordance.\n\n// Inline grip glyph for the drag preview. The native drag image does not rasterize external SVG sprite\n// `<use>` icons (e.g. `@dxos/react-ui` `Icon`), so the preview uses plain inline SVG circles instead.\nconst DragHandleGlyph = () => (\n <svg width={10} height={16} viewBox='0 0 10 16' aria-hidden className='shrink-0 text-fg-muted'>\n {[3, 8, 13].flatMap((cy) =>\n [2.5, 7.5].map((cx) => <circle key={`${cx}-${cy}`} cx={cx} cy={cy} r={1.1} fill='currentColor' />),\n )}\n </svg>\n);\n\nconst StackSection = ({ data, ...tileProps }: StackSectionProps) => {\n const { id, object } = data;\n const { t } = Hooks.useTranslation(meta.profile.key);\n const { attendableId: parentAttendableId, collapsed, onAdd, onMoveUp, onMoveDown, onCollapse, onDelete } = useStack();\n const [optionsMenuOpen, setOptionsMenuOpen] = useState(false);\n const attendableId = GraphPath.getCollectionObjectPath(parentAttendableId, object.id);\n const attentionAttrs = useAttentionAttributes(attendableId);\n const surfaceData = useMemo(() => ({ attendableId, subject: object }), [object, attendableId]);\n const isCollapsed = collapsed.has(id);\n const icon = Obj.getIcon(object)?.icon ?? 'ph--circle-dashed--regular';\n const title = Obj.getLabel(object, { fallback: 'typename' }) ?? t('untitled-section.title');\n\n const rail = (\n <div className='grid grid-rows-[min-content_1fr]'>\n <div className='p-1 dx-toolbar-surface'>\n <Menu.Root open={optionsMenuOpen} onOpenChange={({ open }) => setOptionsMenuOpen(open)}>\n <Menu.Trigger asChild>\n <AttentionSigil.Button size='md' attendableId={attendableId}>\n <Icon.Icon icon={icon} classNames='transition-opacity' />\n </AttentionSigil.Button>\n </Menu.Trigger>\n <Menu.Content>\n {isCollapsed ? (\n <Menu.Item\n item={{\n value: 'section.expand',\n label: t('expand.label'),\n icon: 'ph--arrows-out-line-vertical--regular',\n }}\n onClick={() => onCollapse(id, false)}\n data-testid='section.expand'\n />\n ) : (\n <Menu.Item\n item={{\n value: 'section.collapse',\n label: t('collapse.label'),\n icon: 'ph--arrows-in-line-vertical--regular',\n }}\n onClick={() => onCollapse(id, true)}\n data-testid='section.collapse'\n />\n )}\n <Menu.Separator />\n <Menu.Item\n item={{ value: 'section.add', label: t('add-section.label'), icon: 'ph--plus--regular' }}\n onClick={() => onAdd(id)}\n data-testid='section.add'\n />\n <Menu.Item\n item={{ value: 'section.move-up', label: t('move-up.label'), icon: 'ph--arrow-line-up--regular' }}\n onClick={() => onMoveUp(id)}\n data-testid='section.move-up'\n />\n <Menu.Item\n item={{ value: 'section.move-down', label: t('move-down.label'), icon: 'ph--arrow-line-down--regular' }}\n onClick={() => onMoveDown(id)}\n data-testid='section.move-down'\n />\n <Menu.Separator />\n <Menu.Item\n item={{ value: 'section.remove', label: t('remove-section.label'), icon: 'ph--trash--regular' }}\n onClick={() => onDelete(id)}\n data-testid='section.remove'\n />\n </Menu.Content>\n </Menu.Root>\n </div>\n <div className='p-1'>\n {/* Inline glyph (not a sprite `Icon`) so the handle stays visible in the tile's native drag image. */}\n <Mosaic.DragHandle\n label={t('drag-handle.label')}\n classNames='p-1 min-h-0 w-(--dx-rail-item) h-(--dx-rail-item)'\n testId='section.drag-handle'\n >\n <DragHandleGlyph />\n </Mosaic.DragHandle>\n </div>\n </div>\n );\n\n return (\n <Mosaic.Tile\n {...tileProps}\n data={data}\n classNames='grid grid-cols-[var(--dx-rail-action)_1fr] dx-attention-surface border border-separator-subtle'\n >\n <div className='border-e border-separator-subtle'>\n <div className='sticky top-0 flex flex-col items-center'>{rail}</div>\n </div>\n <div {...attentionAttrs} className='min-w-0'>\n <span className='sr-only'>{title}</span>\n {isCollapsed ? (\n <div className='h-(--dx-toolbar-size) flex p-1'>\n <h2 className='flex items-center font-medium'>{title}</h2>\n </div>\n ) : (\n <Surface.Surface type={AppSurface.Section} data={surfaceData} limit={1} />\n )}\n </div>\n </Mosaic.Tile>\n );\n};\n\nStackSection.displayName = 'Stack.Section';\n\n//\n// Stack\n//\n\nexport const Stack = {\n Root: StackRoot,\n Content: StackContent,\n Viewport: StackViewport,\n Section: StackSection,\n};\n\nexport type { StackContentProps, StackRootProps, StackSectionProps, StackViewportProps };\n"],"mappings":";;;;;;;;;;;;;;;AAWA,IAAa,eAAe,cAA4C,KAAA,CAAS;AAEjF,IAAa,mBAAmB,aAAuC;CACrE,MAAM,UAAU,WAAW,YAAY;CACvC,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,KAAK,SAAS,uCAAuC;CAEvE,OAAO;AACT;;AAGA,IAAa,iBAAoC,gBAAgB,UAAU;;;AC0D3E,IAAM,aAAa,EACjB,IACA,cACA,UACA,cACA,WACA,YACA,OACA,UACA,YACA,eACoB;CACpB,MAAM,CAAC,UAAU,eAAe,SAA6B,IAAI;CACjE,MAAM,QAAQ,eACL;EACL;EACA,cAAc,gBAAgB;GAAE;GAAI,eAAe;EAAM;EACzD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,IACA;EAAC;EAAI;EAAc;EAAU;EAAc;EAAW;EAAY;EAAO;EAAU;EAAY;CAAQ,CACzG;CAEA,OAAO,oBAAC,aAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AAC/E;AAEA,UAAU,cAAc;AAQxB,IAAM,eAAe,YAA+C,EAAE,UAAU,GAAG,SAAS,iBAAiB;CAC3G,MAAM,EAAE,cAAc,aAAa,gBAAgB,eAAe;CAClE,OACE,oBAAC,OAAO,WAAR;EAAkB,SAAA;EAAQ,aAAY;EAAW,YAAY;EAAwB;EACnF,UAAA,oBAAC,WAAW,MAAZ;GAAiB,GAAI;GAAO,aAAY;GAAW,KAAK;GACrD;EACc,CAAA;CACD,CAAA;AAEtB,CAAC;AAED,aAAa,cAAc;AAQ3B,IAAM,gBAAgB,YACnB,EAAE,YAAY,YAAY,iBAA+C;CACxE,MAAM,EAAE,gBAAgB,gBAAgB,gBAAgB;CAExD,MAAM,UAAU,aACb,YAAmC;EAClC,YAAY,OAAO;EACnB,IAAI,OAAO,iBAAiB,YAC1B,aAAa,OAAO;OACf,IAAI,cACT,aAAa,UAAU;CAE3B,GACA,CAAC,aAAa,YAAY,CAC5B;CAEA,OACE,oBAAC,WAAW,UAAZ;EAAiC;EAAY,KAAK;EAC/C;CACkB,CAAA;AAEzB,CACF;AAEA,cAAc,cAAc;;;;;;AAkB5B,IAAM,wBACJ,oBAAC,OAAD;CAAK,OAAO;CAAI,QAAQ;CAAI,SAAQ;CAAY,eAAA;CAAY,WAAU;CACnE,UAAA;EAAC;EAAG;EAAG;CAAE,CAAC,CAAC,SAAS,OACnB,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,OAAO,oBAAC,UAAD;EAAgC;EAAQ;EAAI,GAAG;EAAK,MAAK;CAAgB,GAA5D,GAAG,GAAG,GAAG,IAAmD,CAAC,CACnG;AACG,CAAA;AAGP,IAAM,gBAAgB,EAAE,MAAM,GAAG,gBAAmC;CAClE,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,EAAE,MAAM,MAAM,eAAe,KAAK,QAAQ,GAAG;CACnD,MAAM,EAAE,cAAc,oBAAoB,WAAW,OAAO,UAAU,YAAY,YAAY,aAAa,SAAS;CACpH,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,KAAK;CAC5D,MAAM,eAAe,UAAU,wBAAwB,oBAAoB,OAAO,EAAE;CACpF,MAAM,iBAAiB,uBAAuB,YAAY;CAC1D,MAAM,cAAc,eAAe;EAAE;EAAc,SAAS;CAAO,IAAI,CAAC,QAAQ,YAAY,CAAC;CAC7F,MAAM,cAAc,UAAU,IAAI,EAAE;CACpC,MAAM,OAAO,IAAI,QAAQ,MAAM,CAAC,EAAE,QAAQ;CAC1C,MAAM,QAAQ,IAAI,SAAS,QAAQ,EAAE,UAAU,WAAW,CAAC,KAAK,EAAE,wBAAwB;CAE1F,MAAM,OACJ,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,OAAD;GAAK,WAAU;GACb,UAAA,qBAAC,KAAK,MAAN;IAAW,MAAM;IAAiB,eAAe,EAAE,WAAW,mBAAmB,IAAI;IAArF,UAAA,CACE,oBAAC,KAAK,SAAN;KAAc,SAAA;KACZ,UAAA,oBAAC,eAAe,QAAhB;MAAuB,MAAK;MAAmB;MAC7C,UAAA,oBAAC,KAAK,MAAN;OAAiB;OAAM,YAAW;MAAsB,CAAA;KACnC,CAAA;IACX,CAAA,GACd,qBAAC,KAAK,SAAN,EAAA,UAAA;KACG,cACC,oBAAC,KAAK,MAAN;MACE,MAAM;OACJ,OAAO;OACP,OAAO,EAAE,cAAc;OACvB,MAAM;MACR;MACA,eAAe,WAAW,IAAI,KAAK;MACnC,eAAY;KACb,CAAA,IAED,oBAAC,KAAK,MAAN;MACE,MAAM;OACJ,OAAO;OACP,OAAO,EAAE,gBAAgB;OACzB,MAAM;MACR;MACA,eAAe,WAAW,IAAI,IAAI;MAClC,eAAY;KACb,CAAA;KAEH,oBAAC,KAAK,WAAN,CAAiB,CAAA;KACjB,oBAAC,KAAK,MAAN;MACE,MAAM;OAAE,OAAO;OAAe,OAAO,EAAE,mBAAmB;OAAG,MAAM;MAAoB;MACvF,eAAe,MAAM,EAAE;MACvB,eAAY;KACb,CAAA;KACD,oBAAC,KAAK,MAAN;MACE,MAAM;OAAE,OAAO;OAAmB,OAAO,EAAE,eAAe;OAAG,MAAM;MAA6B;MAChG,eAAe,SAAS,EAAE;MAC1B,eAAY;KACb,CAAA;KACD,oBAAC,KAAK,MAAN;MACE,MAAM;OAAE,OAAO;OAAqB,OAAO,EAAE,iBAAiB;OAAG,MAAM;MAA+B;MACtG,eAAe,WAAW,EAAE;MAC5B,eAAY;KACb,CAAA;KACD,oBAAC,KAAK,WAAN,CAAiB,CAAA;KACjB,oBAAC,KAAK,MAAN;MACE,MAAM;OAAE,OAAO;OAAkB,OAAO,EAAE,sBAAsB;OAAG,MAAM;MAAqB;MAC9F,eAAe,SAAS,EAAE;MAC1B,eAAY;KACb,CAAA;IACW,EAAA,CAAA,CACL;;EACR,CAAA,GACL,oBAAC,OAAD;GAAK,WAAU;GAEb,UAAA,oBAAC,OAAO,YAAR;IACE,OAAO,EAAE,mBAAmB;IAC5B,YAAW;IACX,QAAO;IAEP,UAAA,oBAAC,iBAAD,CAAkB,CAAA;GACD,CAAA;EAChB,CAAA,CACF;;CAGP,OACE,qBAAC,OAAO,MAAR;EACE,GAAI;EACE;EACN,YAAW;EAHb,UAAA,CAKE,oBAAC,OAAD;GAAK,WAAU;GACb,UAAA,oBAAC,OAAD;IAAK,WAAU;IAA2C,UAAA;GAAU,CAAA;EACjE,CAAA,GACL,qBAAC,OAAD;GAAK,GAAI;GAAgB,WAAU;GAAnC,UAAA,CACE,oBAAC,QAAD;IAAM,WAAU;IAAW,UAAA;GAAY,CAAA,GACtC,cACC,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,oBAAC,MAAD;KAAI,WAAU;KAAiC,UAAA;IAAU,CAAA;GACtD,CAAA,IAEL,oBAAC,QAAQ,SAAT;IAAiB,MAAM,WAAW;IAAS,MAAM;IAAa,OAAO;GAAI,CAAA,CAExE;EACM,CAAA,CAAA;;AAEjB;AAEA,aAAa,cAAc;AAM3B,IAAa,QAAQ;CACnB,MAAM;CACN,SAAS;CACT,UAAU;CACV,SAAS;AACX"}
|
|
1
|
+
{"version":3,"file":"components.mjs","names":[],"sources":["../../src/components/Stack/StackContext.ts","../../src/components/Stack/Stack.tsx"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport { createContext, useContext } from 'react';\n\nimport { type StackContextType, type StackContextValue } from './Stack.tsx';\n\n// Kept out of `Stack.tsx`: react-refresh only fast-refreshes a module whose exports are all\n// components, so a context and its hook exported beside them force a full page reload on every edit.\n\nexport const StackContext = createContext<StackContextType | undefined>(undefined);\n\nexport const useStackContext = (consumer: string): StackContextType => {\n const context = useContext(StackContext);\n if (!context) {\n throw new Error(`\\`${consumer}\\` must be used within \\`Stack.Root\\`.`);\n }\n return context;\n};\n\n/** Section-level callbacks consumed by stack sections. */\nexport const useStack = (): StackContextValue => useStackContext('useStack');\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport React, {\n type ComponentPropsWithoutRef,\n type ForwardedRef,\n type PropsWithChildren,\n forwardRef,\n useCallback,\n useMemo,\n useState,\n} from 'react';\n\nimport * as Surface from '@dxos/app-framework/Surface';\nimport * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport * as AttentionSigil from '@dxos/app-toolkit/AttentionSigil';\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport { Obj } from '@dxos/echo';\nimport { useAttentionAttributes } from '@dxos/react-ui-attention';\nimport { type DndContainerHandler } from '@dxos/react-ui-dnd';\nimport { Mosaic, type MosaicTileProps } from '@dxos/react-ui-mosaic';\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as Icon from '@dxos/react-ui/Icon';\nimport * as Layout from '@dxos/react-ui/Layout';\nimport * as Menu from '@dxos/react-ui/Menu';\nimport * as ScrollArea from '@dxos/react-ui/ScrollArea';\nimport type * as Util from '@dxos/react-ui/Util';\n\nimport { meta } from '#meta';\n\nimport { StackContext, useStack, useStackContext } from './StackContext.ts';\n\n//\n// Types\n//\n\nexport type StackSectionItem = {\n id: string;\n object: Obj.Unknown;\n};\n\n//\n// Context\n//\n\n/** Section-level callbacks consumed by stack sections. */\nexport type StackContextValue = {\n attendableId: string;\n /** Ids of sections that are currently collapsed. */\n collapsed: ReadonlySet<string>;\n onCollapse: (id: string, collapsed: boolean) => void;\n /** Add a new section immediately after the given section. */\n onAdd: (id: string) => void;\n onMoveUp: (id: string) => void;\n onMoveDown: (id: string) => void;\n onDelete: (id: string) => void;\n};\n\nexport type StackContextType = StackContextValue & {\n /** Container id used to scope Mosaic drag/drop. */\n id: string;\n eventHandler: DndContainerHandler;\n /** Scroll viewport element; threaded to the Mosaic container for autoscroll. */\n viewport: HTMLElement | null;\n setViewport: (element: HTMLElement | null) => void;\n};\n\n//\n// Root\n//\n\ntype StackRootProps = PropsWithChildren<\n StackContextValue & {\n /** Container id used to scope Mosaic drag/drop. */\n id: string;\n /** Drag/drop event handler; defaults to a read-only (no-drop) handler. */\n eventHandler?: DndContainerHandler;\n }\n>;\n\nconst StackRoot = ({\n id,\n eventHandler,\n children,\n attendableId,\n collapsed,\n onCollapse,\n onAdd,\n onMoveUp,\n onMoveDown,\n onDelete,\n}: StackRootProps) => {\n const [viewport, setViewport] = useState<HTMLElement | null>(null);\n const value = useMemo<StackContextType>(\n () => ({\n id,\n eventHandler: eventHandler ?? { id, canDrop: () => false },\n viewport,\n setViewport,\n attendableId,\n collapsed,\n onCollapse,\n onAdd,\n onMoveUp,\n onMoveDown,\n onDelete,\n }),\n [id, eventHandler, viewport, attendableId, collapsed, onCollapse, onAdd, onMoveUp, onMoveDown, onDelete],\n );\n\n return <StackContext.Provider value={value}>{children}</StackContext.Provider>;\n};\n\nStackRoot.displayName = 'Stack.Root';\n\n//\n// Content\n//\n\ntype StackContentProps = Util.ThemedClassName<ComponentPropsWithoutRef<'div'>>;\n\nconst StackContent = forwardRef<HTMLDivElement, StackContentProps>(({ children, ...props }, forwardedRef) => {\n const { eventHandler, viewport } = useStackContext('Stack.Content');\n return (\n <Mosaic.Container asChild orientation='vertical' autoScroll={viewport} eventHandler={eventHandler}>\n <ScrollArea.Root {...props} orientation='vertical' ref={forwardedRef}>\n {children}\n </ScrollArea.Root>\n </Mosaic.Container>\n );\n});\n\nStackContent.displayName = 'Stack.Content';\n\n//\n// Viewport\n//\n\ntype StackViewportProps = Util.ThemedClassName<PropsWithChildren>;\n\nconst StackViewport = forwardRef<HTMLDivElement, StackViewportProps>(\n ({ classNames, children }, forwardedRef: ForwardedRef<HTMLDivElement>) => {\n const { setViewport } = useStackContext('Stack.Viewport');\n // Capture the viewport element for autoscroll while still honouring a forwarded ref.\n const setRefs = useCallback(\n (element: HTMLDivElement | null) => {\n setViewport(element);\n if (typeof forwardedRef === 'function') {\n forwardedRef(element);\n } else if (forwardedRef) {\n forwardedRef.current = element;\n }\n },\n [setViewport, forwardedRef],\n );\n\n return (\n <ScrollArea.Viewport classNames={classNames} ref={setRefs}>\n {children}\n </ScrollArea.Viewport>\n );\n },\n);\n\nStackViewport.displayName = 'Stack.Viewport';\n\n//\n// Section\n//\n\ntype StackSectionProps = MosaicTileProps<StackSectionItem>;\n\n/**\n * A stack section rendered as a Mosaic tile: a 40px left rail (drag handle + section menu) beside the\n * main content area, which renders the object's content surface (or its title when collapsed). The\n * rail sticks to the top of the scroll viewport while the section is in view.\n */\n// TODO(burdon): All sections are intrinsic (content-sized) for now. Extrinsic content (e.g. a sketch\n// with no intrinsic height) should later get a Mosaic-native resizable height affordance.\n\n// Inline grip glyph for the drag preview. The native drag image does not rasterize external SVG sprite\n// `<use>` icons (e.g. `@dxos/react-ui` `Icon`), so the preview uses plain inline SVG circles instead.\nconst DragHandleGlyph = () => (\n <svg width={10} height={16} viewBox='0 0 10 16' aria-hidden className='shrink-0 text-fg-muted'>\n {[3, 8, 13].flatMap((cy) =>\n [2.5, 7.5].map((cx) => <circle key={`${cx}-${cy}`} cx={cx} cy={cy} r={1.1} fill='currentColor' />),\n )}\n </svg>\n);\n\nconst StackSection = ({ data, ...tileProps }: StackSectionProps) => {\n const { id, object } = data;\n const { t } = Hooks.useTranslation(meta.profile.key);\n const { attendableId: parentAttendableId, collapsed, onAdd, onMoveUp, onMoveDown, onCollapse, onDelete } = useStack();\n const [optionsMenuOpen, setOptionsMenuOpen] = useState(false);\n const attendableId = GraphPath.getCollectionObjectPath(parentAttendableId, object.id);\n const attentionAttrs = useAttentionAttributes(attendableId);\n const surfaceData = useMemo(() => ({ attendableId, subject: object }), [object, attendableId]);\n const isCollapsed = collapsed.has(id);\n const icon = Obj.getIcon(object)?.icon ?? 'ph--circle-dashed--regular';\n const title = Obj.getLabel(object, { fallback: 'typename' }) ?? t('untitled-section.title');\n\n const rail = (\n <Layout.Grid rows={['min', 'fill']}>\n <div className='p-1 dx-toolbar-surface'>\n <Menu.Root open={optionsMenuOpen} onOpenChange={({ open }) => setOptionsMenuOpen(open)}>\n <Menu.Trigger asChild>\n <AttentionSigil.Button size='md' attendableId={attendableId}>\n <Icon.Icon icon={icon} classNames='transition-opacity' />\n </AttentionSigil.Button>\n </Menu.Trigger>\n <Menu.Content>\n {isCollapsed ? (\n <Menu.Item\n item={{\n value: 'section.expand',\n label: t('expand.label'),\n icon: 'ph--arrows-out-line-vertical--regular',\n }}\n onClick={() => onCollapse(id, false)}\n data-testid='section.expand'\n />\n ) : (\n <Menu.Item\n item={{\n value: 'section.collapse',\n label: t('collapse.label'),\n icon: 'ph--arrows-in-line-vertical--regular',\n }}\n onClick={() => onCollapse(id, true)}\n data-testid='section.collapse'\n />\n )}\n <Menu.Separator />\n <Menu.Item\n item={{ value: 'section.add', label: t('add-section.label'), icon: 'ph--plus--regular' }}\n onClick={() => onAdd(id)}\n data-testid='section.add'\n />\n <Menu.Item\n item={{ value: 'section.move-up', label: t('move-up.label'), icon: 'ph--arrow-line-up--regular' }}\n onClick={() => onMoveUp(id)}\n data-testid='section.move-up'\n />\n <Menu.Item\n item={{ value: 'section.move-down', label: t('move-down.label'), icon: 'ph--arrow-line-down--regular' }}\n onClick={() => onMoveDown(id)}\n data-testid='section.move-down'\n />\n <Menu.Separator />\n <Menu.Item\n item={{ value: 'section.remove', label: t('remove-section.label'), icon: 'ph--trash--regular' }}\n onClick={() => onDelete(id)}\n data-testid='section.remove'\n />\n </Menu.Content>\n </Menu.Root>\n </div>\n <div className='p-1'>\n {/* Inline glyph (not a sprite `Icon`) so the handle stays visible in the tile's native drag image. */}\n <Mosaic.DragHandle\n label={t('drag-handle.label')}\n classNames='p-1 min-h-0 w-(--dx-rail-item) h-(--dx-rail-item)'\n testId='section.drag-handle'\n >\n <DragHandleGlyph />\n </Mosaic.DragHandle>\n </div>\n </Layout.Grid>\n );\n\n return (\n <Mosaic.Tile\n {...tileProps}\n data={data}\n classNames='grid grid-cols-[var(--dx-rail-action)_1fr] dx-attention-surface border border-separator-subtle'\n >\n <div className='border-e border-separator-subtle'>\n <Layout.Flex column align='center' classNames='sticky top-0'>\n {rail}\n </Layout.Flex>\n </div>\n <div {...attentionAttrs} className='min-w-0'>\n <span className='sr-only'>{title}</span>\n {isCollapsed ? (\n <Layout.Flex classNames='h-(--dx-toolbar-size) p-1'>\n <h2 className='flex items-center font-medium'>{title}</h2>\n </Layout.Flex>\n ) : (\n <Surface.Surface type={AppSurface.Section} data={surfaceData} limit={1} />\n )}\n </div>\n </Mosaic.Tile>\n );\n};\n\nStackSection.displayName = 'Stack.Section';\n\n//\n// Stack\n//\n\nexport const Stack = {\n Root: StackRoot,\n Content: StackContent,\n Viewport: StackViewport,\n Section: StackSection,\n};\n\nexport type { StackContentProps, StackRootProps, StackSectionProps, StackViewportProps };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stack.d.ts","sourceRoot":"","sources":["../../../../../src/components/Stack/Stack.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EACZ,KAAK,wBAAwB,EAE7B,KAAK,iBAAiB,EAKvB,MAAM,OAAO,CAAC;AAMf,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAU,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"Stack.d.ts","sourceRoot":"","sources":["../../../../../src/components/Stack/Stack.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EACZ,KAAK,wBAAwB,EAE7B,KAAK,iBAAiB,EAKvB,MAAM,OAAO,CAAC;AAMf,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAU,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMrE,OAAO,KAAK,KAAK,IAAI,MAAM,qBAAqB,CAAC;AAUjD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;CACrB,CAAC;AAMF,0DAA0D;AAC1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,6DAA6D;IAC7D,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IACjD,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,mBAAmB,CAAC;IAClC,gFAAgF;IAChF,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CACpD,CAAC;AAMF,KAAK,cAAc,GAAG,iBAAiB,CACrC,iBAAiB,GAAG;IAClB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC,CACF,CAAC;AAEF,QAAA,MAAM,SAAS;kHAWZ,cAAc;;CAoBhB,CAAC;AAQF,KAAK,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAC;AAmB/E,KAAK,kBAAkB,GAAG,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAgClE,KAAK,iBAAiB,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;AAoB3D,QAAA,MAAM,YAAY;6BAA4B,iBAAiB;;CAwG9D,CAAC;AAQF,eAAO,MAAM,KAAK;IAChB,IAAI;IACJ,OAAO;;;IACP,QAAQ;;;;;IACR,OAAO;CACR,CAAC;AAEF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAC"}
|