@casualoffice/sheets 0.9.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/LICENSE +200 -0
- package/dist/embed/embed-runtime.js +537 -0
- package/dist/embed/embed.html +29 -0
- package/dist/embed/parser.worker.js +48474 -0
- package/dist/embed.cjs +225 -0
- package/dist/embed.cjs.map +1 -0
- package/dist/embed.d.cts +100 -0
- package/dist/embed.d.ts +100 -0
- package/dist/embed.js +204 -0
- package/dist/embed.js.map +1 -0
- package/dist/index.cjs +1549 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1530 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.worker.cjs +48469 -0
- package/dist/parser.worker.cjs.map +1 -0
- package/dist/parser.worker.js +48474 -0
- package/dist/parser.worker.js.map +1 -0
- package/dist/protocol--KyBQUjU.d.cts +171 -0
- package/dist/protocol-cEzy7S0i.d.ts +171 -0
- package/dist/sheets.cjs +677 -0
- package/dist/sheets.cjs.map +1 -0
- package/dist/sheets.d.cts +177 -0
- package/dist/sheets.d.ts +177 -0
- package/dist/sheets.js +658 -0
- package/dist/sheets.js.map +1 -0
- package/dist/signing.cjs +706 -0
- package/dist/signing.cjs.map +1 -0
- package/dist/signing.d.cts +141 -0
- package/dist/signing.d.ts +141 -0
- package/dist/signing.js +683 -0
- package/dist/signing.js.map +1 -0
- package/dist/styles.cjs +10 -0
- package/dist/styles.cjs.map +1 -0
- package/dist/styles.d.cts +2 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.js +8 -0
- package/dist/styles.js.map +1 -0
- package/dist/types-s_O0u6Cg.d.cts +90 -0
- package/dist/types-s_O0u6Cg.d.ts +90 -0
- package/dist/univer.cjs +220 -0
- package/dist/univer.cjs.map +1 -0
- package/dist/univer.d.cts +60 -0
- package/dist/univer.d.ts +60 -0
- package/dist/univer.js +187 -0
- package/dist/univer.js.map +1 -0
- package/dist/xlsx.cjs +3388 -0
- package/dist/xlsx.cjs.map +1 -0
- package/dist/xlsx.d.cts +383 -0
- package/dist/xlsx.d.ts +383 -0
- package/dist/xlsx.js +3383 -0
- package/dist/xlsx.js.map +1 -0
- package/package.json +293 -0
- package/src/embed/EmbedHostTransport.ts +226 -0
- package/src/embed/EmbedTransport.ts +323 -0
- package/src/embed/EmbedTransport.unit.test.ts +161 -0
- package/src/embed/index.ts +40 -0
- package/src/embed/protocol.ts +258 -0
- package/src/embed-runtime/embed.html +29 -0
- package/src/embed-runtime/index.tsx +440 -0
- package/src/index.ts +16 -0
- package/src/sheets/CasualSheets.tsx +319 -0
- package/src/sheets/CasualSheetsIframe.tsx +220 -0
- package/src/sheets/api.ts +108 -0
- package/src/sheets/index.ts +11 -0
- package/src/signing/SigningPane.tsx +374 -0
- package/src/signing/SigningProvider.tsx +126 -0
- package/src/signing/captures.tsx +316 -0
- package/src/signing/controller.ts +151 -0
- package/src/signing/controller.unit.test.ts +133 -0
- package/src/signing/index.ts +44 -0
- package/src/signing/types.ts +89 -0
- package/src/styles.ts +16 -0
- package/src/univer/index.ts +17 -0
- package/src/univer/lazy-plugins.ts +280 -0
- package/src/xlsx/_perf.ts +14 -0
- package/src/xlsx/_snapshot-constants.ts +15 -0
- package/src/xlsx/comments-resource.ts +209 -0
- package/src/xlsx/constants.ts +9 -0
- package/src/xlsx/data-validation-resource.ts +219 -0
- package/src/xlsx/import.ts +35 -0
- package/src/xlsx/index.ts +40 -0
- package/src/xlsx/page-setup-resource.ts +205 -0
- package/src/xlsx/parse-impl.ts +418 -0
- package/src/xlsx/parse-in-worker.ts +82 -0
- package/src/xlsx/parser.worker.ts +39 -0
- package/src/xlsx/passthrough-resource.ts +175 -0
- package/src/xlsx/pivot-passthrough.ts +359 -0
- package/src/xlsx/style-mapping.ts +171 -0
- package/src/xlsx/tables-resource.ts +211 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/signing/SigningProvider.tsx","../src/signing/controller.ts","../src/signing/SigningPane.tsx","../src/signing/captures.tsx"],"sourcesContent":["/**\n * SigningProvider — wraps a signing session in React context so\n * descendant components (SigningPane, capture surfaces, and the\n * editor's field-highlight decorations) all see the same snapshot.\n *\n * The pure state machine lives in `./controller.ts`; this file\n * just bridges it to React.\n */\n\nimport { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nimport {\n createSigningController,\n type SigningController,\n type SigningSnapshot,\n} from './controller';\nimport type {\n CancelReason,\n SignatureCompletePayload,\n SignedFieldPayload,\n SigningSessionConfig,\n} from './types';\n\ninterface SigningContextValue {\n controller: SigningController;\n snapshot: SigningSnapshot;\n /** Convenience wrapper around controller.signField that also fires the host's onFieldSigned. */\n signField: (payload: SignedFieldPayload) => Promise<void>;\n /** Convenience wrapper around controller.complete + host's onComplete. */\n completeIfReady: () => Promise<void>;\n /** Convenience wrapper around controller.cancel + host's onCancel. */\n cancel: (reason: CancelReason) => void;\n /** Source-of-truth document bytes the editor renders. Captured at\n * signing-session open; persists for the lifetime of the session. */\n baseDocumentBytes: ArrayBuffer | null;\n}\n\nconst SigningContext = createContext<SigningContextValue | null>(null);\n\nexport interface SigningProviderProps {\n /** Active signing session config. When null, signing is off and\n * children render unchanged. */\n session: SigningSessionConfig | null;\n /** Current document bytes the editor is rendering. Captured into\n * the context so the eventual `complete` payload carries the\n * right base buffer. */\n documentBytes: ArrayBuffer | null;\n children: ReactNode;\n}\n\nexport function SigningProvider({ session, documentBytes, children }: SigningProviderProps) {\n if (!session) {\n return <>{children}</>;\n }\n return (\n <SigningProviderInner session={session} documentBytes={documentBytes}>\n {children}\n </SigningProviderInner>\n );\n}\n\nfunction SigningProviderInner({\n session,\n documentBytes,\n children,\n}: {\n session: SigningSessionConfig;\n documentBytes: ArrayBuffer | null;\n children: ReactNode;\n}) {\n // Controller is constructed once per session-config identity.\n // Hosts that swap sessions mid-tree must change the React `key`\n // on the provider — otherwise stale state would leak.\n const controller = useMemo(\n () => createSigningController(session.fields, session.mode),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [session.fields, session.mode],\n );\n\n const [snapshot, setSnapshot] = useState<SigningSnapshot>(() => controller.snapshot());\n\n useEffect(() => {\n const unsub = controller.subscribe(setSnapshot);\n return unsub;\n }, [controller]);\n\n const value = useMemo<SigningContextValue>(\n () => ({\n controller,\n snapshot,\n signField: async (payload) => {\n controller.signField(payload);\n await session.onFieldSigned?.(payload);\n },\n completeIfReady: async () => {\n if (!controller.snapshot().canComplete) return;\n const final = controller.complete();\n const completePayload: SignatureCompletePayload = {\n fieldIds: final.fields\n .map((f) => f.fieldId)\n .filter((id) => final.signed[id] !== undefined),\n bytes: documentBytes ?? new ArrayBuffer(0),\n fields: final.signed,\n };\n await session.onComplete?.(completePayload);\n },\n cancel: (reason) => {\n controller.cancel();\n session.onCancel?.({ reason });\n },\n baseDocumentBytes: documentBytes,\n }),\n [controller, snapshot, session, documentBytes],\n );\n\n return <SigningContext.Provider value={value}>{children}</SigningContext.Provider>;\n}\n\n/**\n * Hook for descendants of <SigningProvider>. Returns null when\n * no signing session is active — caller renders its non-signing\n * shape.\n */\nexport function useSigning(): SigningContextValue | null {\n return useContext(SigningContext);\n}\n","/**\n * Signing state machine — pure with respect to React so bun-test\n * can exercise it without a renderer. The React wrappers\n * (SigningProvider / SigningPane) sit on top of this.\n *\n * Single source of truth for \"which field is the signer on,\n * which fields are done, can we complete yet\" — the React layer\n * never recomputes these from scratch.\n */\n\nimport type { SignedFieldPayload, SignatureField, SignatureMode } from './types';\n\nexport interface SigningSnapshot {\n fields: SignatureField[];\n mode: SignatureMode;\n /** Fields completed in order they were signed. */\n signed: Record<string, SignedFieldPayload>;\n /** Index into `fields` of the field the signer should focus on\n * next. -1 once everything required is done. */\n activeFieldIndex: number;\n /** True once every required field has a payload. */\n canComplete: boolean;\n /** True once the controller has received a completion command. */\n isComplete: boolean;\n /** True if a cancel has been emitted. */\n isCancelled: boolean;\n}\n\nexport interface SigningController {\n snapshot(): SigningSnapshot;\n subscribe(listener: (s: SigningSnapshot) => void): () => void;\n /** Record a signed field. Throws if the field id is unknown. */\n signField(payload: SignedFieldPayload): void;\n /** Move focus to a specific field (concurrent mode let users\n * pick); in sequential mode the controller silently no-ops if\n * the requested field isn't the next required. */\n focusField(fieldId: string): void;\n /** Mark the session complete. Returns the snapshot at the\n * moment of completion; throws if not yet canComplete. */\n complete(): SigningSnapshot;\n /** Mark the session cancelled. Idempotent. */\n cancel(): void;\n}\n\nexport function createSigningController(\n fields: SignatureField[],\n mode: SignatureMode,\n): SigningController {\n if (fields.length === 0) {\n throw new Error('createSigningController: at least one field required');\n }\n const fieldIds = new Set(fields.map((f) => f.fieldId));\n if (fieldIds.size !== fields.length) {\n throw new Error('createSigningController: duplicate fieldId');\n }\n\n const signed: Record<string, SignedFieldPayload> = {};\n let activeFieldIndex = nextRequiredIndex(fields, signed);\n let isComplete = false;\n let isCancelled = false;\n const listeners = new Set<(s: SigningSnapshot) => void>();\n\n function emit() {\n const snap = snapshotInternal();\n for (const l of listeners) l(snap);\n }\n\n function snapshotInternal(): SigningSnapshot {\n return {\n fields,\n mode,\n signed: { ...signed },\n activeFieldIndex,\n canComplete: allRequiredSigned(fields, signed),\n isComplete,\n isCancelled,\n };\n }\n\n return {\n snapshot: snapshotInternal,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n signField(payload) {\n if (isComplete || isCancelled) return;\n if (!fieldIds.has(payload.fieldId)) {\n throw new Error(`signField: unknown fieldId ${payload.fieldId}`);\n }\n signed[payload.fieldId] = payload;\n activeFieldIndex = nextRequiredIndex(fields, signed);\n emit();\n },\n focusField(fieldId) {\n if (isComplete || isCancelled) return;\n const idx = fields.findIndex((f) => f.fieldId === fieldId);\n if (idx < 0) return;\n if (mode === 'sequential') {\n // Sequential mode: only the next-required field is focusable.\n const next = nextRequiredIndex(fields, signed);\n if (idx !== next) return;\n }\n activeFieldIndex = idx;\n emit();\n },\n complete() {\n if (!allRequiredSigned(fields, signed)) {\n throw new Error('complete: required fields are still unsigned');\n }\n isComplete = true;\n activeFieldIndex = -1;\n emit();\n return snapshotInternal();\n },\n cancel() {\n if (isComplete || isCancelled) return;\n isCancelled = true;\n activeFieldIndex = -1;\n emit();\n },\n };\n}\n\n// ---------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------\n\nfunction allRequiredSigned(\n fields: SignatureField[],\n signed: Record<string, SignedFieldPayload>,\n): boolean {\n return fields.every((f) => !f.required || signed[f.fieldId] !== undefined);\n}\n\nfunction nextRequiredIndex(\n fields: SignatureField[],\n signed: Record<string, SignedFieldPayload>,\n): number {\n for (let i = 0; i < fields.length; i++) {\n if (fields[i].required && !signed[fields[i].fieldId]) return i;\n }\n // Either all required are done OR no required exists; pick the\n // first unsigned optional.\n for (let i = 0; i < fields.length; i++) {\n if (!signed[fields[i].fieldId]) return i;\n }\n return -1;\n}\n","/**\n * SigningPane — the floating sidebar that walks the signer through\n * fields. Lives inside <SigningProvider>; uses the controller via\n * `useSigning()`.\n *\n * Layout: a right-anchored panel showing\n * - Banner (optional, from session config)\n * - Field list with state markers (active / signed / pending)\n * - Method picker for the active field\n * - Capture surface (Drawn / Typed / Uploaded) depending on method\n * - Footer: Cancel + Complete\n *\n * The pane is presentation-only — every action routes through the\n * SigningProvider's helpers (signField, completeIfReady, cancel)\n * so the controller stays the single source of truth.\n */\n\nimport { useEffect, useState, type CSSProperties } from 'react';\n\nimport { useSigning } from './SigningProvider';\nimport type { SignatureField, SignatureMethod, SignedFieldPayload } from './types';\nimport {\n DrawnSignaturePad,\n TypedSignatureField,\n UploadedSignatureField,\n type CapturedSignature,\n} from './captures';\n\nexport interface SigningPaneProps {\n /** Optional banner override; falls back to session.banner. */\n banner?: string;\n /** Optional data-testid root. */\n testId?: string;\n}\n\nexport function SigningPane({ banner, testId = 'signing-pane' }: SigningPaneProps) {\n const ctx = useSigning();\n if (!ctx) return null;\n\n const { snapshot, signField, completeIfReady, cancel } = ctx;\n if (snapshot.isComplete || snapshot.isCancelled) return null;\n\n const active: SignatureField | null =\n snapshot.activeFieldIndex >= 0 ? snapshot.fields[snapshot.activeFieldIndex] : null;\n\n return (\n <aside style={paneStyle} role=\"region\" aria-label=\"Signing pane\" data-testid={testId}>\n {banner && (\n <div style={bannerStyle} data-testid={`${testId}-banner`}>\n {banner}\n </div>\n )}\n <div style={listStyle} data-testid={`${testId}-fields`}>\n {snapshot.fields.map((f, i) => {\n const isSigned = !!snapshot.signed[f.fieldId];\n const isActive = i === snapshot.activeFieldIndex;\n return (\n <div\n key={f.fieldId}\n style={listItemStyle(isActive, isSigned)}\n data-testid={`${testId}-field-${f.fieldId}`}\n data-state={isSigned ? 'signed' : isActive ? 'active' : 'pending'}\n >\n <span style={listIconStyle(isSigned)} aria-hidden=\"true\">\n {isSigned ? '✓' : i + 1}\n </span>\n <span style={listLabelStyle}>{f.label}</span>\n {!f.required && (\n <span style={optionalChipStyle} aria-label=\"Optional\">\n optional\n </span>\n )}\n </div>\n );\n })}\n </div>\n\n {active && (\n <ActiveFieldEditor\n field={active}\n testId={testId}\n onCapture={async (cap, method) => {\n const payload: SignedFieldPayload = {\n fieldId: active.fieldId,\n method,\n bytes: cap.bytes,\n mime: cap.mime,\n signedAt: new Date().toISOString(),\n };\n await signField(payload);\n }}\n />\n )}\n\n {!active && snapshot.canComplete && (\n <div style={completeBlockStyle} data-testid={`${testId}-complete-block`}>\n All required signatures collected. Ready to finalise.\n </div>\n )}\n\n <footer style={footerStyle}>\n <button\n type=\"button\"\n onClick={() => cancel('signer_cancelled')}\n style={secondaryBtnStyle()}\n data-testid={`${testId}-cancel`}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void completeIfReady()}\n disabled={!snapshot.canComplete}\n style={primaryBtnStyle(!snapshot.canComplete)}\n data-testid={`${testId}-complete`}\n >\n Complete\n </button>\n </footer>\n </aside>\n );\n}\n\n// ---------------------------------------------------------------\n// Active field editor — method picker + capture surface\n// ---------------------------------------------------------------\n\nfunction ActiveFieldEditor({\n field,\n testId,\n onCapture,\n}: {\n field: SignatureField;\n testId: string;\n onCapture: (cap: CapturedSignature, method: SignatureMethod) => void | Promise<void>;\n}) {\n const [method, setMethod] = useState<SignatureMethod>(field.methods[0]);\n\n // Reset the method picker whenever the active field changes — a\n // method that was valid for the previous field may not be in this\n // field's list.\n useEffect(() => {\n setMethod(field.methods[0]);\n }, [field]);\n\n return (\n <div style={editorStyle} data-testid={`${testId}-editor`}>\n <div style={editorHeaderStyle}>\n <div style={editorLabelStyle}>{field.label}</div>\n {field.signer?.name && <div style={editorSignerStyle}>{field.signer.name}</div>}\n </div>\n {field.methods.length > 1 && (\n <div style={methodTabsStyle} role=\"tablist\" data-testid={`${testId}-methods`}>\n {field.methods.map((m) => (\n <button\n key={m}\n type=\"button\"\n role=\"tab\"\n aria-selected={method === m}\n onClick={() => setMethod(m)}\n style={methodTabStyle(method === m)}\n data-testid={`${testId}-method-${m}`}\n >\n {methodLabel(m)}\n </button>\n ))}\n </div>\n )}\n <div style={captureWrapStyle}>\n {method === 'drawn' && <DrawnSignaturePad onCapture={(c) => onCapture(c, 'drawn')} />}\n {method === 'typed' && (\n <TypedSignatureField\n defaultText={field.signer?.name ?? ''}\n onCapture={(c) => onCapture(c, 'typed')}\n />\n )}\n {method === 'uploaded' && (\n <UploadedSignatureField onCapture={(c) => onCapture(c, 'uploaded')} />\n )}\n </div>\n </div>\n );\n}\n\nfunction methodLabel(m: SignatureMethod): string {\n switch (m) {\n case 'drawn':\n return 'Draw';\n case 'typed':\n return 'Type';\n case 'uploaded':\n return 'Upload';\n }\n}\n\n// ---------------------------------------------------------------\n// Styles\n// ---------------------------------------------------------------\n\nconst paneStyle: CSSProperties = {\n position: 'fixed',\n top: 16,\n right: 16,\n bottom: 16,\n width: 360,\n maxWidth: '100vw',\n display: 'flex',\n flexDirection: 'column',\n gap: 14,\n padding: 16,\n background: 'var(--doc-surface, #fff)',\n border: '1px solid var(--doc-border, #cbd5e1)',\n borderRadius: 12,\n boxShadow: '0 1px 1px rgba(0, 0, 0, 0.04), 0 6px 24px rgba(15, 23, 42, 0.12)',\n fontFamily: 'inherit',\n zIndex: 9000,\n};\n\nconst bannerStyle: CSSProperties = {\n padding: '8px 10px',\n background: 'var(--doc-surface-2, #f1f5f9)',\n border: '1px solid var(--doc-border-light, #e2e8f0)',\n borderRadius: 6,\n fontSize: 12,\n color: 'var(--doc-text-muted, #475569)',\n};\n\nconst listStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n};\n\nfunction listItemStyle(active: boolean, signed: boolean): CSSProperties {\n return {\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n padding: '8px 10px',\n borderRadius: 6,\n background: active ? 'var(--doc-surface-2, #f1f5f9)' : signed ? 'transparent' : 'transparent',\n border: active ? '1px solid var(--doc-border, #cbd5e1)' : '1px solid transparent',\n opacity: signed && !active ? 0.7 : 1,\n };\n}\n\nfunction listIconStyle(signed: boolean): CSSProperties {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 22,\n height: 22,\n borderRadius: '50%',\n background: signed ? 'var(--doc-accent, #2563eb)' : 'var(--doc-surface-2, #f1f5f9)',\n color: signed ? '#fff' : 'var(--doc-text-muted, #475569)',\n fontSize: 12,\n fontWeight: 600,\n flexShrink: 0,\n };\n}\n\nconst listLabelStyle: CSSProperties = {\n flex: 1,\n fontSize: 13,\n color: 'var(--doc-text, #0f172a)',\n fontWeight: 500,\n};\n\nconst optionalChipStyle: CSSProperties = {\n fontSize: 11,\n color: 'var(--doc-text-muted, #64748b)',\n padding: '2px 6px',\n background: 'var(--doc-surface-2, #f1f5f9)',\n borderRadius: 4,\n};\n\nconst editorStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n};\n\nconst editorHeaderStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n};\n\nconst editorLabelStyle: CSSProperties = {\n fontSize: 13,\n fontWeight: 600,\n color: 'var(--doc-text, #0f172a)',\n};\n\nconst editorSignerStyle: CSSProperties = {\n fontSize: 12,\n color: 'var(--doc-text-muted, #64748b)',\n};\n\nconst methodTabsStyle: CSSProperties = {\n display: 'flex',\n gap: 4,\n padding: 2,\n background: 'var(--doc-surface-2, #f1f5f9)',\n borderRadius: 6,\n};\n\nfunction methodTabStyle(selected: boolean): CSSProperties {\n return {\n flex: 1,\n padding: '6px 10px',\n background: selected ? 'var(--doc-surface, #fff)' : 'transparent',\n border: 'none',\n borderRadius: 4,\n fontSize: 12,\n fontWeight: 500,\n color: selected ? 'var(--doc-text, #0f172a)' : 'var(--doc-text-muted, #475569)',\n cursor: 'pointer',\n fontFamily: 'inherit',\n };\n}\n\nconst captureWrapStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n};\n\nconst completeBlockStyle: CSSProperties = {\n padding: '10px 12px',\n background: 'rgba(34, 197, 94, 0.08)',\n border: '1px solid rgba(34, 197, 94, 0.28)',\n borderRadius: 6,\n fontSize: 12,\n color: 'rgb(20, 83, 45)',\n};\n\nconst footerStyle: CSSProperties = {\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 8,\n marginTop: 'auto',\n paddingTop: 12,\n borderTop: '1px solid var(--doc-border-light, #e2e8f0)',\n};\n\nfunction primaryBtnStyle(disabled: boolean): CSSProperties {\n return {\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid transparent',\n background: disabled ? 'var(--doc-border, #cbd5e1)' : 'var(--doc-accent, #2563eb)',\n color: disabled ? 'var(--doc-text-muted, #64748b)' : '#fff',\n fontSize: 13,\n fontWeight: 600,\n cursor: disabled ? 'not-allowed' : 'pointer',\n fontFamily: 'inherit',\n };\n}\n\nfunction secondaryBtnStyle(): CSSProperties {\n return {\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid var(--doc-border, #cbd5e1)',\n background: 'transparent',\n color: 'var(--doc-text, #0f172a)',\n fontSize: 13,\n fontWeight: 500,\n cursor: 'pointer',\n fontFamily: 'inherit',\n };\n}\n","/**\n * Three signature-capture surfaces: drawn (canvas), typed (input\n * rendered in a script font), uploaded (image file picker).\n *\n * Each one emits an `{ bytes, mime }` pair that the parent feeds\n * into SigningContext.signField. The components are presentation +\n * input only — they don't know about the controller or the field\n * they're capturing for.\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\n\nexport interface CapturedSignature {\n bytes: ArrayBuffer;\n mime: string;\n}\n\n// ---------------------------------------------------------------\n// Drawn — canvas\n// ---------------------------------------------------------------\n\nexport interface DrawnSignaturePadProps {\n /** Fired when the user clicks \"Use this signature\". */\n onCapture: (sig: CapturedSignature) => void;\n /** Optional clear-button label override. */\n clearLabel?: string;\n /** Optional save-button label override. */\n saveLabel?: string;\n /** Canvas pixel size. Default 480 × 160. */\n width?: number;\n height?: number;\n}\n\nexport function DrawnSignaturePad({\n onCapture,\n clearLabel = 'Clear',\n saveLabel = 'Use this signature',\n width = 480,\n height = 160,\n}: DrawnSignaturePadProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const drawingRef = useRef(false);\n const [hasInk, setHasInk] = useState(false);\n\n // Set up a clean canvas on mount. Background is transparent so\n // the stamped image composites cleanly over the document.\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.strokeStyle = '#0f172a';\n ctx.lineWidth = 2;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n }, []);\n\n const start = (e: React.PointerEvent<HTMLCanvasElement>) => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n const { x, y } = pointerPos(e, canvas);\n ctx.beginPath();\n ctx.moveTo(x, y);\n drawingRef.current = true;\n canvas.setPointerCapture(e.pointerId);\n };\n\n const move = (e: React.PointerEvent<HTMLCanvasElement>) => {\n if (!drawingRef.current) return;\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n const { x, y } = pointerPos(e, canvas);\n ctx.lineTo(x, y);\n ctx.stroke();\n if (!hasInk) setHasInk(true);\n };\n\n const end = (e: React.PointerEvent<HTMLCanvasElement>) => {\n drawingRef.current = false;\n canvasRef.current?.releasePointerCapture(e.pointerId);\n };\n\n const clear = () => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n setHasInk(false);\n };\n\n const save = async () => {\n const canvas = canvasRef.current;\n if (!canvas || !hasInk) return;\n const blob = await new Promise<Blob | null>((resolve) => {\n canvas.toBlob((b) => resolve(b), 'image/png');\n });\n if (!blob) return;\n const bytes = await blob.arrayBuffer();\n onCapture({ bytes, mime: 'image/png' });\n clear();\n };\n\n return (\n <div style={padWrapStyle} data-testid=\"drawn-signature-pad\">\n <canvas\n ref={canvasRef}\n width={width}\n height={height}\n style={padCanvasStyle(width, height)}\n onPointerDown={start}\n onPointerMove={move}\n onPointerUp={end}\n onPointerLeave={end}\n data-testid=\"drawn-signature-canvas\"\n />\n <div style={padActionsStyle}>\n <button type=\"button\" onClick={clear} style={secondaryBtnStyle(false)}>\n {clearLabel}\n </button>\n <button\n type=\"button\"\n onClick={save}\n disabled={!hasInk}\n style={primaryBtnStyle(!hasInk)}\n data-testid=\"drawn-signature-save\"\n >\n {saveLabel}\n </button>\n </div>\n </div>\n );\n}\n\nfunction pointerPos(e: React.PointerEvent<HTMLCanvasElement>, canvas: HTMLCanvasElement) {\n const rect = canvas.getBoundingClientRect();\n const scaleX = canvas.width / rect.width;\n const scaleY = canvas.height / rect.height;\n return {\n x: (e.clientX - rect.left) * scaleX,\n y: (e.clientY - rect.top) * scaleY,\n };\n}\n\n// ---------------------------------------------------------------\n// Typed\n// ---------------------------------------------------------------\n\nexport interface TypedSignatureFieldProps {\n onCapture: (sig: CapturedSignature) => void;\n defaultText?: string;\n saveLabel?: string;\n}\n\nexport function TypedSignatureField({\n onCapture,\n defaultText = '',\n saveLabel = 'Use this signature',\n}: TypedSignatureFieldProps) {\n const [value, setValue] = useState(defaultText);\n const save = () => {\n const trimmed = value.trim();\n if (!trimmed) return;\n const bytes = new TextEncoder().encode(trimmed).buffer;\n onCapture({ bytes, mime: 'text/plain' });\n setValue('');\n };\n return (\n <div style={padWrapStyle} data-testid=\"typed-signature-field\">\n <input\n type=\"text\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"Type your full name\"\n style={typedInputStyle}\n data-testid=\"typed-signature-input\"\n autoFocus\n />\n <div style={padActionsStyle}>\n <button\n type=\"button\"\n onClick={save}\n disabled={!value.trim()}\n style={primaryBtnStyle(!value.trim())}\n data-testid=\"typed-signature-save\"\n >\n {saveLabel}\n </button>\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------\n// Uploaded\n// ---------------------------------------------------------------\n\nexport interface UploadedSignatureFieldProps {\n onCapture: (sig: CapturedSignature) => void;\n /** Accept attribute. Default image/*. */\n accept?: string;\n}\n\nexport function UploadedSignatureField({\n onCapture,\n accept = 'image/png,image/jpeg,image/svg+xml',\n}: UploadedSignatureFieldProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [fileName, setFileName] = useState<string | null>(null);\n const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n const bytes = await file.arrayBuffer();\n onCapture({ bytes, mime: file.type || 'application/octet-stream' });\n setFileName(file.name);\n if (inputRef.current) inputRef.current.value = '';\n };\n return (\n <div style={padWrapStyle} data-testid=\"uploaded-signature-field\">\n <label style={uploadLabelStyle}>\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n onChange={onChange}\n style={{ display: 'none' }}\n data-testid=\"uploaded-signature-input\"\n />\n <span>{fileName ?? 'Choose image…'}</span>\n </label>\n </div>\n );\n}\n\n// ---------------------------------------------------------------\n// Shared styles\n// ---------------------------------------------------------------\n\nconst padWrapStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 10,\n};\n\nconst padCanvasStyle = (w: number, h: number): CSSProperties => ({\n width: w,\n height: h,\n maxWidth: '100%',\n border: '1px dashed var(--doc-border, #cbd5e1)',\n borderRadius: 8,\n background: 'var(--doc-surface, #fff)',\n cursor: 'crosshair',\n touchAction: 'none',\n});\n\nconst padActionsStyle: CSSProperties = {\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 8,\n};\n\nconst typedInputStyle: CSSProperties = {\n width: '100%',\n padding: '10px 12px',\n border: '1px solid var(--doc-border, #cbd5e1)',\n borderRadius: 6,\n fontSize: 18,\n fontFamily: '\"Caveat\", \"Dancing Script\", \"Brush Script MT\", cursive',\n background: 'var(--doc-surface, #fff)',\n color: 'var(--doc-text, #0f172a)',\n};\n\nconst uploadLabelStyle: CSSProperties = {\n display: 'inline-flex',\n padding: '8px 14px',\n border: '1px dashed var(--doc-border, #cbd5e1)',\n borderRadius: 6,\n background: 'var(--doc-surface, #fff)',\n color: 'var(--doc-text, #0f172a)',\n fontSize: 13,\n cursor: 'pointer',\n alignSelf: 'flex-start',\n};\n\nfunction primaryBtnStyle(disabled: boolean): CSSProperties {\n return {\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid transparent',\n background: disabled ? 'var(--doc-border, #cbd5e1)' : 'var(--doc-accent, #2563eb)',\n color: disabled ? 'var(--doc-text-muted, #64748b)' : '#fff',\n fontSize: 13,\n fontWeight: 600,\n cursor: disabled ? 'not-allowed' : 'pointer',\n fontFamily: 'inherit',\n };\n}\n\nfunction secondaryBtnStyle(disabled: boolean): CSSProperties {\n return {\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid var(--doc-border, #cbd5e1)',\n background: 'transparent',\n color: 'var(--doc-text, #0f172a)',\n fontSize: 13,\n fontWeight: 500,\n cursor: disabled ? 'not-allowed' : 'pointer',\n fontFamily: 'inherit',\n };\n}\n"],"mappings":"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","names":["useEffect","useState","useEffect","useState","jsx","jsx","jsxs","secondaryBtnStyle","primaryBtnStyle","useState","useEffect"]}
|
package/dist/styles.cjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// src/styles.ts
|
|
4
|
+
var import_lib = require("@univerjs/design/lib/index.css");
|
|
5
|
+
var import_lib2 = require("@univerjs/ui/lib/index.css");
|
|
6
|
+
var import_lib3 = require("@univerjs/docs-ui/lib/index.css");
|
|
7
|
+
var import_lib4 = require("@univerjs/sheets-ui/lib/index.css");
|
|
8
|
+
var import_lib5 = require("@univerjs/sheets-formula-ui/lib/index.css");
|
|
9
|
+
var import_lib6 = require("@univerjs/sheets-numfmt-ui/lib/index.css");
|
|
10
|
+
//# sourceMappingURL=styles.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/styles.ts"],"sourcesContent":["/**\n * Univer plugin CSS — side-effect-only imports for the eager plugin\n * set CasualSheets boots. Hosts import this once at app boot:\n *\n * import '@casualoffice/sheets/styles';\n *\n * If the host adds lazy plugins (sort, filter, drawing, comments,\n * conditional formatting, …) it imports the corresponding CSS\n * separately — those plugins ship their own /lib/index.css.\n */\nimport '@univerjs/design/lib/index.css';\nimport '@univerjs/ui/lib/index.css';\nimport '@univerjs/docs-ui/lib/index.css';\nimport '@univerjs/sheets-ui/lib/index.css';\nimport '@univerjs/sheets-formula-ui/lib/index.css';\nimport '@univerjs/sheets-numfmt-ui/lib/index.css';\n"],"mappings":";;;AAUA,iBAAO;AACP,IAAAA,cAAO;AACP,IAAAA,cAAO;AACP,IAAAA,cAAO;AACP,IAAAA,cAAO;AACP,IAAAA,cAAO;","names":["import_lib"]}
|
package/dist/styles.d.ts
ADDED
package/dist/styles.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/styles.ts
|
|
2
|
+
import "@univerjs/design/lib/index.css";
|
|
3
|
+
import "@univerjs/ui/lib/index.css";
|
|
4
|
+
import "@univerjs/docs-ui/lib/index.css";
|
|
5
|
+
import "@univerjs/sheets-ui/lib/index.css";
|
|
6
|
+
import "@univerjs/sheets-formula-ui/lib/index.css";
|
|
7
|
+
import "@univerjs/sheets-numfmt-ui/lib/index.css";
|
|
8
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/styles.ts"],"sourcesContent":["/**\n * Univer plugin CSS — side-effect-only imports for the eager plugin\n * set CasualSheets boots. Hosts import this once at app boot:\n *\n * import '@casualoffice/sheets/styles';\n *\n * If the host adds lazy plugins (sort, filter, drawing, comments,\n * conditional formatting, …) it imports the corresponding CSS\n * separately — those plugins ship their own /lib/index.css.\n */\nimport '@univerjs/design/lib/index.css';\nimport '@univerjs/ui/lib/index.css';\nimport '@univerjs/docs-ui/lib/index.css';\nimport '@univerjs/sheets-ui/lib/index.css';\nimport '@univerjs/sheets-formula-ui/lib/index.css';\nimport '@univerjs/sheets-numfmt-ui/lib/index.css';\n"],"mappings":";AAUA,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;","names":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing types — mirror the iframe-protocol envelopes from
|
|
3
|
+
* `docs/internal/13-iframe-protocol.md` so the SDK and iframe
|
|
4
|
+
* deliveries can hand the same payloads back and forth.
|
|
5
|
+
*
|
|
6
|
+
* Uniform across `app: 'docs' | 'sheet'`. Only the `anchor`
|
|
7
|
+
* discriminator changes.
|
|
8
|
+
*/
|
|
9
|
+
type SignatureMethod = 'drawn' | 'typed' | 'uploaded';
|
|
10
|
+
type SignatureMode = 'sequential' | 'concurrent';
|
|
11
|
+
/**
|
|
12
|
+
* Doc-anchored field — paragraph id from the editor's `w14:paraId`,
|
|
13
|
+
* with an optional sub-paragraph search for placing the signature
|
|
14
|
+
* inside a phrase rather than a whole block.
|
|
15
|
+
*/
|
|
16
|
+
interface DocAnchor {
|
|
17
|
+
kind: 'doc';
|
|
18
|
+
paraId: string;
|
|
19
|
+
search?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Sheet-anchored field — `sheet` name + A1-style `cell` ref. */
|
|
22
|
+
interface SheetAnchor {
|
|
23
|
+
kind: 'sheet';
|
|
24
|
+
sheet: string;
|
|
25
|
+
cell: string;
|
|
26
|
+
}
|
|
27
|
+
type SignatureAnchor = DocAnchor | SheetAnchor;
|
|
28
|
+
interface SignatureField {
|
|
29
|
+
/** Per-field id supplied by the host; echoed on every progress event. */
|
|
30
|
+
fieldId: string;
|
|
31
|
+
/** Label rendered next to the field — "Employee signature", etc. */
|
|
32
|
+
label: string;
|
|
33
|
+
/** Required fields must complete before `onComplete` fires. */
|
|
34
|
+
required: boolean;
|
|
35
|
+
/** Where the signature lands in the document. */
|
|
36
|
+
anchor: SignatureAnchor;
|
|
37
|
+
/** Allowed signature methods. */
|
|
38
|
+
methods: SignatureMethod[];
|
|
39
|
+
/** Optional signer identity the host knows about. */
|
|
40
|
+
signer?: {
|
|
41
|
+
name?: string;
|
|
42
|
+
email?: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Payload emitted when a signer completes one field. */
|
|
46
|
+
interface SignedFieldPayload {
|
|
47
|
+
fieldId: string;
|
|
48
|
+
method: SignatureMethod;
|
|
49
|
+
/** Raw signature material — PNG for drawn, UTF-8 string-as-bytes for typed, host-attested bytes for uploaded. */
|
|
50
|
+
bytes: ArrayBuffer;
|
|
51
|
+
mime: string;
|
|
52
|
+
/** Client wall-clock at completion; host typically pairs with a server timestamp. */
|
|
53
|
+
signedAt: string;
|
|
54
|
+
/** Optional placement hint a host generating a flat PDF can use. */
|
|
55
|
+
placement?: {
|
|
56
|
+
page: number;
|
|
57
|
+
xPct: number;
|
|
58
|
+
yPct: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Payload emitted when all required fields are signed. */
|
|
62
|
+
interface SignatureCompletePayload {
|
|
63
|
+
fieldIds: string[];
|
|
64
|
+
/** Document bytes WITH stamps applied — note (v1): the editor
|
|
65
|
+
* returns the unmodified document and a `fields` map so the
|
|
66
|
+
* HOST stamps the final bytes. v2 lands editor-side stamping. */
|
|
67
|
+
bytes: ArrayBuffer;
|
|
68
|
+
fields: Record<string, SignedFieldPayload>;
|
|
69
|
+
}
|
|
70
|
+
type CancelReason = 'signer_cancelled' | 'session_expired' | 'host_aborted';
|
|
71
|
+
/**
|
|
72
|
+
* Configuration the host hands the editor when opening a signing
|
|
73
|
+
* session. Maps 1:1 to the iframe `signature.request` envelope.
|
|
74
|
+
*/
|
|
75
|
+
interface SigningSessionConfig {
|
|
76
|
+
fields: SignatureField[];
|
|
77
|
+
mode: SignatureMode;
|
|
78
|
+
/** Optional banner the editor renders at the top of the pane. */
|
|
79
|
+
banner?: string;
|
|
80
|
+
/** Fires once per field as the signer completes it. */
|
|
81
|
+
onFieldSigned?: (payload: SignedFieldPayload) => void | Promise<void>;
|
|
82
|
+
/** Fires after every required field is signed. */
|
|
83
|
+
onComplete?: (payload: SignatureCompletePayload) => void | Promise<void>;
|
|
84
|
+
/** Fires when either side aborts the session. */
|
|
85
|
+
onCancel?: (payload: {
|
|
86
|
+
reason: CancelReason;
|
|
87
|
+
}) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type { CancelReason as C, DocAnchor as D, SheetAnchor as S, SignatureAnchor as a, SignatureCompletePayload as b, SignatureField as c, SignatureMethod as d, SignatureMode as e, SignedFieldPayload as f, SigningSessionConfig as g };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing types — mirror the iframe-protocol envelopes from
|
|
3
|
+
* `docs/internal/13-iframe-protocol.md` so the SDK and iframe
|
|
4
|
+
* deliveries can hand the same payloads back and forth.
|
|
5
|
+
*
|
|
6
|
+
* Uniform across `app: 'docs' | 'sheet'`. Only the `anchor`
|
|
7
|
+
* discriminator changes.
|
|
8
|
+
*/
|
|
9
|
+
type SignatureMethod = 'drawn' | 'typed' | 'uploaded';
|
|
10
|
+
type SignatureMode = 'sequential' | 'concurrent';
|
|
11
|
+
/**
|
|
12
|
+
* Doc-anchored field — paragraph id from the editor's `w14:paraId`,
|
|
13
|
+
* with an optional sub-paragraph search for placing the signature
|
|
14
|
+
* inside a phrase rather than a whole block.
|
|
15
|
+
*/
|
|
16
|
+
interface DocAnchor {
|
|
17
|
+
kind: 'doc';
|
|
18
|
+
paraId: string;
|
|
19
|
+
search?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Sheet-anchored field — `sheet` name + A1-style `cell` ref. */
|
|
22
|
+
interface SheetAnchor {
|
|
23
|
+
kind: 'sheet';
|
|
24
|
+
sheet: string;
|
|
25
|
+
cell: string;
|
|
26
|
+
}
|
|
27
|
+
type SignatureAnchor = DocAnchor | SheetAnchor;
|
|
28
|
+
interface SignatureField {
|
|
29
|
+
/** Per-field id supplied by the host; echoed on every progress event. */
|
|
30
|
+
fieldId: string;
|
|
31
|
+
/** Label rendered next to the field — "Employee signature", etc. */
|
|
32
|
+
label: string;
|
|
33
|
+
/** Required fields must complete before `onComplete` fires. */
|
|
34
|
+
required: boolean;
|
|
35
|
+
/** Where the signature lands in the document. */
|
|
36
|
+
anchor: SignatureAnchor;
|
|
37
|
+
/** Allowed signature methods. */
|
|
38
|
+
methods: SignatureMethod[];
|
|
39
|
+
/** Optional signer identity the host knows about. */
|
|
40
|
+
signer?: {
|
|
41
|
+
name?: string;
|
|
42
|
+
email?: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Payload emitted when a signer completes one field. */
|
|
46
|
+
interface SignedFieldPayload {
|
|
47
|
+
fieldId: string;
|
|
48
|
+
method: SignatureMethod;
|
|
49
|
+
/** Raw signature material — PNG for drawn, UTF-8 string-as-bytes for typed, host-attested bytes for uploaded. */
|
|
50
|
+
bytes: ArrayBuffer;
|
|
51
|
+
mime: string;
|
|
52
|
+
/** Client wall-clock at completion; host typically pairs with a server timestamp. */
|
|
53
|
+
signedAt: string;
|
|
54
|
+
/** Optional placement hint a host generating a flat PDF can use. */
|
|
55
|
+
placement?: {
|
|
56
|
+
page: number;
|
|
57
|
+
xPct: number;
|
|
58
|
+
yPct: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Payload emitted when all required fields are signed. */
|
|
62
|
+
interface SignatureCompletePayload {
|
|
63
|
+
fieldIds: string[];
|
|
64
|
+
/** Document bytes WITH stamps applied — note (v1): the editor
|
|
65
|
+
* returns the unmodified document and a `fields` map so the
|
|
66
|
+
* HOST stamps the final bytes. v2 lands editor-side stamping. */
|
|
67
|
+
bytes: ArrayBuffer;
|
|
68
|
+
fields: Record<string, SignedFieldPayload>;
|
|
69
|
+
}
|
|
70
|
+
type CancelReason = 'signer_cancelled' | 'session_expired' | 'host_aborted';
|
|
71
|
+
/**
|
|
72
|
+
* Configuration the host hands the editor when opening a signing
|
|
73
|
+
* session. Maps 1:1 to the iframe `signature.request` envelope.
|
|
74
|
+
*/
|
|
75
|
+
interface SigningSessionConfig {
|
|
76
|
+
fields: SignatureField[];
|
|
77
|
+
mode: SignatureMode;
|
|
78
|
+
/** Optional banner the editor renders at the top of the pane. */
|
|
79
|
+
banner?: string;
|
|
80
|
+
/** Fires once per field as the signer completes it. */
|
|
81
|
+
onFieldSigned?: (payload: SignedFieldPayload) => void | Promise<void>;
|
|
82
|
+
/** Fires after every required field is signed. */
|
|
83
|
+
onComplete?: (payload: SignatureCompletePayload) => void | Promise<void>;
|
|
84
|
+
/** Fires when either side aborts the session. */
|
|
85
|
+
onCancel?: (payload: {
|
|
86
|
+
reason: CancelReason;
|
|
87
|
+
}) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type { CancelReason as C, DocAnchor as D, SheetAnchor as S, SignatureAnchor as a, SignatureCompletePayload as b, SignatureField as c, SignatureMethod as d, SignatureMode as e, SignedFieldPayload as f, SigningSessionConfig as g };
|
package/dist/univer.cjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/univer/index.ts
|
|
31
|
+
var univer_exports = {};
|
|
32
|
+
__export(univer_exports, {
|
|
33
|
+
eagerLoadForSnapshot: () => eagerLoadForSnapshot,
|
|
34
|
+
ensurePlugin: () => ensurePlugin,
|
|
35
|
+
ensurePluginByName: () => ensurePluginByName,
|
|
36
|
+
idleLoadAll: () => idleLoadAll,
|
|
37
|
+
setUniverForLazyLoad: () => setUniverForLazyLoad
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(univer_exports);
|
|
40
|
+
|
|
41
|
+
// src/univer/lazy-plugins.ts
|
|
42
|
+
var LOADERS = {
|
|
43
|
+
cf: async () => {
|
|
44
|
+
const [base, ui] = await Promise.all([
|
|
45
|
+
import("@univerjs/sheets-conditional-formatting"),
|
|
46
|
+
import("@univerjs/sheets-conditional-formatting-ui")
|
|
47
|
+
]);
|
|
48
|
+
return [
|
|
49
|
+
[base.UniverSheetsConditionalFormattingPlugin],
|
|
50
|
+
[ui.UniverSheetsConditionalFormattingUIPlugin]
|
|
51
|
+
];
|
|
52
|
+
},
|
|
53
|
+
dv: async () => {
|
|
54
|
+
const [base, ui] = await Promise.all([
|
|
55
|
+
import("@univerjs/sheets-data-validation"),
|
|
56
|
+
import("@univerjs/sheets-data-validation-ui")
|
|
57
|
+
]);
|
|
58
|
+
return [
|
|
59
|
+
[base.UniverSheetsDataValidationPlugin],
|
|
60
|
+
[ui.UniverSheetsDataValidationUIPlugin]
|
|
61
|
+
];
|
|
62
|
+
},
|
|
63
|
+
hyperlink: async () => {
|
|
64
|
+
const [base, ui] = await Promise.all([
|
|
65
|
+
import("@univerjs/sheets-hyper-link"),
|
|
66
|
+
import("@univerjs/sheets-hyper-link-ui")
|
|
67
|
+
]);
|
|
68
|
+
return [
|
|
69
|
+
[base.UniverSheetsHyperLinkPlugin],
|
|
70
|
+
[ui.UniverSheetsHyperLinkUIPlugin]
|
|
71
|
+
];
|
|
72
|
+
},
|
|
73
|
+
note: async () => {
|
|
74
|
+
const [base, ui] = await Promise.all([
|
|
75
|
+
import("@univerjs/sheets-note"),
|
|
76
|
+
import("@univerjs/sheets-note-ui")
|
|
77
|
+
]);
|
|
78
|
+
return [[base.UniverSheetsNotePlugin], [ui.UniverSheetsNoteUIPlugin]];
|
|
79
|
+
},
|
|
80
|
+
table: async () => {
|
|
81
|
+
const [base, ui] = await Promise.all([
|
|
82
|
+
import("@univerjs/sheets-table"),
|
|
83
|
+
import("@univerjs/sheets-table-ui")
|
|
84
|
+
]);
|
|
85
|
+
return [[base.UniverSheetsTablePlugin], [ui.UniverSheetsTableUIPlugin]];
|
|
86
|
+
},
|
|
87
|
+
threadComment: async () => {
|
|
88
|
+
const [tc, tcUi, sheetsTc, sheetsTcUi] = await Promise.all([
|
|
89
|
+
import("@univerjs/thread-comment"),
|
|
90
|
+
import("@univerjs/thread-comment-ui"),
|
|
91
|
+
import("@univerjs/sheets-thread-comment"),
|
|
92
|
+
import("@univerjs/sheets-thread-comment-ui")
|
|
93
|
+
]);
|
|
94
|
+
return [
|
|
95
|
+
[tc.UniverThreadCommentPlugin],
|
|
96
|
+
[tcUi.UniverThreadCommentUIPlugin],
|
|
97
|
+
[sheetsTc.UniverSheetsThreadCommentPlugin],
|
|
98
|
+
[sheetsTcUi.UniverSheetsThreadCommentUIPlugin]
|
|
99
|
+
];
|
|
100
|
+
},
|
|
101
|
+
drawing: async () => {
|
|
102
|
+
const [d, dUi, sd, sdUi] = await Promise.all([
|
|
103
|
+
import("@univerjs/drawing"),
|
|
104
|
+
import("@univerjs/drawing-ui"),
|
|
105
|
+
import("@univerjs/sheets-drawing"),
|
|
106
|
+
import("@univerjs/sheets-drawing-ui"),
|
|
107
|
+
// Side-effect imports: install FWorksheet.insertImage / getImages /
|
|
108
|
+
// updateImages on the facade prototype. Without these, code that
|
|
109
|
+
// reaches in via the FUniver facade (e2e specs, future shell glue)
|
|
110
|
+
// sees an undefined method even though the plugin is registered.
|
|
111
|
+
import("@univerjs/sheets-drawing/facade"),
|
|
112
|
+
import("@univerjs/sheets-drawing-ui/facade")
|
|
113
|
+
]);
|
|
114
|
+
return [
|
|
115
|
+
[d.UniverDrawingPlugin],
|
|
116
|
+
[dUi.UniverDrawingUIPlugin],
|
|
117
|
+
[sd.UniverSheetsDrawingPlugin],
|
|
118
|
+
[sdUi.UniverSheetsDrawingUIPlugin]
|
|
119
|
+
];
|
|
120
|
+
},
|
|
121
|
+
sort: async () => {
|
|
122
|
+
const [base, ui] = await Promise.all([
|
|
123
|
+
import("@univerjs/sheets-sort"),
|
|
124
|
+
import("@univerjs/sheets-sort-ui")
|
|
125
|
+
]);
|
|
126
|
+
return [[base.UniverSheetsSortPlugin], [ui.UniverSheetsSortUIPlugin]];
|
|
127
|
+
},
|
|
128
|
+
filter: async () => {
|
|
129
|
+
const [base, ui] = await Promise.all([
|
|
130
|
+
import("@univerjs/sheets-filter"),
|
|
131
|
+
import("@univerjs/sheets-filter-ui")
|
|
132
|
+
]);
|
|
133
|
+
return [[base.UniverSheetsFilterPlugin], [ui.UniverSheetsFilterUIPlugin]];
|
|
134
|
+
},
|
|
135
|
+
findReplace: async () => {
|
|
136
|
+
const [base, sheets] = await Promise.all([
|
|
137
|
+
import("@univerjs/find-replace"),
|
|
138
|
+
import("@univerjs/sheets-find-replace")
|
|
139
|
+
]);
|
|
140
|
+
return [[base.UniverFindReplacePlugin], [sheets.UniverSheetsFindReplacePlugin]];
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var RESOURCE_NAME_TO_GROUP = {
|
|
144
|
+
SHEET_CONDITIONAL_FORMATTING_PLUGIN: "cf",
|
|
145
|
+
SHEET_DATA_VALIDATION_PLUGIN: "dv",
|
|
146
|
+
SHEET_HYPER_LINK_PLUGIN: "hyperlink",
|
|
147
|
+
SHEET_NOTE_PLUGIN: "note",
|
|
148
|
+
SHEET_TABLE_PLUGIN: "table",
|
|
149
|
+
SHEET_THREAD_COMMENT_BASE_PLUGIN: "threadComment",
|
|
150
|
+
SHEET_DRAWING_PLUGIN: "drawing",
|
|
151
|
+
SHEET_SORT_PLUGIN: "sort",
|
|
152
|
+
SHEET_FILTER_PLUGIN: "filter"
|
|
153
|
+
};
|
|
154
|
+
var loaded = /* @__PURE__ */ new Set();
|
|
155
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
156
|
+
var currentUniver = null;
|
|
157
|
+
function setUniverForLazyLoad(univer) {
|
|
158
|
+
currentUniver = univer;
|
|
159
|
+
}
|
|
160
|
+
function ensurePlugin(univer, group) {
|
|
161
|
+
if (loaded.has(group)) return Promise.resolve();
|
|
162
|
+
const existing = inflight.get(group);
|
|
163
|
+
if (existing) return existing;
|
|
164
|
+
const loader = LOADERS[group];
|
|
165
|
+
if (!loader) return Promise.resolve();
|
|
166
|
+
const p = loader().then((plugins) => {
|
|
167
|
+
for (const [PluginCtor, config] of plugins) {
|
|
168
|
+
univer.registerPlugin(PluginCtor, config);
|
|
169
|
+
}
|
|
170
|
+
loaded.add(group);
|
|
171
|
+
inflight.delete(group);
|
|
172
|
+
});
|
|
173
|
+
inflight.set(group, p);
|
|
174
|
+
return p;
|
|
175
|
+
}
|
|
176
|
+
function ensurePluginByName(group) {
|
|
177
|
+
if (loaded.has(group)) return Promise.resolve();
|
|
178
|
+
if (!currentUniver) return Promise.resolve();
|
|
179
|
+
return ensurePlugin(currentUniver, group);
|
|
180
|
+
}
|
|
181
|
+
async function eagerLoadForSnapshot(univer, snapshot) {
|
|
182
|
+
const groups = /* @__PURE__ */ new Set();
|
|
183
|
+
const resources = snapshot.resources ?? [];
|
|
184
|
+
for (const r of resources) {
|
|
185
|
+
const g = RESOURCE_NAME_TO_GROUP[r.name];
|
|
186
|
+
if (g) groups.add(g);
|
|
187
|
+
}
|
|
188
|
+
if (snapshotHasHyperlinks(snapshot)) groups.add("hyperlink");
|
|
189
|
+
await Promise.all(Array.from(groups).map((g) => ensurePlugin(univer, g)));
|
|
190
|
+
}
|
|
191
|
+
function idleLoadAll(univer) {
|
|
192
|
+
const groups = Object.keys(LOADERS);
|
|
193
|
+
schedule(() => {
|
|
194
|
+
for (const g of groups) {
|
|
195
|
+
void ensurePlugin(univer, g);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function schedule(fn) {
|
|
200
|
+
const ric = globalThis.requestIdleCallback;
|
|
201
|
+
if (ric) ric(fn, { timeout: 500 });
|
|
202
|
+
else setTimeout(fn, 0);
|
|
203
|
+
}
|
|
204
|
+
function snapshotHasHyperlinks(snapshot) {
|
|
205
|
+
const sheetOrder = snapshot.sheetOrder ?? [];
|
|
206
|
+
for (const sid of sheetOrder) {
|
|
207
|
+
const sheet = snapshot.sheets?.[sid];
|
|
208
|
+
if (!sheet?.cellData) continue;
|
|
209
|
+
const cellData = sheet.cellData;
|
|
210
|
+
for (const r of Object.keys(cellData)) {
|
|
211
|
+
const row = cellData[r];
|
|
212
|
+
for (const c of Object.keys(row)) {
|
|
213
|
+
const ranges = row[c]?.p?.body?.customRanges ?? [];
|
|
214
|
+
if (ranges.some((cr) => cr.rangeType === 0)) return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=univer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/univer/index.ts","../src/univer/lazy-plugins.ts"],"sourcesContent":["/**\n * Internal Univer wiring shared by the SDK editor and the host app.\n *\n * Phase 1 (Batch 1) of the SDK migration lifts the lazy plugin loader out of\n * `apps/web` so the editor can eventually run entirely from `@casualoffice/sheets`.\n * It's pure Univer/DI code (no React context, FileSource, collab, or routing) with\n * module-level singleton state, so the host and the SDK must resolve this one\n * module instance. Later batches add the facade-coupled helpers (dev-helpers,\n * paste-merge-hook, zoom override) + the editor core. See\n * `docs/SDK_MIGRATION_PIPELINE.md` Phase 1.\n *\n * @internal — not part of the SDK's semver surface; consumers use `<CasualSheets>`.\n */\n\n// Lazy plugin loader (CF, DV, hyperlink, note, table, thread-comment, drawing,\n// sort, filter, find-replace) + the module-level Univer holder.\nexport * from './lazy-plugins';\n","import type { Univer, IWorkbookData } from '@univerjs/core';\n\n/**\n * Lazy plugin loading — pipeline Stage 4. The cheap plugins (render,\n * formula, sheets, sheets-ui, numfmt, docs) stay eager because every\n * workbook needs them. The heavy / feature-specific plugins (CF, DV,\n * hyperlink, table, note, thread-comment, drawing, sort, filter,\n * find-replace) load on demand:\n *\n * 1. Eagerly when a snapshot is about to mount and we detect the\n * plugin's resource key on it (e.g. `SHEET_CONDITIONAL_FORMATTING_PLUGIN`\n * in `data.resources`). This is the safety net: missing this would\n * silently drop plugin data on file open.\n * 2. Lazily when the user reaches for the feature — opens the Data\n * tab (sort/filter), hits Ctrl+F (find-replace), uses the Insert\n * tab (drawing), etc. The shell hooks `ensurePlugin(...)` into\n * these triggers and awaits before the action runs.\n *\n * Each loader returns the plugins-to-register in the correct order\n * (base before UI, same as `plugins.ts`). Registration order matters\n * in Univer; the loaders bundle a small group together to keep that\n * locality explicit.\n */\n\nexport type LazyPluginGroup =\n | 'cf'\n | 'dv'\n | 'hyperlink'\n | 'note'\n | 'table'\n | 'threadComment'\n | 'drawing'\n | 'sort'\n | 'filter'\n | 'findReplace';\n\ntype Loader = () => Promise<Array<[unknown, unknown?]>>;\n\nconst LOADERS: Record<LazyPluginGroup, Loader> = {\n cf: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-conditional-formatting'),\n import('@univerjs/sheets-conditional-formatting-ui'),\n ]);\n return [\n [base.UniverSheetsConditionalFormattingPlugin],\n [ui.UniverSheetsConditionalFormattingUIPlugin],\n ];\n },\n dv: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-data-validation'),\n import('@univerjs/sheets-data-validation-ui'),\n ]);\n return [\n [base.UniverSheetsDataValidationPlugin],\n [ui.UniverSheetsDataValidationUIPlugin],\n ];\n },\n hyperlink: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-hyper-link'),\n import('@univerjs/sheets-hyper-link-ui'),\n ]);\n return [\n [base.UniverSheetsHyperLinkPlugin],\n [ui.UniverSheetsHyperLinkUIPlugin],\n ];\n },\n note: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-note'),\n import('@univerjs/sheets-note-ui'),\n ]);\n return [[base.UniverSheetsNotePlugin], [ui.UniverSheetsNoteUIPlugin]];\n },\n table: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-table'),\n import('@univerjs/sheets-table-ui'),\n ]);\n return [[base.UniverSheetsTablePlugin], [ui.UniverSheetsTableUIPlugin]];\n },\n threadComment: async () => {\n const [tc, tcUi, sheetsTc, sheetsTcUi] = await Promise.all([\n import('@univerjs/thread-comment'),\n import('@univerjs/thread-comment-ui'),\n import('@univerjs/sheets-thread-comment'),\n import('@univerjs/sheets-thread-comment-ui'),\n ]);\n return [\n [tc.UniverThreadCommentPlugin],\n [tcUi.UniverThreadCommentUIPlugin],\n [sheetsTc.UniverSheetsThreadCommentPlugin],\n [sheetsTcUi.UniverSheetsThreadCommentUIPlugin],\n ];\n },\n drawing: async () => {\n const [d, dUi, sd, sdUi] = await Promise.all([\n import('@univerjs/drawing'),\n import('@univerjs/drawing-ui'),\n import('@univerjs/sheets-drawing'),\n import('@univerjs/sheets-drawing-ui'),\n // Side-effect imports: install FWorksheet.insertImage / getImages /\n // updateImages on the facade prototype. Without these, code that\n // reaches in via the FUniver facade (e2e specs, future shell glue)\n // sees an undefined method even though the plugin is registered.\n import('@univerjs/sheets-drawing/facade'),\n import('@univerjs/sheets-drawing-ui/facade'),\n ]);\n return [\n [d.UniverDrawingPlugin],\n [dUi.UniverDrawingUIPlugin],\n [sd.UniverSheetsDrawingPlugin],\n [sdUi.UniverSheetsDrawingUIPlugin],\n ];\n },\n sort: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-sort'),\n import('@univerjs/sheets-sort-ui'),\n ]);\n return [[base.UniverSheetsSortPlugin], [ui.UniverSheetsSortUIPlugin]];\n },\n filter: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-filter'),\n import('@univerjs/sheets-filter-ui'),\n ]);\n return [[base.UniverSheetsFilterPlugin], [ui.UniverSheetsFilterUIPlugin]];\n },\n findReplace: async () => {\n const [base, sheets] = await Promise.all([\n import('@univerjs/find-replace'),\n import('@univerjs/sheets-find-replace'),\n ]);\n return [[base.UniverFindReplacePlugin], [sheets.UniverSheetsFindReplacePlugin]];\n },\n};\n\n/**\n * Map from a Univer `data.resources[].name` to the lazy group that owns\n * it. Used by `eagerLoadForSnapshot` to pre-register plugins whose\n * state already lives on the workbook so file-open never silently\n * drops a CF rule / table / drawing / etc.\n */\nconst RESOURCE_NAME_TO_GROUP: Record<string, LazyPluginGroup> = {\n SHEET_CONDITIONAL_FORMATTING_PLUGIN: 'cf',\n SHEET_DATA_VALIDATION_PLUGIN: 'dv',\n SHEET_HYPER_LINK_PLUGIN: 'hyperlink',\n SHEET_NOTE_PLUGIN: 'note',\n SHEET_TABLE_PLUGIN: 'table',\n SHEET_THREAD_COMMENT_BASE_PLUGIN: 'threadComment',\n SHEET_DRAWING_PLUGIN: 'drawing',\n SHEET_SORT_PLUGIN: 'sort',\n SHEET_FILTER_PLUGIN: 'filter',\n};\n\nconst loaded = new Set<LazyPluginGroup>();\nconst inflight = new Map<LazyPluginGroup, Promise<void>>();\n\n/**\n * Module-level reference to the live Univer instance, set by\n * `UniverSheet.tsx` immediately after `new Univer()`. Lets shell code\n * call `ensurePluginByName(group)` without plumbing the Univer\n * instance through React context (the FUniver facade doesn't expose\n * its host). Cleared on dispose so callers fail loudly if they hit\n * the lazy path after teardown.\n */\nlet currentUniver: Univer | null = null;\n\nexport function setUniverForLazyLoad(univer: Univer | null): void {\n currentUniver = univer;\n}\n\n/**\n * Idempotent: subsequent calls for the same group resolve immediately\n * if the plugin is already loaded; concurrent calls share the same\n * in-flight promise so we never double-register.\n */\nexport function ensurePlugin(univer: Univer, group: LazyPluginGroup): Promise<void> {\n if (loaded.has(group)) return Promise.resolve();\n const existing = inflight.get(group);\n if (existing) return existing;\n const loader = LOADERS[group];\n if (!loader) return Promise.resolve();\n const p = loader().then((plugins) => {\n for (const [PluginCtor, config] of plugins) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n univer.registerPlugin(PluginCtor as any, config);\n }\n loaded.add(group);\n inflight.delete(group);\n });\n inflight.set(group, p);\n return p;\n}\n\n/**\n * Shell-friendly variant of `ensurePlugin` that pulls the Univer\n * instance from the module-level holder set by `UniverSheet.tsx`. Use\n * this anywhere we only have the FUniver facade (toolbar callbacks,\n * panel handlers). Returns a resolved promise if the holder is empty —\n * the worst case is a no-op, never a throw.\n */\nexport function ensurePluginByName(group: LazyPluginGroup): Promise<void> {\n if (loaded.has(group)) return Promise.resolve();\n if (!currentUniver) return Promise.resolve();\n return ensurePlugin(currentUniver, group);\n}\n\n/**\n * Walk a snapshot for plugin-owned resources + side-channel hyperlinks\n * and eagerly load every group that's referenced. Returns a promise\n * the caller MUST await before `createUnit` — Univer's resource manager\n * silently discards keys for plugins that aren't yet registered.\n */\nexport async function eagerLoadForSnapshot(univer: Univer, snapshot: IWorkbookData): Promise<void> {\n const groups = new Set<LazyPluginGroup>();\n const resources = snapshot.resources ?? [];\n for (const r of resources) {\n const g = RESOURCE_NAME_TO_GROUP[r.name];\n if (g) groups.add(g);\n }\n // Hyperlinks live inline in cell.p (Stage 5) — the hyperlink plugin\n // still owns click handling / context-menu actions, so eager-load\n // when any cell has a HYPERLINK customRange.\n if (snapshotHasHyperlinks(snapshot)) groups.add('hyperlink');\n await Promise.all(Array.from(groups).map((g) => ensurePlugin(univer, g)));\n}\n\n/**\n * Idle-load every remaining lazy group AFTER Univer is mounted. The\n * bundle split (each group ships as its own chunk) is the persistent\n * boot-time win — the initial paint doesn't pay for them. This\n * idle-load just ensures they're all eventually registered so a user\n * clicking \"Insert > Table\" doesn't hit a no-op.\n *\n * Loads in parallel via `requestIdleCallback` so they don't compete\n * with the first paint, falling back to `setTimeout(0)` in browsers\n * without rIC (Safari).\n */\nexport function idleLoadAll(univer: Univer): void {\n const groups = Object.keys(LOADERS) as LazyPluginGroup[];\n schedule(() => {\n for (const g of groups) {\n void ensurePlugin(univer, g);\n }\n });\n}\n\nfunction schedule(fn: () => void): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ric = (globalThis as any).requestIdleCallback as\n | ((cb: () => void, opts?: { timeout: number }) => number)\n | undefined;\n if (ric) ric(fn, { timeout: 500 });\n else setTimeout(fn, 0);\n}\n\nfunction snapshotHasHyperlinks(snapshot: IWorkbookData): boolean {\n const sheetOrder = snapshot.sheetOrder ?? [];\n for (const sid of sheetOrder) {\n const sheet = snapshot.sheets?.[sid];\n if (!sheet?.cellData) continue;\n const cellData = sheet.cellData as Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Record<string, { p?: any }>\n >;\n for (const r of Object.keys(cellData)) {\n const row = cellData[r];\n for (const c of Object.keys(row)) {\n const ranges = row[c]?.p?.body?.customRanges ?? [];\n if (ranges.some((cr: { rangeType?: number }) => cr.rangeType === 0)) return true;\n }\n }\n }\n return false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsCA,IAAM,UAA2C;AAAA,EAC/C,IAAI,YAAY;AACd,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,yCAAyC;AAAA,MAChD,OAAO,4CAA4C;AAAA,IACrD,CAAC;AACD,WAAO;AAAA,MACL,CAAC,KAAK,uCAAuC;AAAA,MAC7C,CAAC,GAAG,yCAAyC;AAAA,IAC/C;AAAA,EACF;AAAA,EACA,IAAI,YAAY;AACd,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,kCAAkC;AAAA,MACzC,OAAO,qCAAqC;AAAA,IAC9C,CAAC;AACD,WAAO;AAAA,MACL,CAAC,KAAK,gCAAgC;AAAA,MACtC,CAAC,GAAG,kCAAkC;AAAA,IACxC;AAAA,EACF;AAAA,EACA,WAAW,YAAY;AACrB,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,6BAA6B;AAAA,MACpC,OAAO,gCAAgC;AAAA,IACzC,CAAC;AACD,WAAO;AAAA,MACL,CAAC,KAAK,2BAA2B;AAAA,MACjC,CAAC,GAAG,6BAA6B;AAAA,IACnC;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAChB,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,uBAAuB;AAAA,MAC9B,OAAO,0BAA0B;AAAA,IACnC,CAAC;AACD,WAAO,CAAC,CAAC,KAAK,sBAAsB,GAAG,CAAC,GAAG,wBAAwB,CAAC;AAAA,EACtE;AAAA,EACA,OAAO,YAAY;AACjB,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,wBAAwB;AAAA,MAC/B,OAAO,2BAA2B;AAAA,IACpC,CAAC;AACD,WAAO,CAAC,CAAC,KAAK,uBAAuB,GAAG,CAAC,GAAG,yBAAyB,CAAC;AAAA,EACxE;AAAA,EACA,eAAe,YAAY;AACzB,UAAM,CAAC,IAAI,MAAM,UAAU,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzD,OAAO,0BAA0B;AAAA,MACjC,OAAO,6BAA6B;AAAA,MACpC,OAAO,iCAAiC;AAAA,MACxC,OAAO,oCAAoC;AAAA,IAC7C,CAAC;AACD,WAAO;AAAA,MACL,CAAC,GAAG,yBAAyB;AAAA,MAC7B,CAAC,KAAK,2BAA2B;AAAA,MACjC,CAAC,SAAS,+BAA+B;AAAA,MACzC,CAAC,WAAW,iCAAiC;AAAA,IAC/C;AAAA,EACF;AAAA,EACA,SAAS,YAAY;AACnB,UAAM,CAAC,GAAG,KAAK,IAAI,IAAI,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3C,OAAO,mBAAmB;AAAA,MAC1B,OAAO,sBAAsB;AAAA,MAC7B,OAAO,0BAA0B;AAAA,MACjC,OAAO,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA,MAKpC,OAAO,iCAAiC;AAAA,MACxC,OAAO,oCAAoC;AAAA,IAC7C,CAAC;AACD,WAAO;AAAA,MACL,CAAC,EAAE,mBAAmB;AAAA,MACtB,CAAC,IAAI,qBAAqB;AAAA,MAC1B,CAAC,GAAG,yBAAyB;AAAA,MAC7B,CAAC,KAAK,2BAA2B;AAAA,IACnC;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAChB,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,uBAAuB;AAAA,MAC9B,OAAO,0BAA0B;AAAA,IACnC,CAAC;AACD,WAAO,CAAC,CAAC,KAAK,sBAAsB,GAAG,CAAC,GAAG,wBAAwB,CAAC;AAAA,EACtE;AAAA,EACA,QAAQ,YAAY;AAClB,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnC,OAAO,yBAAyB;AAAA,MAChC,OAAO,4BAA4B;AAAA,IACrC,CAAC;AACD,WAAO,CAAC,CAAC,KAAK,wBAAwB,GAAG,CAAC,GAAG,0BAA0B,CAAC;AAAA,EAC1E;AAAA,EACA,aAAa,YAAY;AACvB,UAAM,CAAC,MAAM,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MACvC,OAAO,wBAAwB;AAAA,MAC/B,OAAO,+BAA+B;AAAA,IACxC,CAAC;AACD,WAAO,CAAC,CAAC,KAAK,uBAAuB,GAAG,CAAC,OAAO,6BAA6B,CAAC;AAAA,EAChF;AACF;AAQA,IAAM,yBAA0D;AAAA,EAC9D,qCAAqC;AAAA,EACrC,8BAA8B;AAAA,EAC9B,yBAAyB;AAAA,EACzB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kCAAkC;AAAA,EAClC,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,qBAAqB;AACvB;AAEA,IAAM,SAAS,oBAAI,IAAqB;AACxC,IAAM,WAAW,oBAAI,IAAoC;AAUzD,IAAI,gBAA+B;AAE5B,SAAS,qBAAqB,QAA6B;AAChE,kBAAgB;AAClB;AAOO,SAAS,aAAa,QAAgB,OAAuC;AAClF,MAAI,OAAO,IAAI,KAAK,EAAG,QAAO,QAAQ,QAAQ;AAC9C,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,QAAQ,KAAK;AAC5B,MAAI,CAAC,OAAQ,QAAO,QAAQ,QAAQ;AACpC,QAAM,IAAI,OAAO,EAAE,KAAK,CAAC,YAAY;AACnC,eAAW,CAAC,YAAY,MAAM,KAAK,SAAS;AAE1C,aAAO,eAAe,YAAmB,MAAM;AAAA,IACjD;AACA,WAAO,IAAI,KAAK;AAChB,aAAS,OAAO,KAAK;AAAA,EACvB,CAAC;AACD,WAAS,IAAI,OAAO,CAAC;AACrB,SAAO;AACT;AASO,SAAS,mBAAmB,OAAuC;AACxE,MAAI,OAAO,IAAI,KAAK,EAAG,QAAO,QAAQ,QAAQ;AAC9C,MAAI,CAAC,cAAe,QAAO,QAAQ,QAAQ;AAC3C,SAAO,aAAa,eAAe,KAAK;AAC1C;AAQA,eAAsB,qBAAqB,QAAgB,UAAwC;AACjG,QAAM,SAAS,oBAAI,IAAqB;AACxC,QAAM,YAAY,SAAS,aAAa,CAAC;AACzC,aAAW,KAAK,WAAW;AACzB,UAAM,IAAI,uBAAuB,EAAE,IAAI;AACvC,QAAI,EAAG,QAAO,IAAI,CAAC;AAAA,EACrB;AAIA,MAAI,sBAAsB,QAAQ,EAAG,QAAO,IAAI,WAAW;AAC3D,QAAM,QAAQ,IAAI,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,MAAM,aAAa,QAAQ,CAAC,CAAC,CAAC;AAC1E;AAaO,SAAS,YAAY,QAAsB;AAChD,QAAM,SAAS,OAAO,KAAK,OAAO;AAClC,WAAS,MAAM;AACb,eAAW,KAAK,QAAQ;AACtB,WAAK,aAAa,QAAQ,CAAC;AAAA,IAC7B;AAAA,EACF,CAAC;AACH;AAEA,SAAS,SAAS,IAAsB;AAEtC,QAAM,MAAO,WAAmB;AAGhC,MAAI,IAAK,KAAI,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,MAC5B,YAAW,IAAI,CAAC;AACvB;AAEA,SAAS,sBAAsB,UAAkC;AAC/D,QAAM,aAAa,SAAS,cAAc,CAAC;AAC3C,aAAW,OAAO,YAAY;AAC5B,UAAM,QAAQ,SAAS,SAAS,GAAG;AACnC,QAAI,CAAC,OAAO,SAAU;AACtB,UAAM,WAAW,MAAM;AAKvB,eAAW,KAAK,OAAO,KAAK,QAAQ,GAAG;AACrC,YAAM,MAAM,SAAS,CAAC;AACtB,iBAAW,KAAK,OAAO,KAAK,GAAG,GAAG;AAChC,cAAM,SAAS,IAAI,CAAC,GAAG,GAAG,MAAM,gBAAgB,CAAC;AACjD,YAAI,OAAO,KAAK,CAAC,OAA+B,GAAG,cAAc,CAAC,EAAG,QAAO;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Univer, IWorkbookData } from '@univerjs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy plugin loading — pipeline Stage 4. The cheap plugins (render,
|
|
5
|
+
* formula, sheets, sheets-ui, numfmt, docs) stay eager because every
|
|
6
|
+
* workbook needs them. The heavy / feature-specific plugins (CF, DV,
|
|
7
|
+
* hyperlink, table, note, thread-comment, drawing, sort, filter,
|
|
8
|
+
* find-replace) load on demand:
|
|
9
|
+
*
|
|
10
|
+
* 1. Eagerly when a snapshot is about to mount and we detect the
|
|
11
|
+
* plugin's resource key on it (e.g. `SHEET_CONDITIONAL_FORMATTING_PLUGIN`
|
|
12
|
+
* in `data.resources`). This is the safety net: missing this would
|
|
13
|
+
* silently drop plugin data on file open.
|
|
14
|
+
* 2. Lazily when the user reaches for the feature — opens the Data
|
|
15
|
+
* tab (sort/filter), hits Ctrl+F (find-replace), uses the Insert
|
|
16
|
+
* tab (drawing), etc. The shell hooks `ensurePlugin(...)` into
|
|
17
|
+
* these triggers and awaits before the action runs.
|
|
18
|
+
*
|
|
19
|
+
* Each loader returns the plugins-to-register in the correct order
|
|
20
|
+
* (base before UI, same as `plugins.ts`). Registration order matters
|
|
21
|
+
* in Univer; the loaders bundle a small group together to keep that
|
|
22
|
+
* locality explicit.
|
|
23
|
+
*/
|
|
24
|
+
type LazyPluginGroup = 'cf' | 'dv' | 'hyperlink' | 'note' | 'table' | 'threadComment' | 'drawing' | 'sort' | 'filter' | 'findReplace';
|
|
25
|
+
declare function setUniverForLazyLoad(univer: Univer | null): void;
|
|
26
|
+
/**
|
|
27
|
+
* Idempotent: subsequent calls for the same group resolve immediately
|
|
28
|
+
* if the plugin is already loaded; concurrent calls share the same
|
|
29
|
+
* in-flight promise so we never double-register.
|
|
30
|
+
*/
|
|
31
|
+
declare function ensurePlugin(univer: Univer, group: LazyPluginGroup): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Shell-friendly variant of `ensurePlugin` that pulls the Univer
|
|
34
|
+
* instance from the module-level holder set by `UniverSheet.tsx`. Use
|
|
35
|
+
* this anywhere we only have the FUniver facade (toolbar callbacks,
|
|
36
|
+
* panel handlers). Returns a resolved promise if the holder is empty —
|
|
37
|
+
* the worst case is a no-op, never a throw.
|
|
38
|
+
*/
|
|
39
|
+
declare function ensurePluginByName(group: LazyPluginGroup): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Walk a snapshot for plugin-owned resources + side-channel hyperlinks
|
|
42
|
+
* and eagerly load every group that's referenced. Returns a promise
|
|
43
|
+
* the caller MUST await before `createUnit` — Univer's resource manager
|
|
44
|
+
* silently discards keys for plugins that aren't yet registered.
|
|
45
|
+
*/
|
|
46
|
+
declare function eagerLoadForSnapshot(univer: Univer, snapshot: IWorkbookData): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Idle-load every remaining lazy group AFTER Univer is mounted. The
|
|
49
|
+
* bundle split (each group ships as its own chunk) is the persistent
|
|
50
|
+
* boot-time win — the initial paint doesn't pay for them. This
|
|
51
|
+
* idle-load just ensures they're all eventually registered so a user
|
|
52
|
+
* clicking "Insert > Table" doesn't hit a no-op.
|
|
53
|
+
*
|
|
54
|
+
* Loads in parallel via `requestIdleCallback` so they don't compete
|
|
55
|
+
* with the first paint, falling back to `setTimeout(0)` in browsers
|
|
56
|
+
* without rIC (Safari).
|
|
57
|
+
*/
|
|
58
|
+
declare function idleLoadAll(univer: Univer): void;
|
|
59
|
+
|
|
60
|
+
export { type LazyPluginGroup, eagerLoadForSnapshot, ensurePlugin, ensurePluginByName, idleLoadAll, setUniverForLazyLoad };
|