@hanzo/ui 8.0.23 → 8.0.25
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/README.md +59 -0
- package/dist/chunk-3NG33DV2.cjs +607 -0
- package/dist/chunk-3NG33DV2.cjs.map +1 -0
- package/dist/chunk-5CZOYONP.js +343 -0
- package/dist/chunk-5CZOYONP.js.map +1 -0
- package/dist/{chunk-P2P6ST6J.js → chunk-7ZNK6JSS.js} +9 -394
- package/dist/chunk-7ZNK6JSS.js.map +1 -0
- package/dist/chunk-GVCOAJLO.js +397 -0
- package/dist/chunk-GVCOAJLO.js.map +1 -0
- package/dist/chunk-GWNNLYX4.cjs +353 -0
- package/dist/chunk-GWNNLYX4.cjs.map +1 -0
- package/dist/{chunk-NBZDIMP3.cjs → chunk-ONVERVFG.cjs} +33 -438
- package/dist/chunk-ONVERVFG.cjs.map +1 -0
- package/dist/chunk-P2QU5MN5.js +555 -0
- package/dist/chunk-P2QU5MN5.js.map +1 -0
- package/dist/chunk-WHJR5S4L.cjs +418 -0
- package/dist/chunk-WHJR5S4L.cjs.map +1 -0
- package/dist/framework/CollectionBuilder.d.ts +12 -0
- package/dist/framework/CollectionsBrowser.d.ts +26 -19
- package/dist/framework/DocTypeRecords.d.ts +31 -11
- package/dist/framework/MediaGrid.d.ts +42 -0
- package/dist/framework/RecordCards.d.ts +35 -0
- package/dist/framework/builder-logic.d.ts +76 -0
- package/dist/framework/client.d.ts +31 -44
- package/dist/framework/core.cjs +213 -0
- package/dist/framework/core.cjs.map +1 -0
- package/dist/framework/core.d.ts +6 -0
- package/dist/framework/core.js +4 -0
- package/dist/framework/core.js.map +1 -0
- package/dist/framework/data.d.ts +2 -2
- package/dist/framework/fields.d.ts +47 -4
- package/dist/framework/index.cjs +1340 -0
- package/dist/framework/index.cjs.map +1 -0
- package/dist/framework/index.d.ts +9 -23
- package/dist/framework/index.js +1119 -0
- package/dist/framework/index.js.map +1 -0
- package/dist/framework/media.d.ts +53 -0
- package/dist/framework/parts.d.ts +74 -0
- package/dist/framework/responsive.d.ts +41 -0
- package/dist/framework/types.d.ts +13 -7
- package/dist/product/SelectMenu.d.ts +13 -1
- package/dist/product/index.cjs +2061 -336
- package/dist/product/index.cjs.map +1 -1
- package/dist/product/index.js +1909 -3
- package/dist/product/index.js.map +1 -1
- package/dist/product/social/index.cjs +17 -16
- package/dist/product/social/index.js +3 -2
- package/package.json +11 -5
- package/dist/chunk-2BM42AZR.cjs +0 -2301
- package/dist/chunk-2BM42AZR.cjs.map +0 -1
- package/dist/chunk-7P7R7FC5.js +0 -2231
- package/dist/chunk-7P7R7FC5.js.map +0 -1
- package/dist/chunk-NBZDIMP3.cjs.map +0 -1
- package/dist/chunk-P2P6ST6J.js.map +0 -1
- package/dist/framework/Loader.d.ts +0 -4
- package/dist/framework.cjs +0 -757
- package/dist/framework.cjs.map +0 -1
- package/dist/framework.d.ts +0 -1
- package/dist/framework.js +0 -727
- package/dist/framework.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/product/host.tsx","../src/product/BackendState.tsx","../src/product/PageHeader.tsx","../src/product/accent.ts","../src/product/PrimaryButton.tsx","../src/product/EmptyState.tsx","../src/product/Field.tsx"],"names":["createContext","jsx","useContext","jsxs","Card","XStack","TriangleAlert","Text","Button","CreditCard","YStack","useSyncExternalStore","useEmit","labelOf","Check","ExternalLink","ArrowRight","Label","Input","textSize","TextArea","Switch","Slider"],"mappings":"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","file":"chunk-WHJR5S4L.cjs","sourcesContent":["'use client'\n\n/**\n * The host seam — the ONE place an app injects its effects into this layer.\n *\n * The product layer is presentational and host-agnostic: it never imports a\n * router or an auth module. An honest state card still has to OFFER \"Sign in\n * again\" (401) or \"Add credits\" (402), so the app provides those two effects\n * once at its root and every card below renders the right affordance. Without a\n * provider the affordance is simply not offered — nothing is faked.\n */\nimport { createContext, useContext, type ReactNode } from 'react'\n\nexport type HostActions = {\n /** Re-authenticate and return to this exact page (401). */\n signIn?: () => void\n /** Navigate to the org's top-up surface (402). */\n addCredits?: () => void\n}\n\nconst HostCtx = createContext<HostActions>({})\n\nexport const HostProvider = ({ actions, children }: { actions: HostActions; children: ReactNode }) => (\n <HostCtx.Provider value={actions}>{children}</HostCtx.Provider>\n)\n\nexport const useHost = (): HostActions => useContext(HostCtx)\n","'use client'\n\n/**\n * Honest backend states for `/v1`-backed modules — ONE place that maps a thrown\n * HTTP error to a truthful explanation.\n *\n * A 503 means the runtime/keys aren't wired on this deployment yet, a 404/405\n * means the route isn't mounted here, 401/403 means the session can't read it,\n * 402 means the org has no funded balance. Nothing is fabricated — on failure the\n * caller shows this card instead of placeholder rows/charts.\n *\n * Classification is duck-typed on `status`, so any client's error class works\n * (console's `ApiError`, commerce's `CommerceError`, …) without this layer\n * depending on one. Effects come from `useHost()`.\n */\nimport type { ReactNode } from 'react'\nimport { Button, Card, Text, XStack } from '@hanzo/gui'\nimport { TriangleAlert, CreditCard } from '@hanzogui/lucide-icons-2'\n\nimport { useHost } from './host'\n\nexport type BackendStateKind =\n | 'not-initialized'\n | 'unavailable'\n | 'not-implemented'\n | 'access'\n | 'signin'\n | 'billing'\n | 'error'\n\nexport type BackendState = { kind: BackendStateKind; message: string }\n\n/**\n * Classify a thrown `/v1` error into an honest kind + raw message.\n *\n * 401 and 403 are DISTINCT for a signed-in user: 401 = the session is expired /\n * not recognized (re-auth fixes it → `signin`), 403 = the account IS signed in\n * but this surface isn't authorized/enabled for its org (`access`). A signed-in\n * user is NEVER told to \"sign in\" for a 403 — that would be a false instruction.\n */\nexport function classifyBackend(e: unknown): BackendState {\n const status = typeof (e as { status?: unknown })?.status === 'number' ? (e as { status: number }).status : 0\n const message = e instanceof Error ? e.message : String(e)\n if (status === 503) return { kind: 'not-initialized', message }\n if (status === 404 || status === 405) return { kind: 'unavailable', message }\n // 501 — the route EXISTS and answers honestly that its body ships later (the\n // Cloudflare R2/KV/D1 Phase-2 seams). Distinct from 404 (not mounted here): the\n // capability is real and planned, so say that rather than \"not available yet\".\n if (status === 501) return { kind: 'not-implemented', message }\n if (status === 402) return { kind: 'billing', message }\n if (status === 401) return { kind: 'signin', message }\n if (status === 403) return { kind: 'access', message }\n return { kind: 'error', message }\n}\n\n/**\n * Classify a READ (GET/list) failure. Identical to `classifyBackend` EXCEPT a 402\n * is NOT a wall: listing/reading a resource is never credit-gated, so a 402 on a\n * read means \"nothing is provisioned yet\" — the caller shows its honest EMPTY\n * state (e.g. \"No buckets yet · Create one\"), never an \"add credits\" paywall\n * (that belongs only on a paid WRITE). Returns `null` for a 402 → treat as empty.\n */\nexport function classifyRead(e: unknown): BackendState | null {\n const s = classifyBackend(e)\n return s.kind === 'billing' ? null : s\n}\n\nconst TITLES: Record<BackendStateKind, string> = {\n 'not-initialized': 'Backend not initialized',\n unavailable: 'Not available on this deployment yet',\n 'not-implemented': 'Not yet available',\n access: 'Not enabled for your account',\n signin: 'Your session expired',\n billing: 'Add credits to continue',\n error: 'Could not reach the backend',\n}\n\nconst BODIES: Record<BackendStateKind, string> = {\n 'not-initialized':\n 'The /v1 route is mounted but its runtime (or the console API key it proxies to) is not configured on this deployment yet. Real data appears here once it is — no placeholder data is shown.',\n unavailable:\n 'This endpoint is not mounted at the gateway on this host yet. The view lights up automatically once the route is live.',\n // 501 — a real, planned capability whose body ships in a later phase.\n 'not-implemented':\n 'This capability is planned but not wired yet. The backend answers honestly rather than pretending it worked — nothing here is fabricated, and the view lights up automatically once it ships.',\n // 403 for a SIGNED-IN user — never \"sign in\".\n access:\n \"You're signed in, but this isn't enabled for your organization on this deployment, or it's an admin-only surface. It appears here automatically once your account has access — nothing is fabricated.\",\n // 401 — the session itself lapsed; re-auth returns to this exact page.\n signin: 'Your session has expired or isn’t recognized here. Sign in again to continue where you left off.',\n // Empty → the card shows the backend's own message (the honest \"Insufficient\n // balance. Please add credits…\" from the gateway billing gate).\n billing: '',\n error: '',\n}\n\n/** A truthful state card for a `/v1` load failure, with an optional retry + hint. */\nexport function BackendStateCard({\n state,\n onRetry,\n hint,\n}: {\n state: BackendState\n onRetry?: () => void\n hint?: ReactNode\n}) {\n const { signIn, addCredits } = useHost()\n return (\n <Card borderWidth={1} borderColor=\"$borderColor\" p=\"$4\" gap=\"$2\" maxWidth={640}>\n <XStack gap=\"$2\" items=\"center\">\n <TriangleAlert size={16} />\n <Text fontSize=\"$4\" fontWeight=\"700\">\n {TITLES[state.kind]}\n </Text>\n </XStack>\n <Text fontSize=\"$3\" color=\"$color11\">\n {BODIES[state.kind] || state.message}\n </Text>\n {hint ? (\n <Text fontSize=\"$2\" color=\"$color10\">\n {hint}\n </Text>\n ) : null}\n {state.kind === 'signin' && signIn ? (\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" onPress={signIn}>\n Sign in again\n </Button>\n ) : state.kind === 'billing' && addCredits ? (\n // 402 — the org has no funded balance. Offer a top-up CTA (and keep Retry so\n // a returning, funded caller can reload in place).\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\">\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" icon={<CreditCard size={14} />} onPress={addCredits}>\n Add credits\n </Button>\n {onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </XStack>\n ) : onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport { Text, XStack, YStack } from '@hanzo/gui'\n\n/** Section title + optional subtitle and right-aligned actions. */\nexport function PageHeader({\n title,\n subtitle,\n actions,\n}: {\n title: string\n subtitle?: string\n actions?: ReactNode\n}) {\n return (\n // flexWrap lets the actions drop below the title on narrow screens; flex={1}\n // minW={0} lets the title column shrink so a long subtitle WRAPS instead of\n // running off the viewport (a flex child defaults to min-width:auto = content\n // width, which would overflow on mobile).\n <XStack justify=\"space-between\" items=\"flex-start\" gap=\"$3\" flexWrap=\"wrap\">\n <YStack gap=\"$1\" flex={1} minW={200}>\n <Text fontSize=\"$7\" fontWeight=\"500\" letterSpacing={-0.4}>\n {title}\n </Text>\n {subtitle ? (\n <Text fontSize=\"$3\" color=\"$color11\">\n {subtitle}\n </Text>\n ) : null}\n </YStack>\n {/* Actions take a FULL line below the title on phones (width 100%) and WRAP —\n so a header with many buttons (e.g. Tracker's Projects/Refresh/List/Board/\n New issue/Delete) never runs off-screen and clips the last controls. At $md+\n they collapse back to a right-aligned inline row beside the title. */}\n {actions ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" width=\"100%\" $md={{ width: 'auto', justify: 'flex-end' }}>\n {actions}\n </XStack>\n ) : null}\n </XStack>\n )\n}\n","'use client'\n\n/**\n * Org accent theme — the ONE place that turns an organization's saved brand color\n * (`themeData.colorPrimary`, when `themeData.isEnabled`) into the LIVE console accent.\n *\n * The console is monochrome by default; an org opts into a custom accent in Settings →\n * Branding. Saving persists the color on the org record (that half already works). This\n * module is the missing APPLY half: a tiny external store holds the resolved accent\n * (hex + a readable contrast, or null); `setOrgAccent` updates it on load AND on save\n * (one mechanism, two callers), and `useAccent()` lets the genuine accent surfaces (the\n * primary button, the active tabs, the active nav item) read it and recolor with real\n * `@hanzo/gui` props — reverting cleanly to the default monochrome when the org disables\n * its theme (or the hex is invalid). React-prop driven (Tamagui's own styling path), NOT\n * a CSS class — Tamagui does not forward `className` to a Button's DOM node.\n *\n * Pure + SSR-safe: the resolve/contrast helpers are pure (unit-tested); the store starts\n * from a stable default snapshot so the server render and first client paint agree.\n */\nimport { useSyncExternalStore } from 'react'\n\n/** An org's persisted theme block (mirrors IAM `themeData`; only the fields we apply). */\nexport type OrgThemeData = {\n colorPrimary?: string\n isEnabled?: boolean\n}\n\n/** The live accent — a resolved hex + a readable text color for content on it, or null. */\nexport type Accent = { accent: string | null; contrast: string }\n\n/** True for a 3- or 6-digit CSS hex color (`#RGB` / `#RRGGBB`). */\nexport function isHexColor(v: string | undefined | null): boolean {\n return typeof v === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v.trim())\n}\n\n/**\n * The accent hex to apply for an org, or `null` when no custom accent should be used —\n * i.e. the theme is disabled, missing, or the color is not a valid hex. PURE.\n */\nexport function resolveAccent(theme: OrgThemeData | null | undefined): string | null {\n if (!theme || !theme.isEnabled) return null\n const hex = (theme.colorPrimary ?? '').trim()\n return isHexColor(hex) ? hex : null\n}\n\n/** Expand `#abc` → `#aabbcc`; pass a 6-digit hex through (lowercased). */\nfunction expandHex(hex: string): string {\n const h = hex.trim().toLowerCase()\n if (h.length === 4) return `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}`\n return h\n}\n\n/**\n * A readable text color (`#000000` / `#ffffff`) for content placed ON the accent,\n * chosen by the accent's relative luminance (WCAG-style). PURE. Falls back to white\n * for a non-hex input (never throws).\n */\nexport function contrastText(hex: string): '#000000' | '#ffffff' {\n if (!isHexColor(hex)) return '#ffffff'\n const h = expandHex(hex)\n const r = parseInt(h.slice(1, 3), 16) / 255\n const g = parseInt(h.slice(3, 5), 16) / 255\n const b = parseInt(h.slice(5, 7), 16) / 255\n const lin = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)\n const luminance = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)\n return luminance > 0.5 ? '#000000' : '#ffffff'\n}\n\n/** Resolve a theme block to the `Accent` value it should apply (accent+contrast or null). PURE. */\nexport function accentFor(theme: OrgThemeData | null | undefined): Accent {\n const hex = resolveAccent(theme)\n return { accent: hex, contrast: hex ? contrastText(hex) : '#ffffff' }\n}\n\n// ── The live accent store (an external store React subscribes to) ─────────────\n\n/** The default (no custom accent) — a STABLE reference so SSR + first paint agree. */\nconst DEFAULT: Accent = { accent: null, contrast: '#ffffff' }\n\nlet current: Accent = DEFAULT\nconst listeners = new Set<() => void>()\n\n/**\n * Set the live accent from an org theme block. Called on load (from the fetched org) AND\n * immediately on save — the SINGLE mechanism, so the accent survives reload and updates\n * without one. A no-op when the resolved accent is unchanged (keeps the snapshot stable).\n */\nexport function setOrgAccent(theme: OrgThemeData | null | undefined): void {\n const next = accentFor(theme)\n if (next.accent === current.accent) return\n current = next.accent ? next : DEFAULT\n for (const l of listeners) l()\n}\n\nfunction subscribe(cb: () => void): () => void {\n listeners.add(cb)\n return () => {\n listeners.delete(cb)\n }\n}\n\n/** The live accent (subscribes to the store). Returns the default until an org theme is applied. */\nexport function useAccent(): Accent {\n return useSyncExternalStore(\n subscribe,\n () => current,\n () => DEFAULT,\n )\n}\n","'use client'\n\n/**\n * Primary button — the one white, high-emphasis action for the console.\n *\n * Monochrome brand: `theme=\"light\"` flips the button to the light theme inside\n * the dark console, giving a white fill with a near-black label and icon — no\n * hue accent. Use it for the single primary action in a view (sign in, save,\n * get started). Secondary and destructive actions use the default neutral\n * `Button`.\n *\n * When an org enables a custom brand color, this — the console's one primary\n * action — recolors to the org accent (inline bg + the color prop the label/icon\n * inherit), reading the live accent from `useAccent()`; with no custom theme it\n * stays the default white. A caller's own `style`/`color` still win (spread last).\n */\nimport type { ComponentProps } from 'react'\nimport { Button } from '@hanzo/gui'\n\nimport { useAccent } from './accent'\nimport { labelOf, useEmit } from './instrument'\n\nexport function PrimaryButton({ onPress, ...rest }: ComponentProps<typeof Button>) {\n const { accent, contrast } = useAccent()\n const track = useEmit()\n // DESTRUCTURE the caller's handler out first. Wrapping it while still reading it\n // off a rebound `props` makes the wrapper call ITSELF — an unbounded recursion\n // that a real browser click turns into \"Maximum call stack size exceeded\".\n const press = (e: unknown) => {\n // The label IS the identity of a primary action — no app has to name it.\n track({\n component: 'PrimaryButton',\n action: 'click',\n id: labelOf(rest.children) ?? rest['aria-label'],\n })\n ;(onPress as ((e: unknown) => void) | undefined)?.(e)\n }\n const handler = press as ComponentProps<typeof Button>['onPress']\n // Accent set → a filled accent button (bg + readable text, via inline style Tamagui\n // forwards to the DOM). No accent → the default monochrome white (theme=\"light\").\n if (accent) {\n return <Button style={{ backgroundColor: accent, color: contrast, borderColor: accent }} onPress={handler} {...rest} />\n }\n return <Button theme=\"light\" onPress={handler} {...rest} />\n}\n","'use client'\n\n/**\n * Rich empty state — the ONE way the console turns a real, honest \"nothing here\n * yet\" into a helpful onboarding moment (DigitalOcean / Vercel class) instead of\n * a dead screen.\n *\n * It is NOT a fabricated-data fallback: callers render it only when a load\n * genuinely succeeded with zero rows (or a resource has no surface yet). It shows\n * the product icon, a one-line \"what this is\", a couple of guidance bullets, a\n * single primary call-to-action (Deploy / Create / Connect …) and an optional\n * secondary link (Docs / CLI). Orthogonal to `BackendStateCard` (failures) and\n * `DataTable`'s thin inline empty (in-table) — this is the full-bleed first-run\n * surface a module shows in place of its table.\n */\nimport type { ReactElement, ReactNode } from 'react'\nimport { Button, Card, Text, XStack, YStack } from '@hanzo/gui'\nimport { ArrowRight, Check, ExternalLink } from '@hanzogui/lucide-icons-2'\n\nimport type { IconLike } from './color'\nimport { PrimaryButton } from './PrimaryButton'\n\n/** A single call-to-action — primary (filled) or a secondary/external link. */\nexport type EmptyAction = {\n label: string\n /** In-app navigation / handler. */\n onPress?: () => void\n /** External URL — opens in a new tab with an external-link affordance. */\n href?: string\n icon?: ReactElement\n}\n\n/** Open an external URL in a new tab (no-opener), guarded for SSR. */\nfunction openHref(href: string): void {\n if (typeof window !== 'undefined') window.open(href, '_blank', 'noopener')\n}\n\nimport { useEmit } from './instrument'\n\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n bullets,\n primary,\n secondary,\n}: {\n /** Product/section icon — any lucide/Gui icon component (size + optional color). */\n icon: IconLike\n title: string\n description: string\n /** Optional \"here's how to get started\" guidance bullets. */\n bullets?: string[]\n /** The single high-emphasis action (Deploy / Create / Connect). */\n primary?: EmptyAction\n /** An optional lower-emphasis action (Docs / CLI / Learn more). */\n secondary?: EmptyAction\n}) {\n const track = useEmit()\n return (\n <Card\n borderWidth={1}\n borderColor=\"$borderColor\"\n borderStyle=\"dashed\"\n p=\"$6\"\n gap=\"$4\"\n items=\"center\"\n maxWidth={640}\n self=\"center\"\n width=\"100%\"\n >\n <YStack\n width={48}\n height={48}\n items=\"center\"\n justify=\"center\"\n rounded=\"$4\"\n bg=\"$color3\"\n >\n <Icon size={24} />\n </YStack>\n\n <YStack gap=\"$2\" items=\"center\" maxW={480}>\n <Text fontSize=\"$6\" fontWeight=\"500\" text=\"center\" letterSpacing={-0.3}>\n {title}\n </Text>\n <Text fontSize=\"$3\" color=\"$color11\" text=\"center\">\n {description}\n </Text>\n </YStack>\n\n {bullets && bullets.length ? (\n <YStack gap=\"$2\" self=\"stretch\" maxW={420} mx=\"auto\">\n {bullets.map((b) => (\n <XStack key={b} gap=\"$2\" items=\"flex-start\">\n <YStack pt=\"$1\">\n <Check size={14} color=\"$color10\" />\n </YStack>\n <Text fontSize=\"$3\" color=\"$color11\" flex={1}>\n {b}\n </Text>\n </XStack>\n ))}\n </YStack>\n ) : null}\n\n {primary || secondary ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" justify=\"center\">\n {primary ? (\n <PrimaryButton\n icon={primary.icon}\n iconAfter={primary.href ? <ExternalLink size={15} /> : <ArrowRight size={15} />}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: primary.label })\n if (primary.href) openHref(primary.href)\n else primary.onPress?.()\n }}\n >\n {primary.label}\n </PrimaryButton>\n ) : null}\n {secondary ? (\n <Button\n chromeless\n icon={secondary.icon}\n iconAfter={secondary.href ? <ExternalLink size={14} /> : undefined}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: secondary.label })\n if (secondary.href) openHref(secondary.href)\n else secondary.onPress?.()\n }}\n >\n {secondary.label}\n </Button>\n ) : null}\n </XStack>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\n/**\n * Form field primitives — labeled rows over Hanzo GUI inputs.\n *\n * One way to render a labeled control. Every editor field goes through these, so\n * spacing, disabled styling, and the label column stay consistent. Style props\n * use the v5 shorthand set (p/px/items/justify/...).\n */\nimport { createContext, useContext, type CSSProperties, type ReactNode } from 'react'\nimport {\n Input,\n Label,\n Switch,\n TextArea,\n Slider,\n Text,\n XStack,\n YStack,\n} from '@hanzo/gui'\n\nimport { textSize, useEmit, type UiEvent } from './instrument'\n\n// The label the caller ALREADY typed on the FieldRow becomes the analytics name —\n// so an app never labels a field twice and no field reports as \"unnamed\".\nconst FieldNameContext = createContext<string | undefined>(undefined)\n\n/** Every Field* control reports `{component, action:'change', id:<row label>}`;\n * the VALUE is only ever a scalar or a text LENGTH, never free text. */\nfunction useFieldTrack(component: string): (e: Omit<UiEvent, 'component'>) => void {\n const track = useEmit()\n const name = useContext(FieldNameContext)\n return (e) => track({ component, id: name, ...e })\n}\n\n/**\n * Labeled row. STACKED on phones (label full-width above a full-width control) so a\n * form inside a SlideOver stays usable and never clips its labels; a fixed 180px\n * label column + flexing control at $md+ (the two-column desktop/tablet layout).\n */\nexport function FieldRow({ label, children }: { label: string; children: ReactNode }) {\n return (\n <FieldNameContext.Provider value={label}>\n <XStack gap=\"$3\" items=\"flex-start\" flexWrap=\"wrap\">\n <Label width=\"100%\" pt=\"$2\" color=\"$color11\" fontSize=\"$3\" $md={{ width: 180 }}>\n {label}\n </Label>\n <YStack flex={1} minW={0} $md={{ minW: 240 }}>\n {children}\n </YStack>\n </XStack>\n </FieldNameContext.Provider>\n )\n}\n\nexport function FieldText({\n value,\n onChange,\n disabled,\n secure,\n placeholder,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n secure?: boolean\n placeholder?: string\n}) {\n const track = useFieldTrack('FieldText')\n return (\n <Input\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: secure ? 0 : textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n secureTextEntry={secure}\n placeholder={placeholder}\n autoCapitalize=\"none\"\n />\n )\n}\n\nexport function FieldTextArea({\n value,\n onChange,\n disabled,\n rows = 6,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n rows?: number\n}) {\n const track = useFieldTrack('FieldTextArea')\n return (\n <TextArea\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n numberOfLines={rows}\n />\n )\n}\n\nexport function FieldSwitch({\n checked,\n onChange,\n disabled,\n}: {\n checked: boolean\n onChange: (v: boolean) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSwitch')\n return (\n <Switch\n checked={checked}\n onCheckedChange={(v: boolean) => {\n track({ action: 'change', value: v })\n onChange(v)\n }}\n disabled={disabled}\n size=\"$3\"\n >\n <Switch.Thumb />\n </Switch>\n )\n}\n\n/**\n * A labeled dropdown.\n *\n * Renders a REAL native `<select>` on web (and the OS-native picker on mobile),\n * NOT the `@hanzo/gui` `<Select native>` — that primitive is broken in gui 7.3.0:\n * `native` emits bare `<option>` elements with NO `<select>` wrapper, so every\n * dropdown across the console (region/size pickers, all edit forms) rendered as a\n * non-interactive flat list of option text that buried the surrounding controls.\n * A native `<select>` is compact, keyboard/ARIA-accessible, gives the native\n * mobile picker (best small-screen UX), and is themed via the Tamagui CSS custom\n * properties (`--background`/`--color12`/`--borderColor`) so it tracks light/dark.\n */\nconst CHEVRON = `data:image/svg+xml,${encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9a9a9a\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>',\n)}`\n\nfunction selectStyle(disabled?: boolean): CSSProperties {\n return {\n width: '100%',\n boxSizing: 'border-box',\n appearance: 'none',\n WebkitAppearance: 'none',\n MozAppearance: 'none',\n background: `var(--background) url(\"${CHEVRON}\") no-repeat right 10px center`,\n color: 'var(--color12)',\n border: '1px solid var(--borderColor)',\n borderRadius: 9,\n padding: '9px 34px 9px 12px',\n fontSize: 14,\n lineHeight: '20px',\n fontFamily: 'inherit',\n height: 40,\n outline: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }\n}\n\nconst OPTION_STYLE: CSSProperties = { background: 'var(--color2)', color: 'var(--color12)' }\n\nexport function FieldSelect({\n value,\n options,\n onChange,\n disabled,\n placeholder = 'Select…',\n}: {\n value: string\n options: string[]\n onChange: (v: string) => void\n disabled?: boolean\n placeholder?: string\n}) {\n // When the current value isn't one of the options (empty/unset), show the\n // placeholder as a selected-but-disabled row so the control reads honestly.\n const track = useFieldTrack('FieldSelect')\n const showPlaceholder = value === '' || !options.includes(value)\n return (\n <select\n value={showPlaceholder ? '' : value}\n onChange={(e) => {\n track({ action: 'change', value: e.currentTarget.value })\n onChange(e.currentTarget.value)\n }}\n disabled={disabled}\n aria-label={placeholder}\n style={selectStyle(disabled)}\n >\n {showPlaceholder ? (\n <option value=\"\" disabled style={OPTION_STYLE}>\n {placeholder}\n </option>\n ) : null}\n {options.map((opt) => (\n <option key={opt} value={opt} style={OPTION_STYLE}>\n {opt}\n </option>\n ))}\n </select>\n )\n}\n\nexport function FieldSlider({\n value,\n min,\n max,\n step,\n onChange,\n disabled,\n}: {\n value: number\n min: number\n max: number\n step: number\n onChange: (v: number) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSlider')\n return (\n <XStack gap=\"$3\" items=\"center\">\n <Text width={56} fontSize=\"$3\" color=\"$color11\">\n {value}\n </Text>\n <Slider\n flex={1}\n min={min}\n max={max}\n step={step}\n value={[value]}\n onValueChange={(v) => {\n track({ action: 'change', value: v[0] ?? min })\n onChange(v[0] ?? min)\n }}\n disabled={disabled}\n >\n <Slider.Track>\n <Slider.TrackActive />\n </Slider.Track>\n <Slider.Thumb index={0} circular />\n </Slider>\n </XStack>\n )\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FrameworkClient } from './client';
|
|
2
|
+
import type { DocType } from './types';
|
|
3
|
+
export interface CollectionBuilderProps {
|
|
4
|
+
client: FrameworkClient;
|
|
5
|
+
module: string;
|
|
6
|
+
/** Editing an existing collection's schema (name locked), else creating a new one. */
|
|
7
|
+
existing?: DocType;
|
|
8
|
+
/** Called with the saved collection's name after create/update. */
|
|
9
|
+
onSaved: (name: string) => void;
|
|
10
|
+
onCancel: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function CollectionBuilder({ client, module, existing, onSaved, onCancel }: CollectionBuilderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CollectionsBrowser — the app-lane home: the DocTypes of a `module` (a CMS
|
|
3
|
-
* "collection"
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* never a fabricated collection.
|
|
3
|
+
* "collection", an ERP document type, a Helpdesk ticket type are all just a
|
|
4
|
+
* framework DocType tagged with that lane's module). It lists them, offers a
|
|
5
|
+
* first-run "Set up" that installs the lane's fixtures
|
|
6
|
+
* (`POST /v1/framework/modules/:module/install`), and a "New collection" that
|
|
7
|
+
* opens the on-page content-type builder.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Generic over the lane by construction: `module`, `label` and the first-run copy
|
|
10
|
+
* are props, and where copy is unset it is DERIVED FROM THE LANE rather than
|
|
11
|
+
* borrowed from one — the old default was the CMS's words, so an ERP org read
|
|
12
|
+
* about Pages and Posts. CMS, ERP, Helpdesk and anything later render this same
|
|
13
|
+
* component.
|
|
14
|
+
*
|
|
15
|
+
* Honest by construction — an org with the lane not yet installed sees the setup
|
|
16
|
+
* call to action, never a fabricated collection.
|
|
12
17
|
*/
|
|
13
18
|
import { type ReactNode } from 'react';
|
|
19
|
+
import type { IconLike } from '../product/color';
|
|
14
20
|
import type { FrameworkClient } from './client';
|
|
15
21
|
export interface CollectionsBrowserProps {
|
|
16
22
|
client: FrameworkClient;
|
|
17
|
-
/** The app lane: 'cms' | 'erp' | 'help' | … */
|
|
23
|
+
/** The app lane: 'cms' | 'erp' | 'help' | 'crm' | … */
|
|
18
24
|
module: string;
|
|
19
25
|
/** Human label for the lane (e.g. "Content"). */
|
|
20
26
|
label: string;
|
|
@@ -22,23 +28,24 @@ export interface CollectionsBrowserProps {
|
|
|
22
28
|
/** Open a collection's records. */
|
|
23
29
|
onOpen: (doctype: string) => void;
|
|
24
30
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
31
|
+
* First-run (pre-install) copy. The defect this fixes was not "there is a
|
|
32
|
+
* default" — it was that the default was ONE LANE'S copy (the CMS's), so an ERP
|
|
33
|
+
* org read about Pages and Posts. Unset now falls back to copy derived from the
|
|
34
|
+
* lane's own `label`, which is true for every lane; a host that has better words
|
|
35
|
+
* passes them.
|
|
29
36
|
*/
|
|
30
37
|
setupDescription?: string;
|
|
31
38
|
setupBullets?: string[];
|
|
32
39
|
/**
|
|
33
|
-
* Schema AUTHORING
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* shell) omits it and the "New collection" affordance is simply not offered.
|
|
37
|
-
* Nothing is faked either way.
|
|
40
|
+
* Schema AUTHORING injected. The library ships a real `CollectionBuilder`, so a
|
|
41
|
+
* host that omits this still gets one; a host that wants its own (or wants the
|
|
42
|
+
* affordance withheld from a read-only surface) supplies or suppresses it.
|
|
38
43
|
*/
|
|
39
44
|
renderBuilder?: (p: {
|
|
40
45
|
onSaved: (doctype: string) => void;
|
|
41
46
|
onCancel: () => void;
|
|
42
47
|
}) => ReactNode;
|
|
48
|
+
/** Lane icon (defaults to the generic collection glyph). */
|
|
49
|
+
icon?: IconLike;
|
|
43
50
|
}
|
|
44
|
-
export declare function CollectionsBrowser({ client, module, label, subtitle, onOpen, setupDescription, setupBullets, renderBuilder }: CollectionsBrowserProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export declare function CollectionsBrowser({ client, module, label, subtitle, onOpen, setupDescription, setupBullets, renderBuilder, icon, }: CollectionsBrowserProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DocTypeRecords — the
|
|
3
|
-
*
|
|
2
|
+
* DocTypeRecords — the documents surface for ONE framework DocType, driven
|
|
3
|
+
* ENTIRELY by DocType metadata. It loads the schema (→ @hanzo/data
|
|
4
4
|
* `FieldDefinition[]` via `docTypeToFields`), the documents, and each relation's
|
|
5
|
-
* candidate records, then renders
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* candidate records, then renders them in the shape the CONTAINER can actually
|
|
6
|
+
* hold. Every field type shows the right Display/Input with ZERO per-doctype code,
|
|
7
|
+
* so a CMS Page, an ERP Invoice, or a Helpdesk Ticket all render here.
|
|
8
|
+
*
|
|
9
|
+
* MOBILE FIRST — the layout is a decision, not a media query afterthought:
|
|
10
|
+
* · phone → `RecordCards`, a stacked card per record. A table of 12 columns on
|
|
11
|
+
* a 390px screen is a sideways drag; a card is a read.
|
|
12
|
+
* · desktop → @hanzo/data's `RecordsView` (table ⇆ board, filter/sort/group,
|
|
13
|
+
* inline cell editing).
|
|
14
|
+
* The first paint is ALWAYS the card list (see `responsive.ts`), and the table is
|
|
15
|
+
* the enhancement applied once the box measures wide enough.
|
|
16
|
+
*
|
|
17
|
+
* The table also finally honors the DocType's OWN column projection
|
|
18
|
+
* (`listHiddenFields` → `inListView`): a Sales Order that declares 4 list columns
|
|
19
|
+
* out of 12 fields now renders 4. That projection was implemented and tested but
|
|
20
|
+
* never passed to a view — which is exactly why the grid was unusable narrow.
|
|
9
21
|
*
|
|
10
22
|
* Persistence is REAL: an inline cell edit sends the FULL record (the engine
|
|
11
23
|
* validates the whole document on update) via `savePayload`, and reflects the
|
|
@@ -15,6 +27,7 @@
|
|
|
15
27
|
import { type ReactNode } from 'react';
|
|
16
28
|
import type { FrameworkClient } from './client';
|
|
17
29
|
import type { DocType, FrameworkDoc } from './types';
|
|
30
|
+
import type { MediaUploader } from './media';
|
|
18
31
|
export interface DocTypeRecordsProps {
|
|
19
32
|
client: FrameworkClient;
|
|
20
33
|
/** DocType name to render. */
|
|
@@ -27,10 +40,15 @@ export interface DocTypeRecordsProps {
|
|
|
27
40
|
project?: string;
|
|
28
41
|
title?: ReactNode;
|
|
29
42
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
43
|
+
* Object storage for a media/asset collection. Without it a media DocType still
|
|
44
|
+
* renders as records (honest) — the DAM gallery needs a real place to put bytes,
|
|
45
|
+
* and this layer refuses to invent one.
|
|
46
|
+
*/
|
|
47
|
+
media?: MediaUploader;
|
|
48
|
+
/**
|
|
49
|
+
* A host that would rather draw the asset gallery itself supplies this; it wins
|
|
50
|
+
* over the built-in `MediaGrid`. Both paths need somewhere real to put bytes, so
|
|
51
|
+
* a host with neither gets the honest generic table — nothing is faked.
|
|
34
52
|
*/
|
|
35
53
|
renderMedia?: (p: {
|
|
36
54
|
dt: DocType;
|
|
@@ -39,5 +57,7 @@ export interface DocTypeRecordsProps {
|
|
|
39
57
|
onChanged: () => void;
|
|
40
58
|
toolbarExtra: ReactNode;
|
|
41
59
|
}) => ReactNode;
|
|
60
|
+
/** How many documents to fetch (the engine caps its own page). */
|
|
61
|
+
limit?: number;
|
|
42
62
|
}
|
|
43
|
-
export declare function DocTypeRecords({ client, doctype, onOpen, onCreate, project, title, renderMedia }: DocTypeRecordsProps): import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
export declare function DocTypeRecords({ client, doctype, onOpen, onCreate, project, title, media, renderMedia, limit, }: DocTypeRecordsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaGrid — the DAM (media library) for a media DocType (an Attach-backed
|
|
3
|
+
* collection). A REAL asset manager: drag/drop or pick files → they upload through
|
|
4
|
+
* the injected `MediaUploader` (the host's own per-org object store) → a document
|
|
5
|
+
* is created from the DocType's OWN schema → the grid shows a live thumbnail
|
|
6
|
+
* (presigned on view, since object URLs are short-lived). Delete removes the
|
|
7
|
+
* document AND the object.
|
|
8
|
+
*
|
|
9
|
+
* The document body is `mediaDocPayload(dt, facts)` — metadata-driven. This used
|
|
10
|
+
* to write a hardcoded `{title, file, mime, size, width, height}`, which is the
|
|
11
|
+
* CMS Media fixture's schema: a lane whose media type labels its rows `caption`
|
|
12
|
+
* got untitled rows, silently, because the engine drops unknown keys. Now the
|
|
13
|
+
* filename lands on the DocType's declared title field and each fact is written
|
|
14
|
+
* only if that DocType has somewhere to put it.
|
|
15
|
+
*
|
|
16
|
+
* MOBILE FIRST: tiles are flex-basis, not a fixed 200px — two-up on a phone,
|
|
17
|
+
* filling whatever the box gives on a desktop. The upload control is a full-width
|
|
18
|
+
* tap target on a phone, and the per-tile delete is a 44px control rather than a
|
|
19
|
+
* 13px glyph.
|
|
20
|
+
*
|
|
21
|
+
* Web-first by nature (file picker + drag/drop + `<img>` thumbnails), like the
|
|
22
|
+
* other file-touching components in this layer.
|
|
23
|
+
*/
|
|
24
|
+
import { type ReactNode } from 'react';
|
|
25
|
+
import type { FrameworkClient } from './client';
|
|
26
|
+
import type { DocType, FrameworkDoc } from './types';
|
|
27
|
+
import { type MediaUploader } from './media';
|
|
28
|
+
export interface MediaGridProps {
|
|
29
|
+
client: FrameworkClient;
|
|
30
|
+
dt: DocType;
|
|
31
|
+
docs: FrameworkDoc[];
|
|
32
|
+
/** The host's object store, bound to this lane's bucket. */
|
|
33
|
+
media: MediaUploader;
|
|
34
|
+
/** Open a media document's detail (edit alt text, replace, etc.). */
|
|
35
|
+
onOpen: (name: string) => void;
|
|
36
|
+
/** Re-fetch the media list after an upload/delete. */
|
|
37
|
+
onChanged: () => void;
|
|
38
|
+
toolbarExtra?: ReactNode;
|
|
39
|
+
/** Accepted file types for the picker. */
|
|
40
|
+
accept?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function MediaGrid({ client, dt, docs, media, onOpen, onChanged, toolbarExtra, accept, }: MediaGridProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecordCards — the PHONE view of a DocType's documents: one stacked card per
|
|
3
|
+
* record instead of a horizontally-scrolling grid.
|
|
4
|
+
*
|
|
5
|
+
* A 12-column ERP Sales Order in a table on a 390px screen is a wall you drag
|
|
6
|
+
* sideways to read one row; the same record as a card is a title plus its declared
|
|
7
|
+
* list columns as label → value pairs, reading top to bottom in the width you
|
|
8
|
+
* have. Nothing is hidden that the table would have shown — the card renders the
|
|
9
|
+
* DocType's OWN `inListView` projection (`cardFields`), and the full record is one
|
|
10
|
+
* tap away.
|
|
11
|
+
*
|
|
12
|
+
* The whole card is the tap target (far past the 44px floor), values render
|
|
13
|
+
* through the SAME `FieldDisplay` router the table uses, so every field type
|
|
14
|
+
* behaves identically in both, and search is the pure `searchRecords` — no second
|
|
15
|
+
* filtering model.
|
|
16
|
+
*/
|
|
17
|
+
import { type ReactNode } from 'react';
|
|
18
|
+
import { type FieldDefinition } from '@hanzo/data';
|
|
19
|
+
export interface RecordCardsProps {
|
|
20
|
+
/** The field shown as the card's headline (the DocType's titleField). */
|
|
21
|
+
titleField?: string;
|
|
22
|
+
/** All fields (used for search); `cardFields` decides what a card shows. */
|
|
23
|
+
fields: FieldDefinition[];
|
|
24
|
+
/** The subset rendered as label → value rows on each card. */
|
|
25
|
+
cardFields: FieldDefinition[];
|
|
26
|
+
records: Record<string, unknown>[];
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
empty?: ReactNode;
|
|
29
|
+
onOpen?: (record: Record<string, unknown>) => void;
|
|
30
|
+
onCreate?: () => void;
|
|
31
|
+
createLabel?: string;
|
|
32
|
+
/** Extra toolbar control (e.g. Refresh). */
|
|
33
|
+
toolbarExtra?: ReactNode;
|
|
34
|
+
}
|
|
35
|
+
export declare function RecordCards({ titleField, fields, cardFields, records, loading, empty, onOpen, onCreate, createLabel, toolbarExtra, }: RecordCardsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic for the content-type builder — the model + validation + DocType
|
|
3
|
+
* projection for defining a collection's fields on-page. No React, no I/O, so it
|
|
4
|
+
* is unit-tested in plain Node. The builder UI is a thin shell over these.
|
|
5
|
+
*
|
|
6
|
+
* A "collection" is a framework DocType tagged with the lane module; a "field" is
|
|
7
|
+
* a DocField. The builder offers exactly the engine's fieldtypes (mapped to
|
|
8
|
+
* friendly labels), lets the author add/remove/reorder/require fields, and
|
|
9
|
+
* projects the whole thing to a valid `DocType` create body. Names are slugified
|
|
10
|
+
* to field-name rules so a save never 400s on a bad identifier.
|
|
11
|
+
*/
|
|
12
|
+
import type { DocType, DocField, Fieldtype } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* The builder's field-type vocabulary — every framework fieldtype, with a friendly
|
|
15
|
+
* label + hint. This is the ONE list the "add field" type picker renders, so the
|
|
16
|
+
* builder always offers exactly what the engine accepts (no drift). Order is the
|
|
17
|
+
* author-facing order (common types first).
|
|
18
|
+
*/
|
|
19
|
+
export declare const BUILDER_FIELD_TYPES: {
|
|
20
|
+
type: Fieldtype;
|
|
21
|
+
label: string;
|
|
22
|
+
hint: string;
|
|
23
|
+
}[];
|
|
24
|
+
/** The friendly label for a fieldtype (falls back to the raw type). */
|
|
25
|
+
export declare function fieldTypeLabel(t: Fieldtype): string;
|
|
26
|
+
/** True when a fieldtype needs `options` (Select choices, Link/Table target). */
|
|
27
|
+
export declare function fieldNeedsOptions(t: Fieldtype): boolean;
|
|
28
|
+
/** One editable row in the builder — a superset of DocField the UI mutates. */
|
|
29
|
+
export interface BuilderField {
|
|
30
|
+
/** Stable client key for React (not persisted). */
|
|
31
|
+
key: string;
|
|
32
|
+
/** The field's label (author-facing); the fieldname is derived from it. */
|
|
33
|
+
label: string;
|
|
34
|
+
type: Fieldtype;
|
|
35
|
+
required: boolean;
|
|
36
|
+
inListView: boolean;
|
|
37
|
+
/** Select → newline-joined choices; Link/Table → target collection name. */
|
|
38
|
+
options: string;
|
|
39
|
+
}
|
|
40
|
+
/** A fresh, unique client key. */
|
|
41
|
+
export declare function newKey(): string;
|
|
42
|
+
/** A blank field row of a given type. */
|
|
43
|
+
export declare function blankField(type?: Fieldtype): BuilderField;
|
|
44
|
+
/**
|
|
45
|
+
* The default starter fields for a NEW content collection: a required Title, a URL
|
|
46
|
+
* Slug (the autoname key), a rich Body, and a Draft/Published Status. The author
|
|
47
|
+
* edits from here. Mirrors the cloud CMS seed so a hand-built collection matches a
|
|
48
|
+
* seeded one.
|
|
49
|
+
*/
|
|
50
|
+
export declare function starterFields(): BuilderField[];
|
|
51
|
+
/** An existing DocType → editable builder rows (drops engine-managed fields). */
|
|
52
|
+
export declare function fieldsFromDocType(dt: DocType): BuilderField[];
|
|
53
|
+
/** The fieldname derived from a label (slug with underscores; identifier-safe). */
|
|
54
|
+
export declare function fieldNameFromLabel(label: string): string;
|
|
55
|
+
export interface BuilderValidation {
|
|
56
|
+
ok: boolean;
|
|
57
|
+
/** Per-row error keyed by BuilderField.key. */
|
|
58
|
+
fieldErrors: Record<string, string>;
|
|
59
|
+
/** A form-level error (name / no-fields). */
|
|
60
|
+
formError: string | null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate the whole builder: the collection name, at least one field, unique
|
|
64
|
+
* non-empty field labels/names, and options present where required. Pure.
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateBuilder(name: string, fields: BuilderField[]): BuilderValidation;
|
|
67
|
+
/** A builder row → a framework DocField (drops UI-only bits, coerces options). */
|
|
68
|
+
export declare function toDocField(f: BuilderField): DocField;
|
|
69
|
+
/** Move a row within the ordered field list (a no-op at either end). Pure. */
|
|
70
|
+
export declare function moveField(fields: BuilderField[], key: string, dir: -1 | 1): BuilderField[];
|
|
71
|
+
/**
|
|
72
|
+
* The builder → a `DocType` create body. Auto-derives `autoname`/`titleField` from
|
|
73
|
+
* conventional fields when present (a `slug` field → `field:slug` naming; a `title`
|
|
74
|
+
* field → the title). Tags the module so the collection joins the lane.
|
|
75
|
+
*/
|
|
76
|
+
export declare function toDocType(name: string, module: string, fields: BuilderField[]): DocType;
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* The generic client for the Hanzo Framework DocType engine (`/v1/framework/*`).
|
|
3
|
+
* It is metadata-driven and doctype-agnostic: the SAME `records` CRUD serves a CMS
|
|
4
|
+
* Page, an ERP Invoice, or a Helpdesk Ticket — an app lane is just a `module`
|
|
5
|
+
* filter over `doctypes`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* THE HOST OWNS TRANSPORT. This module never picks an origin, a credential, or a
|
|
8
|
+
* proxy: it takes a `FrameworkTransport` and builds paths RELATIVE to the framework
|
|
9
|
+
* root (`doctypes`, `Article/hello`, `modules/cms/install`). The console maps those
|
|
10
|
+
* onto its own user-bearer `/v1/framework/...` proxy; a native app or a test maps
|
|
11
|
+
* them somewhere else. One client, any host.
|
|
12
12
|
*
|
|
13
|
-
* Names (DocType + document)
|
|
14
|
-
*
|
|
15
|
-
* defensively from `{ data }` (or a bare array), so a shape drift
|
|
16
|
-
* rather than throwing.
|
|
13
|
+
* Names (DocType + document) go verbatim into the path, so they are
|
|
14
|
+
* `encodeURIComponent`-escaped here and kept slug-style on create (`slugify`).
|
|
15
|
+
* Payloads are read defensively from `{ data }` (or a bare array), so a shape drift
|
|
16
|
+
* degrades a list rather than throwing.
|
|
17
17
|
*/
|
|
18
|
-
import type { DocType, FrameworkDoc, ListQuery, ModuleInfo
|
|
18
|
+
import type { DocType, FrameworkDoc, InstallResult, ListQuery, ModuleInfo } from './types';
|
|
19
19
|
/**
|
|
20
|
-
* The
|
|
21
|
-
*
|
|
22
|
-
* already owns — this layer never touches auth, cookies, or origins.
|
|
20
|
+
* The ONE seam a host injects. Paths are relative to the framework root and never
|
|
21
|
+
* start with a slash; the host prefixes its own base (and adds credentials).
|
|
23
22
|
*/
|
|
24
|
-
export interface
|
|
25
|
-
get:
|
|
26
|
-
post:
|
|
27
|
-
put:
|
|
23
|
+
export interface FrameworkTransport {
|
|
24
|
+
get: (path: string) => Promise<unknown>;
|
|
25
|
+
post: (path: string, body?: unknown) => Promise<unknown>;
|
|
26
|
+
put: (path: string, body?: unknown) => Promise<unknown>;
|
|
28
27
|
del: (path: string) => Promise<void>;
|
|
29
28
|
}
|
|
30
|
-
/**
|
|
29
|
+
/** The name 8.0.24 shipped for the same seam. One concept, one shape, two spellings. */
|
|
30
|
+
export type Transport = FrameworkTransport;
|
|
31
|
+
/** Pull the first array under a common envelope key (the engine uses `data`). */
|
|
32
|
+
export declare function rows(payload: unknown): Record<string, unknown>[];
|
|
33
|
+
/** Build the generic document list querystring from a typed ListQuery. Pure. */
|
|
31
34
|
export declare function listQuery(q?: ListQuery): string;
|
|
32
|
-
/**
|
|
33
|
-
export
|
|
35
|
+
/** The engine surface, as consumed by every DocType view. */
|
|
36
|
+
export interface FrameworkClient {
|
|
34
37
|
/** The DocType registry (schemas) — the collection definitions of every app lane. */
|
|
35
38
|
doctypes: {
|
|
36
39
|
list: () => Promise<DocType[]>;
|
|
@@ -64,24 +67,8 @@ export declare function createFrameworkClient(t: Transport): {
|
|
|
64
67
|
assign: (user: string, role: string) => Promise<void>;
|
|
65
68
|
revoke: (user: string, role: string) => Promise<void>;
|
|
66
69
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/** An HTTP failure carrying the status, so `classifyBackend` can tell 401 from 403 from 404. */
|
|
70
|
-
export declare class FrameworkHttpError extends Error {
|
|
71
|
-
readonly status: number;
|
|
72
|
-
constructor(status: number, message: string);
|
|
70
|
+
/** Engine health/reach probe — `GET /summary`. */
|
|
71
|
+
summary: () => Promise<Record<string, unknown>>;
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
baseUrl: string;
|
|
77
|
-
/** Called per request — a bearer token, or null when signed out. */
|
|
78
|
-
token?: () => string | null | undefined;
|
|
79
|
-
/** Injectable for tests; defaults to the global `fetch`. */
|
|
80
|
-
fetchImpl?: typeof fetch;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* The bearer transport every site shell uses: the caller's IAM access token on
|
|
84
|
-
* the Authorization header, straight to the API origin. No cookie, no second
|
|
85
|
-
* credential path — the engine resolves the org from the token's owner claim.
|
|
86
|
-
*/
|
|
87
|
-
export declare function fetchTransport(opts: FetchTransportOptions): Transport;
|
|
73
|
+
/** Bind the framework surface to a host's transport. */
|
|
74
|
+
export declare function createFrameworkClient(t: FrameworkTransport): FrameworkClient;
|