@hjmds/design-contracts 1.19.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-tools.d.ts +38 -0
- package/dist/authoring-tools.d.ts.map +1 -0
- package/dist/authoring-tools.js +35 -0
- package/dist/authoring-tools.js.map +1 -0
- package/dist/behaviors.d.ts +34 -3
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js +2 -0
- package/dist/behaviors.js.map +1 -1
- package/dist/bottom-navigation.d.ts +10 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +10 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/calendar.d.ts +9 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +4 -2
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +148 -2
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +6 -2
- package/dist/catalog.js.map +1 -1
- package/dist/chart.d.ts +398 -0
- package/dist/chart.d.ts.map +1 -0
- package/dist/chart.js +414 -0
- package/dist/chart.js.map +1 -0
- package/dist/collection-editing.d.ts +38 -0
- package/dist/collection-editing.d.ts.map +1 -0
- package/dist/collection-editing.js +53 -0
- package/dist/collection-editing.js.map +1 -0
- package/dist/communication.d.ts +29 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +11 -0
- package/dist/communication.js.map +1 -0
- package/dist/component-definitions.d.ts +1 -0
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +1 -0
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +25 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +13 -0
- package/dist/component-recipes.js.map +1 -1
- package/dist/data-table.d.ts +229 -5
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +242 -6
- package/dist/data-table.js.map +1 -1
- package/dist/data-workflows.d.ts +70 -0
- package/dist/data-workflows.d.ts.map +1 -0
- package/dist/data-workflows.js +109 -0
- package/dist/data-workflows.js.map +1 -0
- package/dist/dataviz.d.ts +7 -6
- package/dist/dataviz.d.ts.map +1 -1
- package/dist/dataviz.js +7 -6
- package/dist/dataviz.js.map +1 -1
- package/dist/gooey-navigation.d.ts +7 -0
- package/dist/gooey-navigation.d.ts.map +1 -1
- package/dist/gooey-navigation.js +10 -0
- package/dist/gooey-navigation.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/media-transport.d.ts +16 -0
- package/dist/media-transport.d.ts.map +1 -0
- package/dist/media-transport.js +17 -0
- package/dist/media-transport.js.map +1 -0
- package/dist/recipes.d.ts +1 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +1 -0
- package/dist/recipes.js.map +1 -1
- package/dist/semantic-colors.d.ts +9 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +5 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/showcase.d.ts.map +1 -1
- package/dist/showcase.js +3 -1
- package/dist/showcase.js.map +1 -1
- package/dist/social-compositions.d.ts +19 -0
- package/dist/social-compositions.d.ts.map +1 -0
- package/dist/social-compositions.js +33 -0
- package/dist/social-compositions.js.map +1 -0
- package/dist/streaming-answer.d.ts +18 -0
- package/dist/streaming-answer.d.ts.map +1 -0
- package/dist/streaming-answer.js +61 -0
- package/dist/streaming-answer.js.map +1 -0
- package/dist/task-controls.d.ts +31 -0
- package/dist/task-controls.d.ts.map +1 -0
- package/dist/task-controls.js +25 -0
- package/dist/task-controls.js.map +1 -0
- package/dist/toggle-feedback.d.ts +22 -0
- package/dist/toggle-feedback.d.ts.map +1 -0
- package/dist/toggle-feedback.js +32 -0
- package/dist/toggle-feedback.js.map +1 -0
- package/dist/transfer-task.d.ts +13 -0
- package/dist/transfer-task.d.ts.map +1 -0
- package/dist/transfer-task.js +13 -0
- package/dist/transfer-task.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/authoring-tools.md +31 -0
- package/docs/bottom-navigation.md +11 -0
- package/docs/calendar.md +3 -1
- package/docs/catalog-freeze.json +5 -0
- package/docs/chart.md +206 -0
- package/docs/collection-editing.md +83 -0
- package/docs/communication.md +35 -0
- package/docs/data-table.md +56 -6
- package/docs/data-workflows.md +26 -0
- package/docs/generated/component-maturity.md +6 -5
- package/docs/generated/renderer-evidence.json +95 -5
- package/docs/generated/renderer-evidence.md +6 -3
- package/docs/generated/showcase-manifest.json +66 -3
- package/docs/gooey-navigation.md +20 -0
- package/docs/library-gap-analysis.md +4 -2
- package/docs/media-transport.md +37 -0
- package/docs/social-compositions.md +51 -0
- package/docs/streaming-answer.md +57 -0
- package/docs/task-controls.md +38 -0
- package/docs/toggle-feedback.md +56 -0
- package/docs/transfer-task.md +33 -0
- package/docs/usage/README.md +15 -4
- package/docs/usage/components/activity-chip.md +2 -2
- package/docs/usage/components/bottom-navigation.md +14 -1
- package/docs/usage/components/button.md +39 -0
- package/docs/usage/components/calendar.md +4 -2
- package/docs/usage/components/chart.md +277 -0
- package/docs/usage/components/data-table.md +92 -18
- package/docs/usage/components/screen-layout.md +3 -1
- package/docs/usage/components/tabs.md +25 -0
- package/docs/usage/components/toast.md +1 -1
- package/docs/usage/compositions/action-feedback.md +18 -2
- package/docs/usage/compositions/activity-status.md +2 -2
- package/docs/usage/compositions/authoring-tools.md +90 -0
- package/docs/usage/compositions/collection-editing.md +110 -0
- package/docs/usage/compositions/communication.md +100 -0
- package/docs/usage/compositions/data-workflows.md +91 -0
- package/docs/usage/compositions/media-transport.md +87 -0
- package/docs/usage/compositions/social-compositions.md +91 -0
- package/docs/usage/compositions/streaming-answer.md +97 -0
- package/docs/usage/compositions/task-controls.md +93 -0
- package/docs/usage/compositions/toggle-feedback.md +84 -0
- package/docs/usage/compositions/transfer-task.md +86 -0
- package/docs/usage/compositions/web-additions.md +2 -2
- package/docs/usage/screens/common-shell.md +4 -0
- package/package.json +67 -1
package/dist/data-table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type {\n AsyncCollectionState,\n BehaviorContract,\n CheckboxState,\n CollectionKey,\n CollectionSelectionModel,\n} from \"./behaviors.js\";\nimport { collectionItemContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, fontWeight, spacing, type FontWeightValue, type TextVariant } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type DataTableAlign = \"start\" | \"center\" | \"end\";\n\nexport type DataTableColumnDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n /** Visible column header text; also the columnheader's accessible name. */\n header: string;\n align?: DataTableAlign;\n sortable?: boolean;\n /** Layout hint only (px or flex-basis); renderers may ignore or clamp it. */\n width?: number;\n}>;\n\nexport const dataTableColumnDefaults = {\n align: \"start\",\n sortable: false,\n} as const satisfies Readonly<{ align: DataTableAlign; sortable: boolean }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`DataTable ${field} must not be empty`);\n }\n}\n\nexport function validateDataTableColumns<Key extends CollectionKey>(\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (columns.length === 0) {\n throw new TypeError(\"DataTable must declare at least one column\");\n }\n const ids = new Set<Key>();\n for (const column of columns) {\n assertNonEmpty(column.id, \"column id\");\n if (ids.has(column.id)) {\n throw new TypeError(`Duplicate DataTable column id: ${column.id}`);\n }\n ids.add(column.id);\n assertNonEmpty(column.header, `column \"${column.id}\" header`);\n if (column.width !== undefined && (!Number.isFinite(column.width) || column.width <= 0)) {\n throw new RangeError(`DataTable column \"${column.id}\" width must be a positive finite number`);\n }\n }\n}\n\n/**\n * Row identity and selection deliberately do NOT reuse `CollectionItemDescriptor`\n * verbatim: a table row has no single visible `label`/`textValue` the way a\n * Menu or Select item does — its \"content\" is however many columns the\n * product renders per row. Only the parts of the Collection base contract\n * that are genuinely row-shaped (`CollectionKey` identity, `disabled`) apply.\n */\nexport type DataTableRowDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n disabled?: boolean;\n}>;\n\nexport function validateDataTableRows<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n): void {\n const ids = new Set<Key>();\n for (const row of rows) {\n assertNonEmpty(row.id, \"row id\");\n if (ids.has(row.id)) {\n throw new TypeError(`Duplicate DataTable row id: ${row.id}`);\n }\n ids.add(row.id);\n }\n}\n\n/** Row selection is the unmodified Collection base contract — no DataTable-only selection shape. */\nexport type DataTableSelection<Key extends CollectionKey = CollectionKey> =\n CollectionSelectionModel<Key>;\n\n/**\n * Header \"select all\" state as the same tri-state `CheckboxState` the\n * selection column's checkbox already renders (check/dash), instead of a new\n * `\"none\" | \"some\" | \"all\"` enum the recipe would have to translate again.\n * Disabled rows are excluded from both the denominator and the count.\n */\nexport function resolveDataTableSelectAllState<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n selectedKeys: ReadonlySet<Key>,\n): CheckboxState {\n const selectable = rows.filter((row) => !row.disabled);\n if (selectable.length === 0) return false;\n const selectedCount = selectable.filter((row) => selectedKeys.has(row.id)).length;\n if (selectedCount === 0) return false;\n if (selectedCount === selectable.length) return true;\n return \"mixed\";\n}\n\n/**\n * Values are the literal `aria-sort` vocabulary (minus `\"none\"`, which is\n * `null`) so the Web renderer passes them through without a translation\n * table — the same \"pass the platform value through\" choice Slider makes\n * for `valueText`.\n */\nexport type DataTableSortDirection = \"ascending\" | \"descending\";\n\nexport type DataTableSortState<Key extends CollectionKey = CollectionKey> = Readonly<{\n columnId: Key;\n direction: DataTableSortDirection;\n}> | null;\n\nexport type DataTableSortCycle = \"two-state\" | \"three-state\";\n\nexport const dataTableDefaults = {\n sortCycle: \"three-state\",\n} as const satisfies Readonly<{ sortCycle: DataTableSortCycle }>;\n\nexport function validateDataTableSortState<Key extends CollectionKey>(\n state: DataTableSortState<Key>,\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (state === null) return;\n if (state.direction !== \"ascending\" && state.direction !== \"descending\") {\n throw new TypeError(`Unsupported DataTable sort direction: ${String(state.direction)}`);\n }\n const column = columns.find((candidate) => candidate.id === state.columnId);\n if (!column) {\n throw new TypeError(`DataTable sort references unknown column id: ${String(state.columnId)}`);\n }\n if (!column.sortable) {\n throw new TypeError(`DataTable column \"${String(state.columnId)}\" is not sortable`);\n }\n}\n\n/**\n * Pure toggle: does not read or write product data. The product still owns\n * actually re-sorting rows (Combobox's local/external filtering split is the\n * same boundary — HJM judges intent, never executes the data operation).\n */\nexport function getNextDataTableSortState<Key extends CollectionKey>(\n current: DataTableSortState<Key>,\n columnId: Key,\n cycle: DataTableSortCycle = dataTableDefaults.sortCycle,\n): DataTableSortState<Key> {\n if (current === null || current.columnId !== columnId) {\n return { columnId, direction: \"ascending\" };\n }\n if (current.direction === \"ascending\") {\n return { columnId, direction: \"descending\" };\n }\n return cycle === \"three-state\" ? null : { columnId, direction: \"ascending\" };\n}\n\n/** The whole table's row-loading status — the unmodified Collection base async contract. */\nexport type DataTableAsyncState = AsyncCollectionState;\n\nexport type DataTableDensity = \"compact\" | \"regular\";\n\n/**\n * Reuses `collectionItemContract`'s hover/selected/focus tokens instead of a\n * second set of row-interaction colors — Menu, Select, and DataTable rows\n * stay one visual vocabulary.\n */\nexport const dataTableRecipe = {\n slots: [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ] as const,\n defaults: { density: \"regular\" },\n density: {\n compact: { paddingVertical: spacing.xs, paddingHorizontal: spacing.sm },\n regular: { paddingVertical: spacing.sm, paddingHorizontal: spacing.md },\n },\n header: {\n background: semanticColors.canvas,\n color: semanticColors.content.secondary,\n textVariant: \"label\",\n fontWeight: fontWeight.bold,\n borderBottom: semanticColors.border.default,\n },\n row: {\n borderBottom: semanticColors.border.subtle,\n hoverBackground: collectionItemContract.highlightedBackground,\n selectedBackground: collectionItemContract.selectedBackground,\n },\n cell: {\n color: semanticColors.content.body,\n textVariant: \"body\",\n },\n sortButton: {\n minTarget: control.minTouchTarget,\n color: semanticColors.content.secondary,\n activeColor: semanticColors.content.brand,\n },\n selectionCell: {\n width: control.minTouchTarget,\n },\n emptyState: {\n color: semanticColors.content.secondary,\n textVariant: \"body\",\n },\n errorState: {\n color: semanticColors.content.danger,\n textVariant: \"body\",\n },\n states: {\n focus: focusIndicatorContract,\n },\n} as const satisfies {\n slots: readonly [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ];\n defaults: { density: DataTableDensity };\n density: Record<DataTableDensity, { paddingVertical: number; paddingHorizontal: number }>;\n header: {\n background: ColorReference;\n color: ColorReference;\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n borderBottom: ColorReference;\n };\n row: {\n borderBottom: ColorReference;\n hoverBackground: ColorReference;\n selectedBackground: ColorReference;\n };\n cell: { color: ColorReference; textVariant: TextVariant };\n sortButton: { minTarget: number; color: ColorReference; activeColor: ColorReference };\n selectionCell: { width: number };\n emptyState: { color: ColorReference; textVariant: TextVariant };\n errorState: { color: ColorReference; textVariant: TextVariant };\n states: { focus: typeof focusIndicatorContract };\n};\n\n/**\n * No roving-tabindex grid keyboard navigation (arrow keys between cells):\n * no product using this contract has measured demand for full ARIA-grid\n * traversal, and it is a materially bigger accessibility surface than a\n * sortable header button plus native checkbox/radio tab stops.\n */\nexport const dataTableBehavior = {\n controlled: [\"selection\"],\n inputs: [\"columns\", \"rows\", \"sortState\", \"asyncState\"],\n events: [\"onSortChange\"],\n configuration: { sortCycle: [\"two-state\", \"three-state\"] },\n defaults: dataTableDefaults,\n stateAxes: {\n value: [\"selected\"],\n content: [\"idle\", \"loading\", \"loadingMore\", \"empty\", \"error\"],\n },\n web: {\n roles: [\"table\", \"row\", \"columnheader\", \"cell\", \"button\", \"checkbox\", \"radio\"],\n keyboard: [\"Tab\", \"Enter\", \"Space\"],\n focus: \"native\",\n },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"row-selection-reuses-the-shared-collection-selection-model-unchanged\",\n \"select-all-reuses-the-shared-tri-state-checkbox-value\",\n \"async-state-reuses-the-shared-collection-async-state-unchanged\",\n \"sortable-header-is-a-button-inside-the-columnheader-not-the-header-itself\",\n \"sort-direction-values-pass-through-to-aria-sort-without-translation\",\n \"disabled-rows-are-excluded-from-select-all-accounting\",\n \"pagination-or-load-more-is-composed-beneath-the-table-not-owned-by-it\",\n \"row-expansion-is-not-owned-here-compose-the-disclosure-group-contract-per-row\",\n \"a-data-cell-exposes-at-most-one-focusable-control\",\n \"no-roving-tabindex-grid-navigation-is-implied\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
1
|
+
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"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","sourcesContent":["import { accentColor, themeColor, type ColorReference } from \"./color-references.js\";\nimport type {\n AsyncCollectionState,\n BehaviorContract,\n CheckboxState,\n CollectionKey,\n CollectionSelectionModel,\n} from \"./behaviors.js\";\nimport { collectionItemContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, fontWeight, spacing, type FontWeightValue, type TextVariant } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type DataTableAlign = \"start\" | \"center\" | \"end\";\n\nexport type DataTableColumnDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n /** Visible column header text; also the columnheader's accessible name. */\n header: string;\n align?: DataTableAlign;\n sortable?: boolean;\n /** Layout hint only (px or flex-basis); renderers may ignore or clamp it. */\n width?: number;\n /**\n * First press sorts descending (TanStack Table `sortDescFirst`). Stat tables read \"highest first\";\n * lower-is-better columns (ERA, rank) leave it false. Added 2026-10-14 with the Native renderer.\n */\n sortDescFirst?: boolean;\n /**\n * Keeps the column visible while the others scroll horizontally (AG Grid / TanStack column\n * pinning, start edge only). Only a leading run of pinned columns is honored.\n */\n pinned?: \"start\";\n}>;\n\nexport const dataTableColumnDefaults = {\n align: \"start\",\n sortable: false,\n} as const satisfies Readonly<{ align: DataTableAlign; sortable: boolean }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`DataTable ${field} must not be empty`);\n }\n}\n\nexport function validateDataTableColumns<Key extends CollectionKey>(\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (columns.length === 0) {\n throw new TypeError(\"DataTable must declare at least one column\");\n }\n const ids = new Set<Key>();\n for (const column of columns) {\n assertNonEmpty(column.id, \"column id\");\n if (ids.has(column.id)) {\n throw new TypeError(`Duplicate DataTable column id: ${column.id}`);\n }\n ids.add(column.id);\n assertNonEmpty(column.header, `column \"${column.id}\" header`);\n if (column.width !== undefined && (!Number.isFinite(column.width) || column.width <= 0)) {\n throw new RangeError(`DataTable column \"${column.id}\" width must be a positive finite number`);\n }\n }\n}\n\n/**\n * Row identity and selection deliberately do NOT reuse `CollectionItemDescriptor`\n * verbatim: a table row has no single visible `label`/`textValue` the way a\n * Menu or Select item does — its \"content\" is however many columns the\n * product renders per row. Only the parts of the Collection base contract\n * that are genuinely row-shaped (`CollectionKey` identity, `disabled`) apply.\n */\nexport type DataTableRowDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n disabled?: boolean;\n /** Sub-row nesting (TanStack `depth`): 1 indents a substitute under a starter. 0–3. */\n depth?: number;\n}>;\n\nexport function validateDataTableRows<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n): void {\n const ids = new Set<Key>();\n for (const row of rows) {\n assertNonEmpty(row.id, \"row id\");\n if (ids.has(row.id)) {\n throw new TypeError(`Duplicate DataTable row id: ${row.id}`);\n }\n ids.add(row.id);\n if (row.depth !== undefined && (!Number.isInteger(row.depth) || row.depth < 0 || row.depth > 3)) {\n throw new RangeError(`DataTable row \"${String(row.id)}\" depth must be an integer from 0 to 3`);\n }\n }\n}\n\n/** Row selection is the unmodified Collection base contract — no DataTable-only selection shape. */\nexport type DataTableSelection<Key extends CollectionKey = CollectionKey> =\n CollectionSelectionModel<Key>;\n\n/**\n * Header \"select all\" state as the same tri-state `CheckboxState` the\n * selection column's checkbox already renders (check/dash), instead of a new\n * `\"none\" | \"some\" | \"all\"` enum the recipe would have to translate again.\n * Disabled rows are excluded from both the denominator and the count.\n */\nexport function resolveDataTableSelectAllState<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n selectedKeys: ReadonlySet<Key>,\n): CheckboxState {\n const selectable = rows.filter((row) => !row.disabled);\n if (selectable.length === 0) return false;\n const selectedCount = selectable.filter((row) => selectedKeys.has(row.id)).length;\n if (selectedCount === 0) return false;\n if (selectedCount === selectable.length) return true;\n return \"mixed\";\n}\n\n/**\n * Values are the literal `aria-sort` vocabulary (minus `\"none\"`, which is\n * `null`) so the Web renderer passes them through without a translation\n * table — the same \"pass the platform value through\" choice Slider makes\n * for `valueText`.\n */\nexport type DataTableSortDirection = \"ascending\" | \"descending\";\n\nexport type DataTableSortState<Key extends CollectionKey = CollectionKey> = Readonly<{\n columnId: Key;\n direction: DataTableSortDirection;\n}> | null;\n\nexport type DataTableSortCycle = \"two-state\" | \"three-state\";\n\nexport const dataTableDefaults = {\n sortCycle: \"three-state\",\n} as const satisfies Readonly<{ sortCycle: DataTableSortCycle }>;\n\nexport function validateDataTableSortState<Key extends CollectionKey>(\n state: DataTableSortState<Key>,\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (state === null) return;\n if (state.direction !== \"ascending\" && state.direction !== \"descending\") {\n throw new TypeError(`Unsupported DataTable sort direction: ${String(state.direction)}`);\n }\n const column = columns.find((candidate) => candidate.id === state.columnId);\n if (!column) {\n throw new TypeError(`DataTable sort references unknown column id: ${String(state.columnId)}`);\n }\n if (!column.sortable) {\n throw new TypeError(`DataTable column \"${String(state.columnId)}\" is not sortable`);\n }\n}\n\n/**\n * Pure toggle: does not read or write product data. The product still owns\n * actually re-sorting rows (Combobox's local/external filtering split is the\n * same boundary — HJM judges intent, never executes the data operation).\n */\nexport function getNextDataTableSortState<Key extends CollectionKey>(\n current: DataTableSortState<Key>,\n columnId: Key,\n cycle: DataTableSortCycle = dataTableDefaults.sortCycle,\n firstDirection: DataTableSortDirection = \"ascending\",\n): DataTableSortState<Key> {\n const second: DataTableSortDirection = firstDirection === \"ascending\" ? \"descending\" : \"ascending\";\n if (current === null || current.columnId !== columnId) {\n return { columnId, direction: firstDirection };\n }\n if (current.direction === firstDirection) {\n return { columnId, direction: second };\n }\n return cycle === \"three-state\" ? null : { columnId, direction: firstDirection };\n}\n\n/** The first direction a column's header applies, from `sortDescFirst`. */\nexport function dataTableFirstSortDirection(column: Pick<DataTableColumnDescriptor, \"sortDescFirst\">): DataTableSortDirection {\n return column.sortDescFirst ? \"descending\" : \"ascending\";\n}\n\nexport type DataTableSortValue = number | string | null | undefined;\n\n/**\n * Sort key for a displayed cell string: \"0.340\", \".340\", \"36 ⅓\", \"13.5억\", \"61.0%\" → numbers;\n * text such as \"7승0무3패\" or names stay strings; \"—\", \"-\" and \"\" are missing.\n */\nexport function toDataTableSortValue(value: unknown): DataTableSortValue {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n const text = String(value).trim();\n if (!text || text === \"—\" || text === \"-\" || text === \"–\") return null;\n // Two or more letters (any script) mean a label, not a number with a unit.\n if (/\\p{L}.*\\p{L}/u.test(text.replace(/[⅓⅔]/g, \"\"))) return text;\n const number = Number.parseFloat(text.replace(/[^0-9.+-]/g, \"\"));\n return Number.isFinite(number) ? number : text;\n}\n\n/**\n * Pure comparison: numbers numerically, strings with `localeCompare`, numbers before strings.\n * Missing values are not compared here; `sortDataTableRows` always sinks them.\n */\nexport function compareDataTableValues(a: Exclude<DataTableSortValue, null | undefined>, b: Exclude<DataTableSortValue, null | undefined>, locale?: string): number {\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"number\") return -1;\n if (typeof b === \"number\") return 1;\n return a.localeCompare(b, locale);\n}\n\n/**\n * Optional row-model helper (TanStack `getSortedRowModel` with `sortUndefined: \"last\"`): stable,\n * missing values always last in both directions. 1.20 left all sorting to products; Yajalal and\n * other stat tables reimplemented the same rules, so the shared helper exists now. Products with\n * server-side sorting keep calling `getNextDataTableSortState` only.\n */\nexport function sortDataTableRows<Row, Key extends CollectionKey>(\n rows: readonly Row[],\n sort: DataTableSortState<Key>,\n getValue: (row: Row, columnId: Key) => DataTableSortValue,\n options: Readonly<{ locale?: string }> = {},\n): Row[] {\n const indexed = rows.map((row, index) => ({ row, index }));\n if (!sort) return indexed.map(item => item.row);\n const sign = sort.direction === \"ascending\" ? 1 : -1;\n return indexed.sort((x, y) => {\n const a = getValue(x.row, sort.columnId), b = getValue(y.row, sort.columnId);\n const missingA = a === null || a === undefined, missingB = b === null || b === undefined;\n if (missingA || missingB) return missingA === missingB ? x.index - y.index : missingA ? 1 : -1;\n return sign * compareDataTableValues(a, b, options.locale) || x.index - y.index;\n }).map(item => item.row);\n}\n\n/** Leading pinned columns (only an unbroken run from the start is pinned). */\nexport function splitPinnedDataTableColumns<Column extends Pick<DataTableColumnDescriptor, \"pinned\">>(columns: readonly Column[]): Readonly<{ pinned: readonly Column[]; scrolling: readonly Column[] }> {\n let count = 0;\n while (count < columns.length && columns[count]!.pinned === \"start\") count++;\n return { pinned: columns.slice(0, count), scrolling: columns.slice(count) };\n}\n\n/** The whole table's row-loading status — the unmodified Collection base async contract. */\nexport type DataTableAsyncState = AsyncCollectionState;\n\nexport type DataTableDensity = \"compact\" | \"regular\";\n\n/**\n * Reuses `collectionItemContract`'s hover/selected/focus tokens instead of a\n * second set of row-interaction colors — Menu, Select, and DataTable rows\n * stay one visual vocabulary.\n */\nexport const dataTableRecipe = {\n slots: [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ] as const,\n defaults: { density: \"regular\" },\n density: {\n compact: { paddingVertical: spacing.xs, paddingHorizontal: spacing.sm },\n regular: { paddingVertical: spacing.sm, paddingHorizontal: spacing.md },\n },\n header: {\n background: semanticColors.canvas,\n color: semanticColors.content.secondary,\n textVariant: \"label\",\n fontWeight: fontWeight.bold,\n borderBottom: semanticColors.border.default,\n },\n row: {\n borderBottom: semanticColors.border.subtle,\n hoverBackground: collectionItemContract.highlightedBackground,\n selectedBackground: collectionItemContract.selectedBackground,\n },\n cell: {\n color: semanticColors.content.body,\n textVariant: \"body\",\n },\n sortButton: {\n minTarget: control.minTouchTarget,\n color: semanticColors.content.secondary,\n activeColor: semanticColors.content.brand,\n },\n selectionCell: {\n width: control.minTouchTarget,\n },\n emptyState: {\n color: semanticColors.content.secondary,\n textVariant: \"body\",\n },\n errorState: {\n color: semanticColors.content.danger,\n textVariant: \"body\",\n },\n states: {\n focus: focusIndicatorContract,\n },\n} as const satisfies {\n slots: readonly [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ];\n defaults: { density: DataTableDensity };\n density: Record<DataTableDensity, { paddingVertical: number; paddingHorizontal: number }>;\n header: {\n background: ColorReference;\n color: ColorReference;\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n borderBottom: ColorReference;\n };\n row: {\n borderBottom: ColorReference;\n hoverBackground: ColorReference;\n selectedBackground: ColorReference;\n };\n cell: { color: ColorReference; textVariant: TextVariant };\n sortButton: { minTarget: number; color: ColorReference; activeColor: ColorReference };\n selectionCell: { width: number };\n emptyState: { color: ColorReference; textVariant: TextVariant };\n errorState: { color: ColorReference; textVariant: TextVariant };\n states: { focus: typeof focusIndicatorContract };\n};\n\nexport const dataTableFamilyScenarios = [\n \"header-groups-span-contiguous-leaf-columns-and-pad-ungrouped-columns\",\n \"row-groups-insert-a-section-label-before-their-rows\",\n \"cell-tone-and-heat-are-product-decided-semantic-emphasis\",\n \"percentile-rank-flips-for-lower-is-better-stats-and-ties-take-the-midpoint\",\n \"bounded-height-tables-keep-the-header-fixed-while-the-body-scrolls\",\n \"line-score-pads-to-nine-periods-and-extends-for-extra-periods\",\n \"matrix-diagonal-is-a-blank-self-cell\",\n \"comparison-bars-favor-the-better-side-even-when-lower-is-better\",\n] as const;\n\n/**\n * No roving-tabindex grid keyboard navigation (arrow keys between cells):\n * no product using this contract has measured demand for full ARIA-grid\n * traversal, and it is a materially bigger accessibility surface than a\n * sortable header button plus native checkbox/radio tab stops.\n */\nexport const dataTableBehavior = {\n controlled: [\"selection\"],\n inputs: [\"columns\", \"rows\", \"sortState\", \"asyncState\"],\n events: [\"onSortChange\"],\n configuration: { sortCycle: [\"two-state\", \"three-state\"] },\n defaults: dataTableDefaults,\n stateAxes: {\n value: [\"selected\"],\n content: [\"idle\", \"loading\", \"loadingMore\", \"empty\", \"error\"],\n },\n web: {\n roles: [\"table\", \"row\", \"columnheader\", \"cell\", \"button\", \"checkbox\", \"radio\"],\n keyboard: [\"Tab\", \"Enter\", \"Space\"],\n focus: \"native\",\n },\n native: { roles: [\"button\", \"header\"], states: [\"selected\"], actions: [\"activate\"] },\n scenarios: [\n \"sort-desc-first-columns-start-descending-and-cycle-back\",\n \"missing-values-sink-to-the-bottom-in-both-directions\",\n \"pinned-start-columns-stay-visible-while-the-rest-scrolls\",\n \"row-selection-reuses-the-shared-collection-selection-model-unchanged\",\n \"select-all-reuses-the-shared-tri-state-checkbox-value\",\n \"async-state-reuses-the-shared-collection-async-state-unchanged\",\n \"sortable-header-is-a-button-inside-the-columnheader-not-the-header-itself\",\n \"sort-direction-values-pass-through-to-aria-sort-without-translation\",\n \"disabled-rows-are-excluded-from-select-all-accounting\",\n \"pagination-or-load-more-is-composed-beneath-the-table-not-owned-by-it\",\n \"row-expansion-is-not-owned-here-compose-the-disclosure-group-contract-per-row\",\n \"a-data-cell-exposes-at-most-one-focusable-control\",\n \"no-roving-tabindex-grid-navigation-is-implied\",\n ...dataTableFamilyScenarios,\n ],\n} as const satisfies BehaviorContract;\n\n/* ------------------------------------------------------------------------------------------------\n * Table family, 2026-10-14 (user request: research table libraries and absorb several table types).\n * Concepts re-derived, nothing vendored:\n * - column header groups (TanStack Table `getHeaderGroups`, AG Grid column groups),\n * - row groups with a section label and indented sub-rows (AG Grid row grouping, TanStack expanding),\n * - conditional cell tone and percentile heat (AG Grid `cellClassRules`/cellStyle heatmaps,\n * Baseball Savant percentile color: deeper red toward the best, deeper blue toward the worst),\n * - a vertically bounded body with a fixed header (react-native-sticky-table, CSS sticky headers),\n * - sports layouts that products kept hand-building: line score (period columns + totals, MLB/ESPN),\n * cross-table matrix (head-to-head records), and two-sided comparison rows (ESPN matchup).\n * ---------------------------------------------------------------------------------------------- */\n\n/** One header band above leaf columns. Columns not listed in any group get an empty placeholder cell. */\nexport type DataTableColumnGroup<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: string;\n header: string;\n columns: readonly Key[];\n}>;\n\nexport type DataTableHeaderBand = Readonly<{ id: string; header: string | null; span: number; columnIds: readonly string[] }>;\n\n/**\n * Resolves group bands over the given (already pinned-split) column order. A group must cover a\n * contiguous run; columns outside every group become placeholder bands so leaf widths still line up.\n */\nexport function resolveDataTableHeaderBands<Key extends CollectionKey>(\n columns: readonly Pick<DataTableColumnDescriptor<Key>, \"id\">[],\n groups: readonly DataTableColumnGroup<Key>[],\n): DataTableHeaderBand[] {\n const owner = new Map<Key, DataTableColumnGroup<Key>>();\n for (const group of groups) {\n assertNonEmpty(group.id, \"column group id\");\n assertNonEmpty(group.header, `column group \"${group.id}\" header`);\n for (const id of group.columns) {\n if (owner.has(id)) throw new TypeError(`DataTable column \"${String(id)}\" belongs to two groups`);\n owner.set(id, group);\n }\n }\n const bands: { id: string; header: string | null; columnIds: string[] }[] = [];\n for (const column of columns) {\n const group = owner.get(column.id) ?? null;\n const last = bands.at(-1);\n if (group && last && last.id === group.id) { last.columnIds.push(column.id); continue; }\n if (group && bands.some((band) => band.id === group.id)) throw new TypeError(`DataTable column group \"${group.id}\" must be contiguous`);\n bands.push({ id: group ? group.id : `placeholder-${column.id}`, header: group ? group.header : null, columnIds: [column.id] });\n }\n return bands.map((band) => ({ ...band, span: band.columnIds.length }));\n}\n\n/** Labeled row section (e.g. 선발 / 교체, 동부 / 서부). Rows not in any group render first, ungrouped. */\nexport type DataTableRowGroup<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: string;\n label: string;\n rows: readonly Key[];\n}>;\n\nexport type DataTableBodyItem<Key extends CollectionKey = CollectionKey> =\n | Readonly<{ kind: \"group\"; id: string; label: string }>\n | Readonly<{ kind: \"row\"; id: Key }>;\n\n/** Flattens rows + groups into the body order renderers draw. Unknown or duplicated ids throw. */\nexport function resolveDataTableBody<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n groups: readonly DataTableRowGroup<Key>[] = [],\n): DataTableBodyItem<Key>[] {\n if (groups.length === 0) return rows.map((row) => ({ kind: \"row\", id: row.id }));\n const known = new Set(rows.map((row) => row.id)), placed = new Set<Key>();\n const grouped: DataTableBodyItem<Key>[] = [];\n for (const group of groups) {\n assertNonEmpty(group.id, \"row group id\");\n assertNonEmpty(group.label, `row group \"${group.id}\" label`);\n grouped.push({ kind: \"group\", id: group.id, label: group.label });\n for (const id of group.rows) {\n if (!known.has(id)) throw new TypeError(`DataTable row group \"${group.id}\" references unknown row id: ${String(id)}`);\n if (placed.has(id)) throw new TypeError(`DataTable row \"${String(id)}\" belongs to two row groups`);\n placed.add(id);\n grouped.push({ kind: \"row\", id });\n }\n }\n const loose: DataTableBodyItem<Key>[] = rows.filter((row) => !placed.has(row.id)).map((row) => ({ kind: \"row\", id: row.id }));\n return [...loose, ...grouped];\n}\n\n/**\n * Semantic cell emphasis, decided by the product per cell (AG Grid cellClassRules):\n * `leader` marks a league/table best, `positive`/`negative` a direction, `muted` a non-qualifying value.\n */\nexport type DataTableCellTone = \"leader\" | \"positive\" | \"negative\" | \"muted\";\n\n/** Percentile heat (Savant): `high` side toward the best values, `low` toward the worst; strength 1–3. */\nexport type DataTableCellHeat = Readonly<{ side: \"high\" | \"low\"; strength: 1 | 2 | 3 }>;\n\n/**\n * Percentile rank (0–100, best = 100) of `value` among `sample`. `lowerIsBetter` flips ERA-style stats.\n * Ties take the midpoint rank, missing values return null. A one-value sample is the 50th percentile.\n */\nexport function percentileRank(value: number | null | undefined, sample: readonly (number | null | undefined)[], options: Readonly<{ lowerIsBetter?: boolean }> = {}): number | null {\n if (value === null || value === undefined || !Number.isFinite(value)) return null;\n const values = sample.filter((item): item is number => item !== null && item !== undefined && Number.isFinite(item));\n if (values.length <= 1) return 50;\n let below = 0, equal = 0;\n for (const item of values) {\n const better = options.lowerIsBetter ? item > value : item < value;\n if (item === value) equal++;\n else if (better) below++;\n }\n return Math.round(((below + (equal - 1) / 2) / (values.length - 1)) * 100);\n}\n\n/** Maps a percentile to heat: ≥90/≥75/≥60 high, ≤10/≤25/≤40 low, the middle stays neutral (null). */\nexport function percentileHeat(percentile: number | null): DataTableCellHeat | null {\n if (percentile === null) return null;\n if (percentile >= 90) return { side: \"high\", strength: 3 };\n if (percentile >= 75) return { side: \"high\", strength: 2 };\n if (percentile >= 60) return { side: \"high\", strength: 1 };\n if (percentile <= 10) return { side: \"low\", strength: 3 };\n if (percentile <= 25) return { side: \"low\", strength: 2 };\n if (percentile <= 40) return { side: \"low\", strength: 1 };\n return null;\n}\n\nexport const dataTableFamilyRecipe = {\n /** Heat background alpha per strength, over the high/low base colors. */\n heatAlpha: { 1: 0.14, 2: 0.26, 3: 0.42 },\n heat: { high: themeColor(\"danger\"), low: accentColor(\"info\") },\n tone: {\n leader: { color: semanticColors.content.primary, fontWeight: fontWeight.bold },\n positive: { color: themeColor(\"danger\"), fontWeight: fontWeight.bold },\n negative: { color: accentColor(\"info\"), fontWeight: fontWeight.bold },\n muted: { color: semanticColors.content.tertiary, fontWeight: fontWeight.regular },\n },\n groupRow: { background: semanticColors.surface.sunken, color: semanticColors.content.secondary, textVariant: \"label\" as TextVariant },\n /** Indent per sub-row depth level (substitutes under a starter, players under a team). */\n depthIndent: spacing.md,\n headerBand: { color: semanticColors.content.secondary, borderBottom: semanticColors.border.subtle },\n} as const;\n\n/* ---------------------------------------------- Line score ------------------------------------- */\n\nexport type LineScoreTeam = Readonly<{\n id: string;\n name: string;\n /** Runs per period; null = not played yet, \"X\" = home team did not bat in the last inning. */\n periods: readonly (number | \"X\" | null)[];\n /** Totals in display order, e.g. { R: 5, H: 9, E: 0, B: 4 }. */\n totals: Readonly<Record<string, number | null>>;\n}>;\n\nexport type LineScoreColumn = Readonly<{ id: string; header: string; kind: \"period\" | \"total\" }>;\n\n/**\n * Period columns are padded to `minPeriods` (9 innings) and extend for extra innings; totals follow.\n * The current period (live game) is reported so renderers can highlight it.\n */\nexport function resolveLineScore(teams: readonly LineScoreTeam[], options: Readonly<{ minPeriods?: number; totals: readonly string[]; currentPeriod?: number | null }>): Readonly<{ columns: LineScoreColumn[]; currentPeriod: number | null; leaderId: string | null }> {\n if (teams.length < 2) throw new TypeError(\"LineScore needs at least two teams\");\n const ids = new Set<string>();\n for (const team of teams) {\n assertNonEmpty(team.id, \"line score team id\");\n if (ids.has(team.id)) throw new TypeError(`Duplicate LineScore team id: ${team.id}`);\n ids.add(team.id);\n }\n const played = Math.max(...teams.map((team) => team.periods.length));\n const count = Math.max(options.minPeriods ?? 9, played);\n const columns: LineScoreColumn[] = Array.from({ length: count }, (_, i) => ({ id: `p${i + 1}`, header: String(i + 1), kind: \"period\" }));\n for (const total of options.totals) columns.push({ id: `t-${total}`, header: total, kind: \"total\" });\n const runs = options.totals[0];\n let leaderId: string | null = null;\n if (runs) {\n const scores = teams.map((team) => ({ id: team.id, value: team.totals[runs] ?? null }));\n const best = Math.max(...scores.map((score) => score.value ?? -Infinity));\n const leaders = scores.filter((score) => score.value === best && Number.isFinite(best));\n leaderId = leaders.length === 1 ? leaders[0]!.id : null;\n }\n const current = options.currentPeriod ?? null;\n return { columns, currentPeriod: current !== null && current >= 1 && current <= count ? current : null, leaderId };\n}\n\nexport function formatLineScoreCell(value: number | \"X\" | null | undefined): string {\n return value === null || value === undefined ? \"\" : String(value);\n}\n\n/* ---------------------------------------------- Matrix table ----------------------------------- */\n\nexport type MatrixCell = Readonly<{ label: string; tone?: DataTableCellTone | null; heat?: DataTableCellHeat | null }>;\n\n/**\n * Square cross table (team × team head-to-head). Rows and columns share one id list; the diagonal is\n * a self cell and renders blank. `getCell(row, column)` returns null for no record.\n */\nexport function resolveMatrix<Key extends string>(ids: readonly Key[], getCell: (row: Key, column: Key) => MatrixCell | null): readonly (readonly (MatrixCell | \"self\" | null)[])[] {\n if (new Set(ids).size !== ids.length) throw new TypeError(\"Matrix ids must be unique\");\n return ids.map((row) => ids.map((column) => (row === column ? \"self\" : getCell(row, column))));\n}\n\n/* ---------------------------------------------- Comparison table ------------------------------- */\n\nexport type ComparisonRow = Readonly<{\n id: string;\n label: string;\n values: readonly [number | null, number | null];\n /** Display strings; defaults to String(value). */\n display?: readonly [string, string];\n lowerIsBetter?: boolean;\n}>;\n\n/** Which side wins a row (null on ties/missing), and each side's bar share of the pair (0–1). */\nexport function resolveComparisonRow(row: ComparisonRow): Readonly<{ winner: 0 | 1 | null; shares: readonly [number, number] }> {\n const [a, b] = row.values;\n if (a === null || b === null || !Number.isFinite(a) || !Number.isFinite(b)) return { winner: null, shares: [a === null ? 0 : 0.5, b === null ? 0 : 0.5] };\n const winner = a === b ? null : (row.lowerIsBetter ? a < b : a > b) ? 0 : 1;\n // Lower-is-better rows invert the magnitudes so the better side still draws the longer bar.\n const left = Math.abs(row.lowerIsBetter ? b : a), right = Math.abs(row.lowerIsBetter ? a : b), sum = left + right;\n return { winner, shares: sum === 0 ? [0.5, 0.5] : [left / sum, right / sum] };\n}\n\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export type WorkflowField = Readonly<{
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
}>;
|
|
6
|
+
export type ImportMapping = Readonly<Record<string, string | null>>;
|
|
7
|
+
export declare function resolveImportMapping(sources: readonly WorkflowField[], targets: readonly WorkflowField[], mapping: ImportMapping): {
|
|
8
|
+
valid: boolean;
|
|
9
|
+
duplicateTargets: string[];
|
|
10
|
+
missingTargets: string[];
|
|
11
|
+
skipKey: string;
|
|
12
|
+
mapping: ImportMapping;
|
|
13
|
+
};
|
|
14
|
+
export type FunnelStage = Readonly<{
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
count: number;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function resolveFunnel(stages: readonly FunnelStage[]): {
|
|
21
|
+
conversion: number | null;
|
|
22
|
+
previousConversion: number | null;
|
|
23
|
+
lost: number | null;
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
count: number;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}[];
|
|
29
|
+
export type Cohort = Readonly<{
|
|
30
|
+
id: string;
|
|
31
|
+
label: string;
|
|
32
|
+
size: number;
|
|
33
|
+
retained: readonly (number | null)[];
|
|
34
|
+
}>;
|
|
35
|
+
export declare function resolveCohorts(rows: readonly Cohort[], periodCount: number): {
|
|
36
|
+
retained: (number | null)[];
|
|
37
|
+
rates: (number | null)[];
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
size: number;
|
|
41
|
+
}[];
|
|
42
|
+
export type CorrelationData = Readonly<{
|
|
43
|
+
variables: readonly WorkflowField[];
|
|
44
|
+
values: readonly (readonly (number | null)[])[];
|
|
45
|
+
}>;
|
|
46
|
+
export declare function validateCorrelation({ variables, values }: CorrelationData): void;
|
|
47
|
+
export declare function resolveTarget(actual: number, target: number): {
|
|
48
|
+
actual: number;
|
|
49
|
+
target: number;
|
|
50
|
+
difference: number;
|
|
51
|
+
ratio: number | null;
|
|
52
|
+
progress: number | null;
|
|
53
|
+
};
|
|
54
|
+
export type HistogramBin = Readonly<{
|
|
55
|
+
id: string;
|
|
56
|
+
lower: number;
|
|
57
|
+
upper: number;
|
|
58
|
+
includesUpper: boolean;
|
|
59
|
+
count: number;
|
|
60
|
+
}>;
|
|
61
|
+
export declare function binHistogram(samples: readonly (number | null)[], binCount: number, domain?: readonly [number, number]): {
|
|
62
|
+
bins: readonly HistogramBin[];
|
|
63
|
+
domain: readonly [number, number];
|
|
64
|
+
underflow: number;
|
|
65
|
+
overflow: number;
|
|
66
|
+
observed: number;
|
|
67
|
+
missing: number;
|
|
68
|
+
maxCount: number;
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=data-workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-workflows.d.ts","sourceRoot":"","sources":["../src/data-workflows.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;AACpE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,OAAO,EAAE,aAAa;;;;;aAiBf,aAAa;EAC9H;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAClG,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE;;;;QADnB,MAAM;WAAS,MAAM;WAAS,MAAM;aAAW,MAAM;IAY7F;AAED,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;CAAE,CAAC,CAAC;AACjH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM;;;QADvC,MAAM;WAAS,MAAM;UAAQ,MAAM;IAYtE;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAA;CAAE,CAAC,CAAC;AACjI,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,eAAe,GAAG,IAAI,CAUhF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;EAK3D;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACzH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;UAiB7F,SAAS,YAAY,EAAE;;;;;;;EAE/C"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { assertDistinctIds } from "./collection-editing.js";
|
|
2
|
+
export function resolveImportMapping(sources, targets, mapping) {
|
|
3
|
+
assertDistinctIds(sources);
|
|
4
|
+
assertDistinctIds(targets);
|
|
5
|
+
const sourceIds = new Set(sources.map(field => field.id)), targetIds = new Set(targets.map(field => field.id));
|
|
6
|
+
if (Object.keys(mapping).some(id => !sourceIds.has(id)))
|
|
7
|
+
throw new TypeError("Mapping references an unknown source");
|
|
8
|
+
const assigned = new Map();
|
|
9
|
+
for (const source of sources) {
|
|
10
|
+
const target = mapping[source.id] ?? null;
|
|
11
|
+
if (target === null)
|
|
12
|
+
continue;
|
|
13
|
+
if (!targetIds.has(target))
|
|
14
|
+
throw new TypeError("Mapping references an unknown target");
|
|
15
|
+
assigned.set(target, [...(assigned.get(target) ?? []), source.id]);
|
|
16
|
+
}
|
|
17
|
+
const duplicateTargets = [...assigned].filter(([, ids]) => ids.length > 1).map(([id]) => id);
|
|
18
|
+
const missingTargets = targets.filter(field => field.required && !assigned.has(field.id)).map(field => field.id);
|
|
19
|
+
let skipKey = "__hjm_skip__";
|
|
20
|
+
while (targetIds.has(skipKey))
|
|
21
|
+
skipKey += "_";
|
|
22
|
+
return { valid: assigned.size > 0 && !duplicateTargets.length && !missingTargets.length, duplicateTargets, missingTargets, skipKey,
|
|
23
|
+
// Snapshot source ids too: a pending import must not acquire new columns or edits.
|
|
24
|
+
mapping: Object.freeze(Object.fromEntries(sources.map(source => [source.id, mapping[source.id] ?? null]))) };
|
|
25
|
+
}
|
|
26
|
+
export function resolveFunnel(stages) {
|
|
27
|
+
assertDistinctIds(stages);
|
|
28
|
+
return stages.map((stage, index) => {
|
|
29
|
+
const previous = stages[index - 1]?.count, first = stages[0].count;
|
|
30
|
+
if (!Number.isSafeInteger(stage.count) || stage.count < 0 || (previous !== undefined && stage.count > previous)) {
|
|
31
|
+
throw new RangeError("Sequential funnel counts must be nonnegative integers that never increase");
|
|
32
|
+
}
|
|
33
|
+
return { ...stage, conversion: first === 0 ? null : stage.count / first,
|
|
34
|
+
previousConversion: previous === undefined || previous === 0 ? null : stage.count / previous,
|
|
35
|
+
lost: previous === undefined ? null : previous - stage.count };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function resolveCohorts(rows, periodCount) {
|
|
39
|
+
assertDistinctIds(rows);
|
|
40
|
+
if (!Number.isSafeInteger(periodCount) || periodCount < 1)
|
|
41
|
+
throw new RangeError("Cohorts require a positive period count");
|
|
42
|
+
return rows.map(row => {
|
|
43
|
+
if (!Number.isSafeInteger(row.size) || row.size < 0 || row.retained.length > periodCount)
|
|
44
|
+
throw new RangeError("Invalid cohort denominator or period count");
|
|
45
|
+
const retained = Array.from({ length: periodCount }, (_, index) => row.retained[index] ?? null);
|
|
46
|
+
if (retained.some(value => value !== null && (!Number.isSafeInteger(value) || value < 0 || value > row.size))) {
|
|
47
|
+
throw new RangeError("Retained count must be observed, nonnegative and no larger than cohort size");
|
|
48
|
+
}
|
|
49
|
+
return { ...row, retained, rates: retained.map(count => count === null || row.size === 0 ? null : count / row.size) };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function validateCorrelation({ variables, values }) {
|
|
53
|
+
assertDistinctIds(variables);
|
|
54
|
+
if (values.length !== variables.length || values.some(row => row.length !== variables.length))
|
|
55
|
+
throw new RangeError("Correlation matrix must be square");
|
|
56
|
+
for (let row = 0; row < values.length; row++)
|
|
57
|
+
for (let column = 0; column < values.length; column++) {
|
|
58
|
+
const value = values[row][column];
|
|
59
|
+
if (value !== null && (!Number.isFinite(value) || value < -1 || value > 1))
|
|
60
|
+
throw new RangeError("Correlation must be null or between -1 and 1");
|
|
61
|
+
const mirror = values[column][row];
|
|
62
|
+
if ((value === null) !== (mirror === null) || (value !== null && mirror !== null && Math.abs(value - mirror) > 1e-10))
|
|
63
|
+
throw new RangeError("Correlation matrix must be symmetric");
|
|
64
|
+
if (row === column && value !== null && value !== 1)
|
|
65
|
+
throw new RangeError("Observed self correlation must be one");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function resolveTarget(actual, target) {
|
|
69
|
+
if (![actual, target].every(value => Number.isFinite(value) && value >= 0))
|
|
70
|
+
throw new RangeError("Actual and target must be finite and nonnegative");
|
|
71
|
+
// A zero target has no percentage denominator. Never manufacture 100% completion.
|
|
72
|
+
return { actual, target, difference: actual - target, ratio: target === 0 ? null : actual / target,
|
|
73
|
+
progress: target === 0 ? null : Math.min(1, actual / target) };
|
|
74
|
+
}
|
|
75
|
+
export function binHistogram(samples, binCount, domain) {
|
|
76
|
+
if (!Number.isSafeInteger(binCount) || binCount < 1 || binCount > 100)
|
|
77
|
+
throw new RangeError("Histogram bin count must be between 1 and 100");
|
|
78
|
+
const observed = samples.filter((value) => value !== null);
|
|
79
|
+
if (observed.some(value => !Number.isFinite(value)))
|
|
80
|
+
throw new RangeError("Histogram samples must be finite or null");
|
|
81
|
+
let lower = domain?.[0] ?? (observed.length ? observed.reduce((low, value) => Math.min(low, value), Infinity) : 0);
|
|
82
|
+
let upper = domain?.[1] ?? (observed.length ? observed.reduce((high, value) => Math.max(high, value), -Infinity) : 1);
|
|
83
|
+
if (!domain && upper === lower) {
|
|
84
|
+
const padding = Math.max(Math.abs(lower) * .01, .5);
|
|
85
|
+
lower -= padding;
|
|
86
|
+
upper += padding;
|
|
87
|
+
}
|
|
88
|
+
if (![lower, upper].every(Number.isFinite) || upper <= lower || !Number.isFinite(upper - lower))
|
|
89
|
+
throw new RangeError("Histogram domain must have a positive finite span");
|
|
90
|
+
const width = (upper - lower) / binCount;
|
|
91
|
+
if (width <= 0)
|
|
92
|
+
throw new RangeError("Histogram bins are below numeric precision");
|
|
93
|
+
const bins = Array.from({ length: binCount }, (_, index) => ({ id: `bin-${index}`, lower: lower + index * width, upper: lower + (index + 1) * width, includesUpper: index === binCount - 1, count: 0 }));
|
|
94
|
+
let underflow = 0, overflow = 0;
|
|
95
|
+
for (const value of observed) {
|
|
96
|
+
if (value < lower) {
|
|
97
|
+
underflow++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (value > upper) {
|
|
101
|
+
overflow++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
bins[Math.min(binCount - 1, Math.floor((value - lower) / width))].count++;
|
|
105
|
+
}
|
|
106
|
+
return { bins: bins, domain: [lower, upper], underflow, overflow,
|
|
107
|
+
observed: observed.length, missing: samples.length - observed.length, maxCount: Math.max(1, ...bins.map(bin => bin.count)) };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=data-workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-workflows.js","sourceRoot":"","sources":["../src/data-workflows.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertDistinctIds } from \"./collection-editing.js\";\n\nexport type WorkflowField = Readonly<{ id: string; label: string; required?: boolean }>;\nexport type ImportMapping = Readonly<Record<string, string | null>>;\nexport function resolveImportMapping(sources: readonly WorkflowField[], targets: readonly WorkflowField[], mapping: ImportMapping) {\n assertDistinctIds(sources); assertDistinctIds(targets);\n const sourceIds = new Set(sources.map(field => field.id)), targetIds = new Set(targets.map(field => field.id));\n if (Object.keys(mapping).some(id => !sourceIds.has(id))) throw new TypeError(\"Mapping references an unknown source\");\n const assigned = new Map<string, string[]>();\n for (const source of sources) {\n const target = mapping[source.id] ?? null;\n if (target === null) continue;\n if (!targetIds.has(target)) throw new TypeError(\"Mapping references an unknown target\");\n assigned.set(target, [...(assigned.get(target) ?? []), source.id]);\n }\n const duplicateTargets = [...assigned].filter(([, ids]) => ids.length > 1).map(([id]) => id);\n const missingTargets = targets.filter(field => field.required && !assigned.has(field.id)).map(field => field.id);\n let skipKey = \"__hjm_skip__\";\n while (targetIds.has(skipKey)) skipKey += \"_\";\n return { valid: assigned.size > 0 && !duplicateTargets.length && !missingTargets.length, duplicateTargets, missingTargets, skipKey,\n // Snapshot source ids too: a pending import must not acquire new columns or edits.\n mapping: Object.freeze(Object.fromEntries(sources.map(source => [source.id, mapping[source.id] ?? null]))) as ImportMapping };\n}\n\nexport type FunnelStage = Readonly<{ id: string; label: string; count: number; reason?: string }>;\nexport function resolveFunnel(stages: readonly FunnelStage[]) {\n assertDistinctIds(stages);\n return stages.map((stage, index) => {\n const previous = stages[index - 1]?.count, first = stages[0]!.count;\n if (!Number.isSafeInteger(stage.count) || stage.count < 0 || (previous !== undefined && stage.count > previous)) {\n throw new RangeError(\"Sequential funnel counts must be nonnegative integers that never increase\");\n }\n return { ...stage, conversion: first === 0 ? null : stage.count / first,\n previousConversion: previous === undefined || previous === 0 ? null : stage.count / previous,\n lost: previous === undefined ? null : previous - stage.count };\n });\n}\n\nexport type Cohort = Readonly<{ id: string; label: string; size: number; retained: readonly (number | null)[] }>;\nexport function resolveCohorts(rows: readonly Cohort[], periodCount: number) {\n assertDistinctIds(rows);\n if (!Number.isSafeInteger(periodCount) || periodCount < 1) throw new RangeError(\"Cohorts require a positive period count\");\n return rows.map(row => {\n if (!Number.isSafeInteger(row.size) || row.size < 0 || row.retained.length > periodCount) throw new RangeError(\"Invalid cohort denominator or period count\");\n const retained = Array.from({ length: periodCount }, (_, index) => row.retained[index] ?? null);\n if (retained.some(value => value !== null && (!Number.isSafeInteger(value) || value < 0 || value > row.size))) {\n throw new RangeError(\"Retained count must be observed, nonnegative and no larger than cohort size\");\n }\n return { ...row, retained, rates: retained.map(count => count === null || row.size === 0 ? null : count / row.size) };\n });\n}\n\nexport type CorrelationData = Readonly<{ variables: readonly WorkflowField[]; values: readonly (readonly (number | null)[])[] }>;\nexport function validateCorrelation({ variables, values }: CorrelationData): void {\n assertDistinctIds(variables);\n if (values.length !== variables.length || values.some(row => row.length !== variables.length)) throw new RangeError(\"Correlation matrix must be square\");\n for (let row = 0; row < values.length; row++) for (let column = 0; column < values.length; column++) {\n const value = values[row]![column]!;\n if (value !== null && (!Number.isFinite(value) || value < -1 || value > 1)) throw new RangeError(\"Correlation must be null or between -1 and 1\");\n const mirror = values[column]![row]!;\n if ((value === null) !== (mirror === null) || (value !== null && mirror !== null && Math.abs(value - mirror) > 1e-10)) throw new RangeError(\"Correlation matrix must be symmetric\");\n if (row === column && value !== null && value !== 1) throw new RangeError(\"Observed self correlation must be one\");\n }\n}\n\nexport function resolveTarget(actual: number, target: number) {\n if (![actual, target].every(value => Number.isFinite(value) && value >= 0)) throw new RangeError(\"Actual and target must be finite and nonnegative\");\n // A zero target has no percentage denominator. Never manufacture 100% completion.\n return { actual, target, difference: actual - target, ratio: target === 0 ? null : actual / target,\n progress: target === 0 ? null : Math.min(1, actual / target) };\n}\n\nexport type HistogramBin = Readonly<{ id: string; lower: number; upper: number; includesUpper: boolean; count: number }>;\nexport function binHistogram(samples: readonly (number | null)[], binCount: number, domain?: readonly [number, number]) {\n if (!Number.isSafeInteger(binCount) || binCount < 1 || binCount > 100) throw new RangeError(\"Histogram bin count must be between 1 and 100\");\n const observed = samples.filter((value): value is number => value !== null);\n if (observed.some(value => !Number.isFinite(value))) throw new RangeError(\"Histogram samples must be finite or null\");\n let lower = domain?.[0] ?? (observed.length ? observed.reduce((low, value) => Math.min(low, value), Infinity) : 0);\n let upper = domain?.[1] ?? (observed.length ? observed.reduce((high, value) => Math.max(high, value), -Infinity) : 1);\n if (!domain && upper === lower) { const padding = Math.max(Math.abs(lower) * .01, .5); lower -= padding; upper += padding; }\n if (![lower, upper].every(Number.isFinite) || upper <= lower || !Number.isFinite(upper - lower)) throw new RangeError(\"Histogram domain must have a positive finite span\");\n const width = (upper - lower) / binCount;\n if (width <= 0) throw new RangeError(\"Histogram bins are below numeric precision\");\n const bins = Array.from({ length: binCount }, (_, index) => ({ id: `bin-${index}`, lower: lower + index * width, upper: lower + (index + 1) * width, includesUpper: index === binCount - 1, count: 0 }));\n let underflow = 0, overflow = 0;\n for (const value of observed) {\n if (value < lower) { underflow++; continue; }\n if (value > upper) { overflow++; continue; }\n bins[Math.min(binCount - 1, Math.floor((value - lower) / width))]!.count++;\n }\n return { bins: bins as readonly HistogramBin[], domain: [lower, upper] as const, underflow, overflow,\n observed: observed.length, missing: samples.length - observed.length, maxCount: Math.max(1, ...bins.map(bin => bin.count)) };\n}\n"]}
|
package/dist/dataviz.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { ResolvedTheme } from "./colors.js";
|
|
2
2
|
/**
|
|
3
|
-
* 차트의
|
|
3
|
+
* 차트의 **색·축·격자·범례 토큰 계약**이다. 그리기는 `src/chart.ts`와 두 renderer가 맡는다.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* 2026-10-14 번복: 처음에는 렌더러를 만들지 않고 그리기를 제품 라이브러리(Recharts·
|
|
6
|
+
* victory-native·d3)에 맡겼다. 실제로는 제품(Yajalal)이 같은 스크럽 선 차트·막대·정렬 표를
|
|
7
|
+
* 직접 다시 만들었고, 사용자가 차트·표 라이브러리 흡수를 요청해 그리기를 HJM으로 옮겼다.
|
|
8
|
+
* 색이 제각각이던 문제(Spint 열지도·Yajalal 통계)를 막는 이 토큰의 값과 규칙은 그대로이며,
|
|
9
|
+
* HJM 차트와 계약 밖 종류를 그리는 제품 라이브러리가 함께 소비한다. 이전 결정과 번복의
|
|
10
|
+
* 근거는 docs/chart.md다.
|
|
10
11
|
*
|
|
11
12
|
* 상태색(info/success/warning/attention)을 계열색으로 쓰지 않는다. 상태색은 뜻을 가진
|
|
12
13
|
* 색이라 "3번 계열"에 쓰면 사용자가 경고로 읽는다.
|
package/dist/dataviz.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataviz.d.ts","sourceRoot":"","sources":["../src/dataviz.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD
|
|
1
|
+
{"version":3,"file":"dataviz.d.ts","sourceRoot":"","sources":["../src/dataviz.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB;;;CAGsC,CAAC;AAExE,eAAO,MAAM,kBAAkB,GAAoC,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMrF;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwB9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,yBAAyB,oDAAqD,CAAC;AAC5F,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,wBAAwB,EAAE,GAC7C,IAAI,CAMN"}
|
package/dist/dataviz.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { themeColor } from "./color-references.js";
|
|
2
2
|
import { stroke, typography } from "./foundations.js";
|
|
3
3
|
/**
|
|
4
|
-
* 차트의
|
|
4
|
+
* 차트의 **색·축·격자·범례 토큰 계약**이다. 그리기는 `src/chart.ts`와 두 renderer가 맡는다.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* 2026-10-14 번복: 처음에는 렌더러를 만들지 않고 그리기를 제품 라이브러리(Recharts·
|
|
7
|
+
* victory-native·d3)에 맡겼다. 실제로는 제품(Yajalal)이 같은 스크럽 선 차트·막대·정렬 표를
|
|
8
|
+
* 직접 다시 만들었고, 사용자가 차트·표 라이브러리 흡수를 요청해 그리기를 HJM으로 옮겼다.
|
|
9
|
+
* 색이 제각각이던 문제(Spint 열지도·Yajalal 통계)를 막는 이 토큰의 값과 규칙은 그대로이며,
|
|
10
|
+
* HJM 차트와 계약 밖 종류를 그리는 제품 라이브러리가 함께 소비한다. 이전 결정과 번복의
|
|
11
|
+
* 근거는 docs/chart.md다.
|
|
11
12
|
*
|
|
12
13
|
* 상태색(info/success/warning/attention)을 계열색으로 쓰지 않는다. 상태색은 뜻을 가진
|
|
13
14
|
* 색이라 "3번 계열"에 쓰면 사용자가 경고로 읽는다.
|
package/dist/dataviz.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataviz.js","sourceRoot":"","sources":["../src/dataviz.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEtD
|
|
1
|
+
{"version":3,"file":"dataviz.js","sourceRoot":"","sources":["../src/dataviz.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;IAC/F,IAAI,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;CACzB,CAAC;AAExE,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC;AAEpE;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAoB,EAAE,KAAa;IAC3E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,UAAU,CAAC,qDAAqD,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAE,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC;IAC9B,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC;IAClC,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC;IACnC,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC;IACnC,cAAc,EAAE,UAAU,CAAC,SAAS,CAAC;IACrC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC;IACnC,cAAc,EAAE,UAAU,CAAC,UAAU,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC,OAAO;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM;IACxB,YAAY,EAAE,SAAS;IACvB,aAAa,EAAE,OAAO;CAatB,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,aAAa,EAAE,SAAS,EAAE,aAAa,CAAU,CAAC;AAG5F,MAAM,UAAU,uBAAuB,CACrC,SAA8C;IAE9C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,UAAU,CAClB,8FAA8F,CAC/F,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import type { ResolvedTheme } from \"./colors.js\";\nimport { themeColor, type ColorReference } from \"./color-references.js\";\nimport { stroke, typography } from \"./foundations.js\";\n\n/**\n * 차트의 **색·축·격자·범례 토큰 계약**이다. 그리기는 `src/chart.ts`와 두 renderer가 맡는다.\n *\n * 2026-10-14 번복: 처음에는 렌더러를 만들지 않고 그리기를 제품 라이브러리(Recharts·\n * victory-native·d3)에 맡겼다. 실제로는 제품(Yajalal)이 같은 스크럽 선 차트·막대·정렬 표를\n * 직접 다시 만들었고, 사용자가 차트·표 라이브러리 흡수를 요청해 그리기를 HJM으로 옮겼다.\n * 색이 제각각이던 문제(Spint 열지도·Yajalal 통계)를 막는 이 토큰의 값과 규칙은 그대로이며,\n * HJM 차트와 계약 밖 종류를 그리는 제품 라이브러리가 함께 소비한다. 이전 결정과 번복의\n * 근거는 docs/chart.md다.\n *\n * 상태색(info/success/warning/attention)을 계열색으로 쓰지 않는다. 상태색은 뜻을 가진\n * 색이라 \"3번 계열\"에 쓰면 사용자가 경고로 읽는다.\n */\nexport const datavizSeriesPalette = {\n light: [\"#2563eb\", \"#db2777\", \"#0d9488\", \"#b45309\", \"#7c3aed\", \"#0369a1\", \"#9d174d\", \"#4d7c0f\"],\n dark: [\"#60a5fa\", \"#f472b6\", \"#2dd4bf\", \"#fbbf24\", \"#a78bfa\", \"#38bdf8\", \"#fb7185\", \"#a3e635\"],\n} as const satisfies Readonly<Record<ResolvedTheme, readonly string[]>>;\n\nexport const datavizSeriesCount = datavizSeriesPalette.light.length;\n\n/**\n * 계열 수가 팔레트보다 많으면 **순환**한다. 색을 자동으로 더 만들지 않는다 — 생성한 색은\n * 대비를 보장할 수 없고, 9번째 계열이 필요한 화면은 대개 차트가 아니라 표가 맞다.\n */\nexport function resolveDatavizSeriesColor(theme: ResolvedTheme, index: number): string {\n if (!Number.isInteger(index) || index < 0) {\n throw new RangeError(\"Dataviz series index must be a non-negative integer\");\n }\n const palette = datavizSeriesPalette[theme];\n return palette[index % palette.length]!;\n}\n\n/**\n * 축·격자·범례는 차트가 아니라 **화면의 부속**이라 본문 토큰을 그대로 쓴다. 차트 전용\n * 회색을 따로 두면 같은 화면의 표·캡션과 어긋난다.\n */\nexport const datavizChromeTokens = {\n axisLine: themeColor(\"border\"),\n axisLabel: themeColor(\"textMuted\"),\n gridLine: themeColor(\"border\", 0.6),\n legendLabel: themeColor(\"textBody\"),\n tooltipSurface: themeColor(\"surface\"),\n tooltipBorder: themeColor(\"border\"),\n tooltipContent: themeColor(\"textBody\"),\n lineWidth: stroke.default,\n gridWidth: stroke.subtle,\n labelVariant: \"caption\",\n legendVariant: \"label\",\n} as const satisfies Readonly<{\n axisLine: ColorReference;\n axisLabel: ColorReference;\n gridLine: ColorReference;\n legendLabel: ColorReference;\n tooltipSurface: ColorReference;\n tooltipBorder: ColorReference;\n tooltipContent: ColorReference;\n lineWidth: number;\n gridWidth: number;\n labelVariant: keyof typeof typography;\n legendVariant: keyof typeof typography;\n}>;\n\n/**\n * 색만으로 계열을 구분하지 않는다(WCAG 1.4.1). 제품이 쓰는 라이브러리가 무엇이든 이\n * 셋 중 하나를 색과 **함께** 붙여야 한다 — 색각 이상과 흑백 인쇄에서 차트가 남는 조건이다.\n */\nexport const datavizRedundantEncodings = [\"directLabel\", \"pattern\", \"markerShape\"] as const;\nexport type DatavizRedundantEncoding = (typeof datavizRedundantEncodings)[number];\n\nexport function validateDatavizEncoding(\n encodings: readonly DatavizRedundantEncoding[],\n): void {\n if (encodings.length === 0) {\n throw new RangeError(\n \"A dataviz series needs at least one non-color encoding: directLabel, pattern, or markerShape\",\n );\n }\n}\n"]}
|
|
@@ -28,4 +28,11 @@ export declare function resolveGooeyIndicator(from: GooeyIndicatorRect, to: Gooe
|
|
|
28
28
|
readonly x: readonly [number, number, number];
|
|
29
29
|
readonly width: readonly [number, number, number];
|
|
30
30
|
};
|
|
31
|
+
/** A pager publishes fractional item indexes without rendering React each frame.
|
|
32
|
+
* Selection and panel activation stay owned by value/onValueChange at settle. */
|
|
33
|
+
export type TabSwipePosition = Readonly<{
|
|
34
|
+
get: () => number;
|
|
35
|
+
subscribe: (listener: (position: number) => void) => () => void;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function resolveSwipeTabIndicator(rects: readonly (GooeyIndicatorRect | undefined)[], position: number): GooeyIndicatorRect | null;
|
|
31
38
|
//# sourceMappingURL=gooey-navigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gooey-navigation.d.ts","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAExE;oFACoF;AACpF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,eAAe,GAAE,MAAM,GAAG,OAAgB,EAC1C,WAAW,GAAE,YAAY,GAAG,UAAyB,GACpD,cAAc,CAGhB;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO;;;;;;;;;;EAOlH;AAED;kFACkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAUvH;AAKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB;;;;;EAMrF"}
|
|
1
|
+
{"version":3,"file":"gooey-navigation.d.ts","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAExE;oFACoF;AACpF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,eAAe,GAAE,MAAM,GAAG,OAAgB,EAC1C,WAAW,GAAE,YAAY,GAAG,UAAyB,GACpD,cAAc,CAGhB;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO;;;;;;;;;;EAOlH;AAED;kFACkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAUvH;AAKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB;;;;;EAMrF;AAED;iFACiF;AACjF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CACjE,CAAC,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,CAAC,kBAAkB,GAAG,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAOxI"}
|
package/dist/gooey-navigation.js
CHANGED
|
@@ -43,4 +43,14 @@ export function resolveGooeyIndicator(from, to) {
|
|
|
43
43
|
const right = Math.max(from.x + from.width, to.x + to.width);
|
|
44
44
|
return { duration: 320, input: [0, 0.45, 1], x: [from.x, left, to.x], width: [from.width, right - left, to.width] };
|
|
45
45
|
}
|
|
46
|
+
export function resolveSwipeTabIndicator(rects, position) {
|
|
47
|
+
if (!Number.isFinite(position) || rects.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
const clamped = Math.max(0, Math.min(rects.length - 1, position));
|
|
50
|
+
const index = Math.floor(clamped), fraction = clamped - index;
|
|
51
|
+
const from = rects[index], to = rects[Math.min(index + 1, rects.length - 1)];
|
|
52
|
+
if (!from || !to)
|
|
53
|
+
return null;
|
|
54
|
+
return { x: from.x + (to.x - from.x) * fraction, width: from.width + (to.width - from.width) * fraction };
|
|
55
|
+
}
|
|
46
56
|
//# sourceMappingURL=gooey-navigation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gooey-navigation.js","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { motion } from \"./foundations.js\";\n\nexport type TabsAppearance = \"standard\" | \"slide\" | \"gooey\";\nexport type GooeyIndicatorRect = Readonly<{ x: number; width: number }>;\n\n/** A product's explicit appearance wins; vertical tabs keep the canonical line.\n * Plain profile sliding is not an implicit request for elastic gooey stretching. */\nexport function resolveTabsAppearance(\n appearance: TabsAppearance | undefined,\n selectionMotion: \"none\" | \"slide\" = \"none\",\n orientation: \"horizontal\" | \"vertical\" = \"horizontal\",\n): TabsAppearance {\n if (appearance !== undefined && ![\"standard\", \"slide\", \"gooey\"].includes(appearance)) throw new TypeError(\"Unsupported tabs appearance\");\n return orientation === \"vertical\" ? \"standard\" : appearance ?? (selectionMotion === \"slide\" ? \"slide\" : \"standard\");\n}\n\n/** Both renderers interpolate measured bounds without changing selection or panels. */\nexport function resolveTabIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect, appearance: \"slide\" | \"gooey\") {\n if (appearance === \"gooey\") return resolveGooeyIndicator(from, to);\n if (appearance !== \"slide\") throw new TypeError(\"Unsupported moving tab indicator\");\n validateRects(from, to);\n // Ordinary selection feedback uses the shared normal duration; retain the\n // existing 320ms elastic recipe for explicit gooey consumers.\n return { duration: motion.normal, input: [0, 1], x: [from.x, to.x], width: [from.width, to.width] } as const;\n}\n\n/** Capture the current JS-driver frame before cancellation so rapid input starts\n * where the indicator is visible, rather than jumping to the abandoned target. */\nexport function sampleTabIndicator(recipe: ReturnType<typeof resolveTabIndicator>, progress: number): GooeyIndicatorRect {\n const value = Number.isFinite(progress) ? Math.max(0, Math.min(1, progress)) : 1;\n const index = recipe.input.findIndex((offset, i) => i > 0 && offset >= value);\n const end = index < 0 ? recipe.input.length - 1 : index;\n const start = Math.max(0, end - 1);\n const fraction = (value - recipe.input[start]!) / (recipe.input[end]! - recipe.input[start]!);\n return {\n x: recipe.x[start]! + (recipe.x[end]! - recipe.x[start]!) * fraction,\n width: recipe.width[start]! + (recipe.width[end]! - recipe.width[start]!) * fraction,\n };\n}\n\nfunction validateRects(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n for (const rect of [from, to]) if (!Number.isFinite(rect.x) || !Number.isFinite(rect.width) || rect.width <= 0) throw new RangeError(\"Indicator needs finite coordinates and positive widths\");\n}\n/** Stretch only the selected indicator; tab hit targets and text never move. */\nexport function resolveGooeyIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n validateRects(from, to);\n // Keep the elastic bridge within the measured source/destination span, including RTL layouts.\n const left = Math.min(from.x, to.x);\n const right = Math.max(from.x + from.width, to.x + to.width);\n return { duration: 320, input: [0, 0.45, 1], x: [from.x, left, to.x], width: [from.width, right - left, to.width] } as const;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gooey-navigation.js","sourceRoot":"","sources":["../src/gooey-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { motion } from \"./foundations.js\";\n\nexport type TabsAppearance = \"standard\" | \"slide\" | \"gooey\";\nexport type GooeyIndicatorRect = Readonly<{ x: number; width: number }>;\n\n/** A product's explicit appearance wins; vertical tabs keep the canonical line.\n * Plain profile sliding is not an implicit request for elastic gooey stretching. */\nexport function resolveTabsAppearance(\n appearance: TabsAppearance | undefined,\n selectionMotion: \"none\" | \"slide\" = \"none\",\n orientation: \"horizontal\" | \"vertical\" = \"horizontal\",\n): TabsAppearance {\n if (appearance !== undefined && ![\"standard\", \"slide\", \"gooey\"].includes(appearance)) throw new TypeError(\"Unsupported tabs appearance\");\n return orientation === \"vertical\" ? \"standard\" : appearance ?? (selectionMotion === \"slide\" ? \"slide\" : \"standard\");\n}\n\n/** Both renderers interpolate measured bounds without changing selection or panels. */\nexport function resolveTabIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect, appearance: \"slide\" | \"gooey\") {\n if (appearance === \"gooey\") return resolveGooeyIndicator(from, to);\n if (appearance !== \"slide\") throw new TypeError(\"Unsupported moving tab indicator\");\n validateRects(from, to);\n // Ordinary selection feedback uses the shared normal duration; retain the\n // existing 320ms elastic recipe for explicit gooey consumers.\n return { duration: motion.normal, input: [0, 1], x: [from.x, to.x], width: [from.width, to.width] } as const;\n}\n\n/** Capture the current JS-driver frame before cancellation so rapid input starts\n * where the indicator is visible, rather than jumping to the abandoned target. */\nexport function sampleTabIndicator(recipe: ReturnType<typeof resolveTabIndicator>, progress: number): GooeyIndicatorRect {\n const value = Number.isFinite(progress) ? Math.max(0, Math.min(1, progress)) : 1;\n const index = recipe.input.findIndex((offset, i) => i > 0 && offset >= value);\n const end = index < 0 ? recipe.input.length - 1 : index;\n const start = Math.max(0, end - 1);\n const fraction = (value - recipe.input[start]!) / (recipe.input[end]! - recipe.input[start]!);\n return {\n x: recipe.x[start]! + (recipe.x[end]! - recipe.x[start]!) * fraction,\n width: recipe.width[start]! + (recipe.width[end]! - recipe.width[start]!) * fraction,\n };\n}\n\nfunction validateRects(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n for (const rect of [from, to]) if (!Number.isFinite(rect.x) || !Number.isFinite(rect.width) || rect.width <= 0) throw new RangeError(\"Indicator needs finite coordinates and positive widths\");\n}\n/** Stretch only the selected indicator; tab hit targets and text never move. */\nexport function resolveGooeyIndicator(from: GooeyIndicatorRect, to: GooeyIndicatorRect) {\n validateRects(from, to);\n // Keep the elastic bridge within the measured source/destination span, including RTL layouts.\n const left = Math.min(from.x, to.x);\n const right = Math.max(from.x + from.width, to.x + to.width);\n return { duration: 320, input: [0, 0.45, 1], x: [from.x, left, to.x], width: [from.width, right - left, to.width] } as const;\n}\n\n/** A pager publishes fractional item indexes without rendering React each frame.\n * Selection and panel activation stay owned by value/onValueChange at settle. */\nexport type TabSwipePosition = Readonly<{\n get: () => number;\n subscribe: (listener: (position: number) => void) => () => void;\n}>;\n\nexport function resolveSwipeTabIndicator(rects: readonly (GooeyIndicatorRect | undefined)[], position: number): GooeyIndicatorRect | null {\n if (!Number.isFinite(position) || rects.length === 0) return null;\n const clamped = Math.max(0, Math.min(rects.length - 1, position));\n const index = Math.floor(clamped), fraction = clamped - index;\n const from = rects[index], to = rects[Math.min(index + 1, rects.length - 1)];\n if (!from || !to) return null;\n return { x: from.x + (to.x - from.x) * fraction, width: from.width + (to.width - from.width) * fraction };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { containerDefaults, containerRecipe, resolveContainerDescriptor, validat
|
|
|
3
3
|
export { visuallyHiddenRecipe, type VisuallyHiddenContract, } from "./visually-hidden.js";
|
|
4
4
|
export { getIconDirectionality, getIconTransform, resolveIconDescriptor, semanticIconNames, validateIconDescriptor, type DecorativeIconDescriptor, type IconDescriptor, type IconDirection, type IconDirectionality, type IconTransform, type InformativeIconDescriptor, type ResolvedIconDescriptor, type SemanticIconName, } from "./icon.js";
|
|
5
5
|
export { resolveLinkDescriptor, validateLinkDescriptor, validateLinkDestination, type ExternalLinkDestination, type InternalLinkDestination, type LinkDescriptor, type LinkDestination, type LinkIconDescriptor, type ResolvedLinkDescriptor, type ResolvedLinkIconDescriptor, } from "./link.js";
|
|
6
|
-
export { bottomNavigationBehaviorDefaults, resolveBottomNavigationActivation, resolveBottomNavigationConfiguration, resolveBottomNavigationDescriptor, resolveBottomNavigationItem, validateBottomNavigationConfiguration, validateBottomNavigationDescriptor, type BottomNavigationActivation, type BottomNavigationConfiguration, type BottomNavigationCounterBadge, type BottomNavigationDescriptor, type BottomNavigationDirection, type BottomNavigationIconDescriptor, type BottomNavigationItemDescriptor, type BottomNavigationKeyboardBehavior, type BottomNavigationLabels, type ResolvedBottomNavigationCounterBadge, type ResolvedBottomNavigationConfiguration, type ResolvedBottomNavigationDescriptor, type ResolvedBottomNavigationIconDescriptor, type ResolvedBottomNavigationItemDescriptor, } from "./bottom-navigation.js";
|
|
6
|
+
export { bottomNavigationBehaviorDefaults, resolveBottomNavigationActivation, resolveBottomNavigationConfiguration, resolveBottomNavigationDescriptor, resolveBottomNavigationItem, validateBottomNavigationConfiguration, validateBottomNavigationDescriptor, type BottomNavigationActivation, type BottomNavigationConfiguration, type BottomNavigationCounterBadge, type BottomNavigationDescriptor, type BottomNavigationDirection, type BottomNavigationIconDescriptor, type BottomNavigationItemDescriptor, type BottomNavigationKeyboardBehavior, type BottomNavigationLabels, type BottomNavigationSurface, type ResolvedBottomNavigationCounterBadge, type ResolvedBottomNavigationConfiguration, type ResolvedBottomNavigationDescriptor, type ResolvedBottomNavigationIconDescriptor, type ResolvedBottomNavigationItemDescriptor, } from "./bottom-navigation.js";
|
|
7
7
|
export { resolveTooltipDescriptor, tooltipBehaviorDefaults, tooltipDescriptorDefaults, validateTooltipDescriptor, validateTooltipOpenState, type ControlledTooltipOpenState, type ResolvedTooltipDescriptor, type TooltipAlign, type TooltipDescriptor, type TooltipOpenChangeDetails, type TooltipOpenChangeReason, type TooltipOpenState, type TooltipPlacement, type UncontrolledTooltipOpenState, } from "./tooltip.js";
|
|
8
8
|
export { alertDialogBehaviorDefaults, canDismissAlertDialog, createAlertDialogSession, getAlertDialogInitialFocus, validateAlertDialogRequest, type AlertDialogAsyncState, type AlertDialogCancelReason, type AlertDialogConfirmAction, type AlertDialogDismissReason, type AlertDialogInitialFocus, type AlertDialogMode, type AlertDialogOpenChangeReason, type AlertDialogOpenState, type AlertDialogPhase, type AlertDialogRequest, type AlertDialogResult, type AlertDialogSession, } from "./alert-dialog.js";
|
|
9
9
|
export { canDismissSheet, createSheetLifecycle, sheetBehaviorDefaults, type SheetDismissPolicy, type SheetDismissReason, type SheetLifecycle, type SheetOpenChangeDetails, type SheetOpenState, } from "./sheet.js";
|
|
@@ -34,6 +34,7 @@ export * from "./design-system-provider.js";
|
|
|
34
34
|
export * from "./floating-action-button.js";
|
|
35
35
|
export * from "./content-state.js";
|
|
36
36
|
export * from "./data-table.js";
|
|
37
|
+
export * from "./chart.js";
|
|
37
38
|
export * from "./mentions.js";
|
|
38
39
|
export * from "./transfer-list.js";
|
|
39
40
|
export * from "./tour.js";
|