@flanksource/clicky-ui 0.2.7 → 0.2.9
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/components/Combobox.cjs +89 -49
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts +3 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +91 -51
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/FilterBar.cjs +42 -44
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts +19 -0
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +43 -45
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/JsonSchemaForm.cjs +35 -0
- package/dist/components/JsonSchemaForm.cjs.map +1 -0
- package/dist/components/JsonSchemaForm.d.ts +3 -0
- package/dist/components/JsonSchemaForm.d.ts.map +1 -0
- package/dist/components/JsonSchemaForm.js +35 -0
- package/dist/components/JsonSchemaForm.js.map +1 -0
- package/dist/components/json-schema-form-array.cjs +169 -0
- package/dist/components/json-schema-form-array.cjs.map +1 -0
- package/dist/components/json-schema-form-array.d.ts +8 -0
- package/dist/components/json-schema-form-array.d.ts.map +1 -0
- package/dist/components/json-schema-form-array.js +169 -0
- package/dist/components/json-schema-form-array.js.map +1 -0
- package/dist/components/json-schema-form-fields.cjs +365 -0
- package/dist/components/json-schema-form-fields.cjs.map +1 -0
- package/dist/components/json-schema-form-fields.d.ts +19 -0
- package/dist/components/json-schema-form-fields.d.ts.map +1 -0
- package/dist/components/json-schema-form-fields.js +365 -0
- package/dist/components/json-schema-form-fields.js.map +1 -0
- package/dist/components/json-schema-form-render.cjs +154 -0
- package/dist/components/json-schema-form-render.cjs.map +1 -0
- package/dist/components/json-schema-form-render.d.ts +40 -0
- package/dist/components/json-schema-form-render.d.ts.map +1 -0
- package/dist/components/json-schema-form-render.js +154 -0
- package/dist/components/json-schema-form-render.js.map +1 -0
- package/dist/components/json-schema-form-resolve.cjs +121 -0
- package/dist/components/json-schema-form-resolve.cjs.map +1 -0
- package/dist/components/json-schema-form-resolve.d.ts +19 -0
- package/dist/components/json-schema-form-resolve.d.ts.map +1 -0
- package/dist/components/json-schema-form-resolve.js +121 -0
- package/dist/components/json-schema-form-resolve.js.map +1 -0
- package/dist/components/json-schema-form-types.d.ts +108 -0
- package/dist/components/json-schema-form-types.d.ts.map +1 -0
- package/dist/components.cjs +7 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +8 -1
- package/dist/components.js.map +1 -1
- package/dist/data/Clicky.cjs +1 -1
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +1 -1
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/DataTable.cjs +16 -15
- package/dist/data/DataTable.cjs.map +1 -1
- package/dist/data/DataTable.d.ts +2 -1
- package/dist/data/DataTable.d.ts.map +1 -1
- package/dist/data/DataTable.js +16 -15
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/Icon.cjs +11 -0
- package/dist/data/Icon.cjs.map +1 -1
- package/dist/data/Icon.d.ts +12 -1
- package/dist/data/Icon.d.ts.map +1 -1
- package/dist/data/Icon.js +12 -1
- package/dist/data/Icon.js.map +1 -1
- package/dist/data/LogDetails.cjs +277 -0
- package/dist/data/LogDetails.cjs.map +1 -0
- package/dist/data/LogDetails.d.ts +5 -0
- package/dist/data/LogDetails.d.ts.map +1 -0
- package/dist/data/LogDetails.js +277 -0
- package/dist/data/LogDetails.js.map +1 -0
- package/dist/data/LogsTable.cjs +6 -202
- package/dist/data/LogsTable.cjs.map +1 -1
- package/dist/data/LogsTable.d.ts.map +1 -1
- package/dist/data/LogsTable.js +4 -200
- package/dist/data/LogsTable.js.map +1 -1
- package/dist/data/MatrixTable.cjs +17 -9
- package/dist/data/MatrixTable.cjs.map +1 -1
- package/dist/data/MatrixTable.d.ts +3 -1
- package/dist/data/MatrixTable.d.ts.map +1 -1
- package/dist/data/MatrixTable.js +17 -9
- package/dist/data/MatrixTable.js.map +1 -1
- package/dist/data/Properties.cjs +2 -2
- package/dist/data/Properties.cjs.map +1 -1
- package/dist/data/Properties.js +2 -2
- package/dist/data/Properties.js.map +1 -1
- package/dist/data/cells/TagList.cjs +19 -3
- package/dist/data/cells/TagList.cjs.map +1 -1
- package/dist/data/cells/TagList.d.ts +11 -1
- package/dist/data/cells/TagList.d.ts.map +1 -1
- package/dist/data/cells/TagList.js +19 -3
- package/dist/data/cells/TagList.js.map +1 -1
- package/dist/data/cells/Timestamp.cjs +17 -2
- package/dist/data/cells/Timestamp.cjs.map +1 -1
- package/dist/data/cells/Timestamp.d.ts +2 -0
- package/dist/data/cells/Timestamp.d.ts.map +1 -1
- package/dist/data/cells/Timestamp.js +17 -2
- package/dist/data/cells/Timestamp.js.map +1 -1
- package/dist/data/log-utils.cjs +7 -0
- package/dist/data/log-utils.cjs.map +1 -0
- package/dist/data/log-utils.d.ts +2 -0
- package/dist/data/log-utils.d.ts.map +1 -0
- package/dist/data/log-utils.js +7 -0
- package/dist/data/log-utils.js.map +1 -0
- package/dist/data.cjs +3 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +2 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +5 -2
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/dist/rpc/EntityExplorerApp.cjs +3 -2
- package/dist/rpc/EntityExplorerApp.cjs.map +1 -1
- package/dist/rpc/EntityExplorerApp.js +3 -2
- package/dist/rpc/EntityExplorerApp.js.map +1 -1
- package/dist/rpc/OperationActionDialog.cjs +1 -1
- package/dist/rpc/OperationActionDialog.cjs.map +1 -1
- package/dist/rpc/OperationActionDialog.js +1 -1
- package/dist/rpc/OperationActionDialog.js.map +1 -1
- package/dist/rpc/OperationCommandPage.cjs +21 -14
- package/dist/rpc/OperationCommandPage.cjs.map +1 -1
- package/dist/rpc/OperationCommandPage.d.ts.map +1 -1
- package/dist/rpc/OperationCommandPage.js +21 -14
- package/dist/rpc/OperationCommandPage.js.map +1 -1
- package/dist/rpc/apiClient.cjs +316 -0
- package/dist/rpc/apiClient.cjs.map +1 -0
- package/dist/rpc/apiClient.d.ts +44 -0
- package/dist/rpc/apiClient.d.ts.map +1 -0
- package/dist/rpc/apiClient.js +316 -0
- package/dist/rpc/apiClient.js.map +1 -0
- package/dist/rpc/useOperations.cjs.map +1 -1
- package/dist/rpc/useOperations.d.ts +2 -1
- package/dist/rpc/useOperations.d.ts.map +1 -1
- package/dist/rpc/useOperations.js.map +1 -1
- package/dist/rpc.cjs +3 -0
- package/dist/rpc.cjs.map +1 -1
- package/dist/rpc.d.ts +1 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +3 -0
- package/dist/rpc.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Properties.cjs","sources":["../../src/data/Properties.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { formatPropertyLabel } from \"./properties-utils\";\n\nexport type PropertiesAction<V = unknown> = {\n /** Stable action id. */\n id: string;\n /** Iconify name or imported icon component. */\n icon: string | StaticIconComponent;\n /** Accessible label and tooltip for this row action. */\n label: (key: string, value: V, item: PropertiesItem<V>) => string;\n /** Return false to hide the action for a specific row. */\n visible?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Return true to disable the action for a specific row. */\n disabled?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Called when the row action is clicked. */\n onClick: (key: string, value: V, item: PropertiesItem<V>) => void;\n};\n\nexport type PropertiesItem<V = unknown> = {\n /** Stable property key. Also used as the default label. */\n key: string;\n /** Property value passed to renderers and actions. */\n value: V;\n /** Secondary text shown below the label. */\n subtitle?: ReactNode;\n /** Hide this row without removing it from the item array. */\n hidden?: boolean;\n /** Enables an expandable child area below the row value. */\n expandable?: boolean;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Called when an expandable row is toggled. */\n onToggle?: (next: boolean) => void;\n /** Child content rendered when the row is expanded. */\n renderChildren?: () => ReactNode;\n};\n\nexport type PropertiesDensity = \"comfortable\" | \"compact\";\n\nexport type PropertiesProps<V = unknown> = {\n /** Property rows to render. Hidden items are filtered out. */\n items: PropertiesItem<V>[];\n /** Custom label renderer. */\n renderLabel?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Custom value renderer. */\n renderValue?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Static icon name or function that returns an icon name per row. */\n labelIcon?: string | ((key: string, value: V, item: PropertiesItem<V>) => string | undefined);\n /** Actions rendered before the value. */\n prefixActions?: PropertiesAction<V>[];\n /** Actions rendered after the value. */\n suffixActions?: PropertiesAction<V>[];\n /** Classes applied to the list root. */\n className?: string;\n /** Classes applied to each row. */\n rowClassName?: string;\n /** Classes applied to each label cell. */\n labelClassName?: string;\n /** Classes applied to each value cell. */\n valueClassName?: string;\n /** Row density preset. */\n density?: PropertiesDensity;\n /** Empty-state content when no rows are visible. */\n emptyMessage?: ReactNode;\n};\n\nconst DENSITY_CLASSES: Record<\n PropertiesDensity,\n { row: string; label: string; value: string; gridCols: string }\n> = {\n comfortable: {\n row: \"px-density-3 py-density-2\",\n label: \"text-xs font-medium text-muted-foreground\",\n value: \"text-sm text-foreground\",\n gridCols: \"grid-cols-[minmax(7rem,12rem)_minmax(0,1fr)]\",\n },\n compact: {\n row: \"px-density-2 py-density-1.5\",\n label: \"font-mono text-[11px] text-muted-foreground\",\n value: \"text-xs text-foreground\",\n gridCols: \"grid-cols-[minmax(8rem,14rem)_minmax(0,1fr)]\",\n },\n};\n\nexport function Properties<V = unknown>({\n items,\n renderLabel,\n renderValue,\n labelIcon,\n prefixActions,\n suffixActions,\n className,\n rowClassName,\n labelClassName,\n valueClassName,\n density = \"comfortable\",\n emptyMessage = \"No properties\",\n}: PropertiesProps<V>) {\n const visible = items.filter((item) => !item.hidden);\n\n if (visible.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const styles = DENSITY_CLASSES[density];\n\n return (\n <dl\n className={cn(\n \"divide-y divide-border rounded-md border border-border bg-muted/20\",\n className,\n )}\n >\n {visible.map((item) => {\n const iconName =\n typeof labelIcon === \"function\" ? labelIcon(item.key, item.value, item) : labelIcon;\n const renderedLabel = renderLabel\n ? renderLabel(item.key, item.value, item)\n : formatPropertyLabel(item.key);\n const ariaLabel = typeof renderedLabel === \"string\" ? renderedLabel : item.key;\n\n return (\n <div\n key={item.key}\n className={cn(\"grid min-w-0 gap-density-3\", styles.gridCols, styles.row, rowClassName)}\n >\n <dt\n aria-label={ariaLabel}\n className={cn(\"min-w-0 truncate\", styles.label, labelClassName)}\n >\n <div className=\"flex min-w-0 items-center gap-density-1\">\n {iconName ? <Icon name={iconName} className=\"text-xs shrink-0\" /> : null}\n <span className=\"min-w-0 truncate\">{renderedLabel}</span>\n </div>\n {item.subtitle ? (\n <div className=\"mt-0.5 text-[10px] font-normal text-muted-foreground/80\">\n {item.subtitle}\n </div>\n ) : null}\n </dt>\n <dd className={cn(\"min-w-0 space-y-density-1\", valueClassName)}>\n <div className=\"flex min-w-0 items-start gap-density-1\">\n <ActionList actions={prefixActions} item={item} className=\"shrink-0 pt-0.5\" />\n <div className={cn(\"min-w-0 max-w-full flex-1\", styles.value)}>\n {renderValue\n ? renderValue(item.key, item.value, item)\n : defaultRenderValue(item.value)}\n </div>\n <ActionList actions={suffixActions} item={item} className=\"shrink-0 pt-0.5\" />\n </div>\n {item.expandable && item.expanded && item.renderChildren\n ? item.renderChildren()\n : null}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction ActionList<V>({\n actions,\n item,\n className,\n}: {\n actions: PropertiesAction<V>[] | undefined;\n item: PropertiesItem<V>;\n className?: string;\n}) {\n if (!actions || actions.length === 0) return null;\n const rendered = actions.filter(\n (action) => !action.visible || action.visible(item.key, item.value, item),\n );\n if (rendered.length === 0) return null;\n return (\n <span className={cn(\"inline-flex items-center gap-0.5\", className)}>\n {rendered.map((action) => (\n <PropertiesActionButton key={action.id} action={action} item={item} />\n ))}\n </span>\n );\n}\n\nfunction PropertiesActionButton<V>({\n action,\n item,\n}: {\n action: PropertiesAction<V>;\n item: PropertiesItem<V>;\n}) {\n const label = action.label(item.key, item.value, item);\n const disabled = action.disabled?.(item.key, item.value, item) ?? false;\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n \"disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-muted-foreground\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n action.onClick(item.key, item.value, item);\n }}\n >\n <Icon\n {...(typeof action.icon === \"string\" ? { name: action.icon } : { icon: action.icon })}\n className=\"text-xs\"\n />\n </button>\n );\n}\n\nfunction defaultRenderValue(value: unknown): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"font-mono italic text-muted-foreground\">null</span>;\n }\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return <span className=\"font-mono\">{String(value)}</span>;\n }\n try {\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n } catch {\n return String(value);\n }\n}\n"],"names":["jsx","cn","formatPropertyLabel","jsxs","Icon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Properties.cjs","sources":["../../src/data/Properties.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { formatPropertyLabel } from \"./properties-utils\";\n\nexport type PropertiesAction<V = unknown> = {\n /** Stable action id. */\n id: string;\n /** Iconify name or imported icon component. */\n icon: string | StaticIconComponent;\n /** Accessible label and tooltip for this row action. */\n label: (key: string, value: V, item: PropertiesItem<V>) => string;\n /** Return false to hide the action for a specific row. */\n visible?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Return true to disable the action for a specific row. */\n disabled?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Called when the row action is clicked. */\n onClick: (key: string, value: V, item: PropertiesItem<V>) => void;\n};\n\nexport type PropertiesItem<V = unknown> = {\n /** Stable property key. Also used as the default label. */\n key: string;\n /** Property value passed to renderers and actions. */\n value: V;\n /** Secondary text shown below the label. */\n subtitle?: ReactNode;\n /** Hide this row without removing it from the item array. */\n hidden?: boolean;\n /** Enables an expandable child area below the row value. */\n expandable?: boolean;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Called when an expandable row is toggled. */\n onToggle?: (next: boolean) => void;\n /** Child content rendered when the row is expanded. */\n renderChildren?: () => ReactNode;\n};\n\nexport type PropertiesDensity = \"comfortable\" | \"compact\";\n\nexport type PropertiesProps<V = unknown> = {\n /** Property rows to render. Hidden items are filtered out. */\n items: PropertiesItem<V>[];\n /** Custom label renderer. */\n renderLabel?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Custom value renderer. */\n renderValue?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Static icon name or function that returns an icon name per row. */\n labelIcon?: string | ((key: string, value: V, item: PropertiesItem<V>) => string | undefined);\n /** Actions rendered before the value. */\n prefixActions?: PropertiesAction<V>[];\n /** Actions rendered after the value. */\n suffixActions?: PropertiesAction<V>[];\n /** Classes applied to the list root. */\n className?: string;\n /** Classes applied to each row. */\n rowClassName?: string;\n /** Classes applied to each label cell. */\n labelClassName?: string;\n /** Classes applied to each value cell. */\n valueClassName?: string;\n /** Row density preset. */\n density?: PropertiesDensity;\n /** Empty-state content when no rows are visible. */\n emptyMessage?: ReactNode;\n};\n\nconst DENSITY_CLASSES: Record<\n PropertiesDensity,\n { row: string; label: string; value: string; gridCols: string }\n> = {\n comfortable: {\n row: \"px-density-3 py-density-2\",\n label: \"text-xs font-medium text-muted-foreground\",\n value: \"text-sm text-foreground\",\n gridCols: \"grid-cols-[minmax(7rem,12rem)_minmax(0,1fr)]\",\n },\n compact: {\n row: \"px-density-2 py-density-1.5\",\n label: \"font-mono text-[11px] text-muted-foreground\",\n value: \"text-xs text-foreground\",\n gridCols: \"grid-cols-[minmax(8rem,14rem)_minmax(0,1fr)]\",\n },\n};\n\nexport function Properties<V = unknown>({\n items,\n renderLabel,\n renderValue,\n labelIcon,\n prefixActions,\n suffixActions,\n className,\n rowClassName,\n labelClassName,\n valueClassName,\n density = \"comfortable\",\n emptyMessage = \"No properties\",\n}: PropertiesProps<V>) {\n const visible = items.filter((item) => !item.hidden);\n\n if (visible.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const styles = DENSITY_CLASSES[density];\n\n return (\n <dl\n className={cn(\n \"divide-y divide-border rounded-md border border-border bg-muted/20\",\n className,\n )}\n >\n {visible.map((item) => {\n const iconName =\n typeof labelIcon === \"function\" ? labelIcon(item.key, item.value, item) : labelIcon;\n const renderedLabel = renderLabel\n ? renderLabel(item.key, item.value, item)\n : formatPropertyLabel(item.key);\n const ariaLabel = typeof renderedLabel === \"string\" ? renderedLabel : item.key;\n\n return (\n <div\n key={item.key}\n className={cn(\"grid min-w-0 gap-density-3\", styles.gridCols, styles.row, rowClassName)}\n >\n <dt\n aria-label={ariaLabel}\n className={cn(\"min-w-0 truncate\", styles.label, labelClassName)}\n >\n <div className=\"flex min-w-0 items-center gap-density-1\">\n {iconName ? <Icon name={iconName} className=\"text-xs shrink-0\" /> : null}\n <span className=\"min-w-0 truncate\">{renderedLabel}</span>\n </div>\n {item.subtitle ? (\n <div className=\"mt-0.5 text-[10px] font-normal text-muted-foreground/80\">\n {item.subtitle}\n </div>\n ) : null}\n </dt>\n <dd className={cn(\"min-w-0 space-y-density-1\", valueClassName)}>\n <div className=\"flex min-w-0 items-start gap-density-1\">\n <ActionList actions={prefixActions} item={item} className=\"shrink-0 pt-0.5\" />\n <div className={cn(\"min-w-0 max-w-full\", styles.value)}>\n {renderValue\n ? renderValue(item.key, item.value, item)\n : defaultRenderValue(item.value)}\n </div>\n <ActionList actions={suffixActions} item={item} className=\"shrink-0 pt-0.5\" />\n </div>\n {item.expandable && item.expanded && item.renderChildren\n ? item.renderChildren()\n : null}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction ActionList<V>({\n actions,\n item,\n className,\n}: {\n actions: PropertiesAction<V>[] | undefined;\n item: PropertiesItem<V>;\n className?: string;\n}) {\n if (!actions || actions.length === 0) return null;\n const rendered = actions.filter(\n (action) => !action.visible || action.visible(item.key, item.value, item),\n );\n if (rendered.length === 0) return null;\n return (\n <span className={cn(\"inline-flex items-center\", className)}>\n {rendered.map((action) => (\n <PropertiesActionButton key={action.id} action={action} item={item} />\n ))}\n </span>\n );\n}\n\nfunction PropertiesActionButton<V>({\n action,\n item,\n}: {\n action: PropertiesAction<V>;\n item: PropertiesItem<V>;\n}) {\n const label = action.label(item.key, item.value, item);\n const disabled = action.disabled?.(item.key, item.value, item) ?? false;\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n \"disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-muted-foreground\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n action.onClick(item.key, item.value, item);\n }}\n >\n <Icon\n {...(typeof action.icon === \"string\" ? { name: action.icon } : { icon: action.icon })}\n className=\"text-xs\"\n />\n </button>\n );\n}\n\nfunction defaultRenderValue(value: unknown): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"font-mono italic text-muted-foreground\">null</span>;\n }\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return <span className=\"font-mono\">{String(value)}</span>;\n }\n try {\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n } catch {\n return String(value);\n }\n}\n"],"names":["jsx","cn","formatPropertyLabel","jsxs","Icon"],"mappings":"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"}
|
package/dist/data/Properties.js
CHANGED
|
@@ -68,7 +68,7 @@ function Properties({
|
|
|
68
68
|
/* @__PURE__ */ jsxs("dd", { className: cn("min-w-0 space-y-density-1", valueClassName), children: [
|
|
69
69
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-density-1", children: [
|
|
70
70
|
/* @__PURE__ */ jsx(ActionList, { actions: prefixActions, item, className: "shrink-0 pt-0.5" }),
|
|
71
|
-
/* @__PURE__ */ jsx("div", { className: cn("min-w-0 max-w-full
|
|
71
|
+
/* @__PURE__ */ jsx("div", { className: cn("min-w-0 max-w-full", styles.value), children: renderValue ? renderValue(item.key, item.value, item) : defaultRenderValue(item.value) }),
|
|
72
72
|
/* @__PURE__ */ jsx(ActionList, { actions: suffixActions, item, className: "shrink-0 pt-0.5" })
|
|
73
73
|
] }),
|
|
74
74
|
item.expandable && item.expanded && item.renderChildren ? item.renderChildren() : null
|
|
@@ -91,7 +91,7 @@ function ActionList({
|
|
|
91
91
|
(action) => !action.visible || action.visible(item.key, item.value, item)
|
|
92
92
|
);
|
|
93
93
|
if (rendered.length === 0) return null;
|
|
94
|
-
return /* @__PURE__ */ jsx("span", { className: cn("inline-flex items-center
|
|
94
|
+
return /* @__PURE__ */ jsx("span", { className: cn("inline-flex items-center", className), children: rendered.map((action) => /* @__PURE__ */ jsx(PropertiesActionButton, { action, item }, action.id)) });
|
|
95
95
|
}
|
|
96
96
|
function PropertiesActionButton({
|
|
97
97
|
action,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Properties.js","sources":["../../src/data/Properties.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { formatPropertyLabel } from \"./properties-utils\";\n\nexport type PropertiesAction<V = unknown> = {\n /** Stable action id. */\n id: string;\n /** Iconify name or imported icon component. */\n icon: string | StaticIconComponent;\n /** Accessible label and tooltip for this row action. */\n label: (key: string, value: V, item: PropertiesItem<V>) => string;\n /** Return false to hide the action for a specific row. */\n visible?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Return true to disable the action for a specific row. */\n disabled?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Called when the row action is clicked. */\n onClick: (key: string, value: V, item: PropertiesItem<V>) => void;\n};\n\nexport type PropertiesItem<V = unknown> = {\n /** Stable property key. Also used as the default label. */\n key: string;\n /** Property value passed to renderers and actions. */\n value: V;\n /** Secondary text shown below the label. */\n subtitle?: ReactNode;\n /** Hide this row without removing it from the item array. */\n hidden?: boolean;\n /** Enables an expandable child area below the row value. */\n expandable?: boolean;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Called when an expandable row is toggled. */\n onToggle?: (next: boolean) => void;\n /** Child content rendered when the row is expanded. */\n renderChildren?: () => ReactNode;\n};\n\nexport type PropertiesDensity = \"comfortable\" | \"compact\";\n\nexport type PropertiesProps<V = unknown> = {\n /** Property rows to render. Hidden items are filtered out. */\n items: PropertiesItem<V>[];\n /** Custom label renderer. */\n renderLabel?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Custom value renderer. */\n renderValue?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Static icon name or function that returns an icon name per row. */\n labelIcon?: string | ((key: string, value: V, item: PropertiesItem<V>) => string | undefined);\n /** Actions rendered before the value. */\n prefixActions?: PropertiesAction<V>[];\n /** Actions rendered after the value. */\n suffixActions?: PropertiesAction<V>[];\n /** Classes applied to the list root. */\n className?: string;\n /** Classes applied to each row. */\n rowClassName?: string;\n /** Classes applied to each label cell. */\n labelClassName?: string;\n /** Classes applied to each value cell. */\n valueClassName?: string;\n /** Row density preset. */\n density?: PropertiesDensity;\n /** Empty-state content when no rows are visible. */\n emptyMessage?: ReactNode;\n};\n\nconst DENSITY_CLASSES: Record<\n PropertiesDensity,\n { row: string; label: string; value: string; gridCols: string }\n> = {\n comfortable: {\n row: \"px-density-3 py-density-2\",\n label: \"text-xs font-medium text-muted-foreground\",\n value: \"text-sm text-foreground\",\n gridCols: \"grid-cols-[minmax(7rem,12rem)_minmax(0,1fr)]\",\n },\n compact: {\n row: \"px-density-2 py-density-1.5\",\n label: \"font-mono text-[11px] text-muted-foreground\",\n value: \"text-xs text-foreground\",\n gridCols: \"grid-cols-[minmax(8rem,14rem)_minmax(0,1fr)]\",\n },\n};\n\nexport function Properties<V = unknown>({\n items,\n renderLabel,\n renderValue,\n labelIcon,\n prefixActions,\n suffixActions,\n className,\n rowClassName,\n labelClassName,\n valueClassName,\n density = \"comfortable\",\n emptyMessage = \"No properties\",\n}: PropertiesProps<V>) {\n const visible = items.filter((item) => !item.hidden);\n\n if (visible.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const styles = DENSITY_CLASSES[density];\n\n return (\n <dl\n className={cn(\n \"divide-y divide-border rounded-md border border-border bg-muted/20\",\n className,\n )}\n >\n {visible.map((item) => {\n const iconName =\n typeof labelIcon === \"function\" ? labelIcon(item.key, item.value, item) : labelIcon;\n const renderedLabel = renderLabel\n ? renderLabel(item.key, item.value, item)\n : formatPropertyLabel(item.key);\n const ariaLabel = typeof renderedLabel === \"string\" ? renderedLabel : item.key;\n\n return (\n <div\n key={item.key}\n className={cn(\"grid min-w-0 gap-density-3\", styles.gridCols, styles.row, rowClassName)}\n >\n <dt\n aria-label={ariaLabel}\n className={cn(\"min-w-0 truncate\", styles.label, labelClassName)}\n >\n <div className=\"flex min-w-0 items-center gap-density-1\">\n {iconName ? <Icon name={iconName} className=\"text-xs shrink-0\" /> : null}\n <span className=\"min-w-0 truncate\">{renderedLabel}</span>\n </div>\n {item.subtitle ? (\n <div className=\"mt-0.5 text-[10px] font-normal text-muted-foreground/80\">\n {item.subtitle}\n </div>\n ) : null}\n </dt>\n <dd className={cn(\"min-w-0 space-y-density-1\", valueClassName)}>\n <div className=\"flex min-w-0 items-start gap-density-1\">\n <ActionList actions={prefixActions} item={item} className=\"shrink-0 pt-0.5\" />\n <div className={cn(\"min-w-0 max-w-full flex-1\", styles.value)}>\n {renderValue\n ? renderValue(item.key, item.value, item)\n : defaultRenderValue(item.value)}\n </div>\n <ActionList actions={suffixActions} item={item} className=\"shrink-0 pt-0.5\" />\n </div>\n {item.expandable && item.expanded && item.renderChildren\n ? item.renderChildren()\n : null}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction ActionList<V>({\n actions,\n item,\n className,\n}: {\n actions: PropertiesAction<V>[] | undefined;\n item: PropertiesItem<V>;\n className?: string;\n}) {\n if (!actions || actions.length === 0) return null;\n const rendered = actions.filter(\n (action) => !action.visible || action.visible(item.key, item.value, item),\n );\n if (rendered.length === 0) return null;\n return (\n <span className={cn(\"inline-flex items-center gap-0.5\", className)}>\n {rendered.map((action) => (\n <PropertiesActionButton key={action.id} action={action} item={item} />\n ))}\n </span>\n );\n}\n\nfunction PropertiesActionButton<V>({\n action,\n item,\n}: {\n action: PropertiesAction<V>;\n item: PropertiesItem<V>;\n}) {\n const label = action.label(item.key, item.value, item);\n const disabled = action.disabled?.(item.key, item.value, item) ?? false;\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n \"disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-muted-foreground\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n action.onClick(item.key, item.value, item);\n }}\n >\n <Icon\n {...(typeof action.icon === \"string\" ? { name: action.icon } : { icon: action.icon })}\n className=\"text-xs\"\n />\n </button>\n );\n}\n\nfunction defaultRenderValue(value: unknown): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"font-mono italic text-muted-foreground\">null</span>;\n }\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return <span className=\"font-mono\">{String(value)}</span>;\n }\n try {\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n } catch {\n return String(value);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Properties.js","sources":["../../src/data/Properties.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { formatPropertyLabel } from \"./properties-utils\";\n\nexport type PropertiesAction<V = unknown> = {\n /** Stable action id. */\n id: string;\n /** Iconify name or imported icon component. */\n icon: string | StaticIconComponent;\n /** Accessible label and tooltip for this row action. */\n label: (key: string, value: V, item: PropertiesItem<V>) => string;\n /** Return false to hide the action for a specific row. */\n visible?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Return true to disable the action for a specific row. */\n disabled?: (key: string, value: V, item: PropertiesItem<V>) => boolean;\n /** Called when the row action is clicked. */\n onClick: (key: string, value: V, item: PropertiesItem<V>) => void;\n};\n\nexport type PropertiesItem<V = unknown> = {\n /** Stable property key. Also used as the default label. */\n key: string;\n /** Property value passed to renderers and actions. */\n value: V;\n /** Secondary text shown below the label. */\n subtitle?: ReactNode;\n /** Hide this row without removing it from the item array. */\n hidden?: boolean;\n /** Enables an expandable child area below the row value. */\n expandable?: boolean;\n /** Controlled expanded state. */\n expanded?: boolean;\n /** Called when an expandable row is toggled. */\n onToggle?: (next: boolean) => void;\n /** Child content rendered when the row is expanded. */\n renderChildren?: () => ReactNode;\n};\n\nexport type PropertiesDensity = \"comfortable\" | \"compact\";\n\nexport type PropertiesProps<V = unknown> = {\n /** Property rows to render. Hidden items are filtered out. */\n items: PropertiesItem<V>[];\n /** Custom label renderer. */\n renderLabel?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Custom value renderer. */\n renderValue?: (key: string, value: V, item: PropertiesItem<V>) => ReactNode;\n /** Static icon name or function that returns an icon name per row. */\n labelIcon?: string | ((key: string, value: V, item: PropertiesItem<V>) => string | undefined);\n /** Actions rendered before the value. */\n prefixActions?: PropertiesAction<V>[];\n /** Actions rendered after the value. */\n suffixActions?: PropertiesAction<V>[];\n /** Classes applied to the list root. */\n className?: string;\n /** Classes applied to each row. */\n rowClassName?: string;\n /** Classes applied to each label cell. */\n labelClassName?: string;\n /** Classes applied to each value cell. */\n valueClassName?: string;\n /** Row density preset. */\n density?: PropertiesDensity;\n /** Empty-state content when no rows are visible. */\n emptyMessage?: ReactNode;\n};\n\nconst DENSITY_CLASSES: Record<\n PropertiesDensity,\n { row: string; label: string; value: string; gridCols: string }\n> = {\n comfortable: {\n row: \"px-density-3 py-density-2\",\n label: \"text-xs font-medium text-muted-foreground\",\n value: \"text-sm text-foreground\",\n gridCols: \"grid-cols-[minmax(7rem,12rem)_minmax(0,1fr)]\",\n },\n compact: {\n row: \"px-density-2 py-density-1.5\",\n label: \"font-mono text-[11px] text-muted-foreground\",\n value: \"text-xs text-foreground\",\n gridCols: \"grid-cols-[minmax(8rem,14rem)_minmax(0,1fr)]\",\n },\n};\n\nexport function Properties<V = unknown>({\n items,\n renderLabel,\n renderValue,\n labelIcon,\n prefixActions,\n suffixActions,\n className,\n rowClassName,\n labelClassName,\n valueClassName,\n density = \"comfortable\",\n emptyMessage = \"No properties\",\n}: PropertiesProps<V>) {\n const visible = items.filter((item) => !item.hidden);\n\n if (visible.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const styles = DENSITY_CLASSES[density];\n\n return (\n <dl\n className={cn(\n \"divide-y divide-border rounded-md border border-border bg-muted/20\",\n className,\n )}\n >\n {visible.map((item) => {\n const iconName =\n typeof labelIcon === \"function\" ? labelIcon(item.key, item.value, item) : labelIcon;\n const renderedLabel = renderLabel\n ? renderLabel(item.key, item.value, item)\n : formatPropertyLabel(item.key);\n const ariaLabel = typeof renderedLabel === \"string\" ? renderedLabel : item.key;\n\n return (\n <div\n key={item.key}\n className={cn(\"grid min-w-0 gap-density-3\", styles.gridCols, styles.row, rowClassName)}\n >\n <dt\n aria-label={ariaLabel}\n className={cn(\"min-w-0 truncate\", styles.label, labelClassName)}\n >\n <div className=\"flex min-w-0 items-center gap-density-1\">\n {iconName ? <Icon name={iconName} className=\"text-xs shrink-0\" /> : null}\n <span className=\"min-w-0 truncate\">{renderedLabel}</span>\n </div>\n {item.subtitle ? (\n <div className=\"mt-0.5 text-[10px] font-normal text-muted-foreground/80\">\n {item.subtitle}\n </div>\n ) : null}\n </dt>\n <dd className={cn(\"min-w-0 space-y-density-1\", valueClassName)}>\n <div className=\"flex min-w-0 items-start gap-density-1\">\n <ActionList actions={prefixActions} item={item} className=\"shrink-0 pt-0.5\" />\n <div className={cn(\"min-w-0 max-w-full\", styles.value)}>\n {renderValue\n ? renderValue(item.key, item.value, item)\n : defaultRenderValue(item.value)}\n </div>\n <ActionList actions={suffixActions} item={item} className=\"shrink-0 pt-0.5\" />\n </div>\n {item.expandable && item.expanded && item.renderChildren\n ? item.renderChildren()\n : null}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction ActionList<V>({\n actions,\n item,\n className,\n}: {\n actions: PropertiesAction<V>[] | undefined;\n item: PropertiesItem<V>;\n className?: string;\n}) {\n if (!actions || actions.length === 0) return null;\n const rendered = actions.filter(\n (action) => !action.visible || action.visible(item.key, item.value, item),\n );\n if (rendered.length === 0) return null;\n return (\n <span className={cn(\"inline-flex items-center\", className)}>\n {rendered.map((action) => (\n <PropertiesActionButton key={action.id} action={action} item={item} />\n ))}\n </span>\n );\n}\n\nfunction PropertiesActionButton<V>({\n action,\n item,\n}: {\n action: PropertiesAction<V>;\n item: PropertiesItem<V>;\n}) {\n const label = action.label(item.key, item.value, item);\n const disabled = action.disabled?.(item.key, item.value, item) ?? false;\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n \"disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-muted-foreground\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n action.onClick(item.key, item.value, item);\n }}\n >\n <Icon\n {...(typeof action.icon === \"string\" ? { name: action.icon } : { icon: action.icon })}\n className=\"text-xs\"\n />\n </button>\n );\n}\n\nfunction defaultRenderValue(value: unknown): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"font-mono italic text-muted-foreground\">null</span>;\n }\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return <span className=\"font-mono\">{String(value)}</span>;\n }\n try {\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n } catch {\n return String(value);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -220,13 +220,25 @@ function TagList({
|
|
|
220
220
|
maxVisible = 3,
|
|
221
221
|
className,
|
|
222
222
|
actions = "hover",
|
|
223
|
-
compact
|
|
223
|
+
compact,
|
|
224
|
+
wrap = false
|
|
224
225
|
}) {
|
|
225
226
|
const density = useDensity.useDensityValue();
|
|
226
227
|
const isCompact = compact ?? density === "compact";
|
|
227
228
|
if (tags.length === 0) {
|
|
228
229
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "—" });
|
|
229
230
|
}
|
|
231
|
+
if (wrap) {
|
|
232
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("flex min-w-0 flex-wrap items-center gap-1", className), children: tags.map((tag, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
233
|
+
TagBadge,
|
|
234
|
+
{
|
|
235
|
+
tag,
|
|
236
|
+
actions,
|
|
237
|
+
compact: isCompact
|
|
238
|
+
},
|
|
239
|
+
`${tag.display}-${index}`
|
|
240
|
+
)) });
|
|
241
|
+
}
|
|
230
242
|
const visible = tags.slice(0, maxVisible);
|
|
231
243
|
const overflow = tags.slice(maxVisible);
|
|
232
244
|
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils.cn("flex min-w-0 items-center gap-1", className), children: [
|
|
@@ -263,7 +275,10 @@ function TagList({
|
|
|
263
275
|
)
|
|
264
276
|
] });
|
|
265
277
|
}
|
|
266
|
-
function TagPropertiesList({
|
|
278
|
+
function TagPropertiesList({
|
|
279
|
+
tags,
|
|
280
|
+
rowClassName = "grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]"
|
|
281
|
+
}) {
|
|
267
282
|
const tagActions = useTagActions();
|
|
268
283
|
const items = react.useMemo(
|
|
269
284
|
() => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),
|
|
@@ -275,7 +290,7 @@ function TagPropertiesList({ tags }) {
|
|
|
275
290
|
items,
|
|
276
291
|
density: "compact",
|
|
277
292
|
className: "border-0 bg-transparent",
|
|
278
|
-
rowClassName
|
|
293
|
+
rowClassName,
|
|
279
294
|
renderLabel: (_key, tag) => tag.key ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: tag.key }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic text-muted-foreground", children: "tag" }),
|
|
280
295
|
renderValue: (_key, tag) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate font-mono", title: tag.value, children: tag.value }),
|
|
281
296
|
suffixActions: [
|
|
@@ -323,6 +338,7 @@ function useTagActionsValue(value, onChange) {
|
|
|
323
338
|
}
|
|
324
339
|
exports.TagActionsProvider = TagActionsProvider;
|
|
325
340
|
exports.TagList = TagList;
|
|
341
|
+
exports.TagPropertiesList = TagPropertiesList;
|
|
326
342
|
exports.normalizeTags = normalizeTags;
|
|
327
343
|
exports.splitTagToken = splitTagToken;
|
|
328
344
|
exports.tagActionsFromRecord = tagActionsFromRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagList.cjs","sources":["../../../src/data/cells/TagList.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo } from \"react\";\nimport { Badge } from \"../Badge\";\nimport { HoverCard } from \"../../overlay/HoverCard\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { Properties, type PropertiesItem } from \"../Properties\";\nimport { UiCopy, UiZoomIn, UiZoomOut } from \"../../icons\";\nimport { useDensityValue } from \"../../hooks/use-density\";\nimport { cn } from \"../../lib/utils\";\nimport type { FilterMode } from \"../FilterPill\";\n\nexport type TagInput = string | { key: string; value: string } | { name: string; value: string };\n\nexport type NormalizedTag = {\n key?: string;\n value: string;\n display: string;\n token: string;\n};\n\nexport type TagsValue =\n | TagInput[]\n | Record<string, string | number | boolean | null>\n | null\n | undefined;\n\nexport type TagsOptions = {\n /** Number of tags to show inline before using the `+N` overflow popover. */\n maxVisible?: number;\n /** Separator between tag keys and values. Defaults to `=`. */\n separator?: string;\n};\n\nexport function normalizeTags(value: TagsValue, separator = \"=\"): NormalizedTag[] {\n if (value == null) return [];\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeOne(entry, separator)).filter((tag) => tag.value !== \"\");\n }\n\n if (typeof value === \"object\") {\n return Object.entries(value)\n .map(([key, raw]): NormalizedTag | null => {\n if (raw == null) return null;\n const valueStr = String(raw).trim();\n if (!valueStr) return null;\n return {\n key,\n value: valueStr,\n display: `${key}${separator}${valueStr}`,\n token: `${key}${separator}${valueStr}`,\n } satisfies NormalizedTag;\n })\n .filter((tag): tag is NormalizedTag => tag !== null);\n }\n\n return [];\n}\n\nfunction normalizeOne(entry: TagInput, separator: string): NormalizedTag {\n if (typeof entry === \"string\") {\n const trimmed = entry.trim();\n const eq = trimmed.indexOf(\"=\");\n const colon = trimmed.indexOf(\":\");\n const splitAt =\n eq >= 0 && (colon < 0 || eq < colon)\n ? eq\n : colon >= 0 && colon < trimmed.length - 1\n ? colon\n : -1;\n\n if (splitAt > 0) {\n const key = trimmed.slice(0, splitAt);\n const value = trimmed.slice(splitAt + 1);\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n\n return { value: trimmed, display: trimmed, token: trimmed };\n }\n\n const key =\n \"key\" in entry && typeof entry.key === \"string\"\n ? entry.key\n : \"name\" in entry && typeof entry.name === \"string\"\n ? entry.name\n : undefined;\n const value = String(entry.value ?? \"\").trim();\n\n if (key) {\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n return { value, display: value, token: value };\n}\n\nexport function tagFilterTokens(value: TagsValue, separator = \"=\"): string[] {\n return normalizeTags(value, separator).map((tag) => tag.token);\n}\n\n/**\n * Splits a token previously produced by `normalizeTags`/`tagFilterTokens`\n * back into its key and value parts using the same separator. Tokens\n * without a separator (bare tags) return `{ key: \"\", value: token }`.\n */\nexport function splitTagToken(token: string, separator = \"=\"): { key: string; value: string } {\n const idx = token.indexOf(separator);\n if (idx <= 0) return { key: \"\", value: token };\n return {\n key: token.slice(0, idx),\n value: token.slice(idx + separator.length),\n };\n}\n\n// Filter mode mapping (re-using FilterPill's tri-state vocabulary so the wire\n// shape matches DataTable's existing multiFilters state).\nexport type TagFilterMode = Extract<FilterMode, \"include\" | \"exclude\">;\n\nexport type TagActionsContextValue = {\n /** Returns the current mode for a token; \"neutral\" if the user hasn't acted on it. */\n getMode: (token: string) => FilterMode;\n /** Toggle include for this token (off → include → off). */\n toggleInclude: (token: string) => void;\n /** Toggle exclude for this token (off → exclude → off). */\n toggleExclude: (token: string) => void;\n};\n\nconst noop: TagActionsContextValue = {\n getMode: () => \"neutral\",\n toggleInclude: () => {},\n toggleExclude: () => {},\n};\n\nconst TagActionsContext = createContext<TagActionsContextValue>(noop);\n\nexport function TagActionsProvider({\n value,\n children,\n}: {\n value: TagActionsContextValue;\n children: React.ReactNode;\n}) {\n return <TagActionsContext.Provider value={value}>{children}</TagActionsContext.Provider>;\n}\n\nexport function useTagActions(): TagActionsContextValue {\n return useContext(TagActionsContext);\n}\n\nexport type TagListProps = {\n /** Normalized tags to render. */\n tags: NormalizedTag[];\n /** Number of tags to show inline before the overflow popover. */\n maxVisible?: number;\n /** Classes applied to the tag row. */\n className?: string;\n /** Whether copy/zoom actions appear on hover or inline. */\n actions?: \"hover\" | \"inline\";\n /**\n * When `true`, visible tag badges render only their value — the key is\n * dropped from the inline display. The full `key=value` text is still\n * available in `tag.display` (used for tooltips, copy actions, and the\n * overflow `+N` popover). Defaults to whether the surrounding density\n * (via {@link useDensityValue}) is `\"compact\"`.\n */\n compact?: boolean;\n};\n\nfunction copyToClipboard(text: string) {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => undefined);\n }\n}\n\nfunction TagBadge({\n tag,\n actions: actionMode,\n compact = false,\n}: {\n tag: NormalizedTag;\n actions: \"hover\" | \"inline\";\n compact?: boolean;\n}) {\n const actions = useTagActions();\n const mode = actions.getMode(tag.token);\n\n const onPlus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleInclude(tag.token);\n },\n [actions, tag.token],\n );\n const onMinus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleExclude(tag.token);\n },\n [actions, tag.token],\n );\n const onCopy = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n copyToClipboard(tag.value);\n },\n [tag.value],\n );\n\n // Active state colors mirror FilterPill: include = green, exclude = red.\n const includeActive = mode === \"include\";\n const excludeActive = mode === \"exclude\";\n\n const showTooltip = compact && !!tag.key;\n const badge = (\n <span\n {...(showTooltip ? { title: tag.display } : {})}\n className={cn(\n \"inline-flex items-center rounded-md border bg-background align-middle\",\n includeActive\n ? \"border-green-500/50 bg-green-500/5\"\n : excludeActive\n ? \"border-red-500/50 bg-red-500/5\"\n : \"border-border\",\n )}\n >\n {tag.key && !compact ? (\n <Badge\n size=\"xs\"\n variant=\"label\"\n tone=\"neutral\"\n label={tag.key}\n value={tag.value}\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n className=\"border-transparent shadow-none\"\n />\n ) : (\n <Badge\n size=\"xs\"\n variant=\"soft\"\n tone=\"neutral\"\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n >\n {tag.value}\n </Badge>\n )}\n </span>\n );\n\n const toolbar = (\n <span className=\"inline-flex items-center gap-0.5\">\n <TagActionButton\n ariaLabel={`Include ${tag.display}`}\n onClick={onPlus}\n active={includeActive}\n activeClassName=\"bg-green-500/20 text-green-700 dark:text-green-400\"\n icon={UiZoomIn}\n />\n <TagActionButton\n ariaLabel={`Exclude ${tag.display}`}\n onClick={onMinus}\n active={excludeActive}\n activeClassName=\"bg-red-500/20 text-red-700 dark:text-red-400\"\n icon={UiZoomOut}\n />\n <TagActionButton ariaLabel={`Copy ${tag.display}`} onClick={onCopy} icon={UiCopy} />\n </span>\n );\n\n if (actionMode === \"inline\") {\n return (\n <span className=\"inline-flex items-center gap-0.5 rounded-md border border-border bg-background pr-0.5\">\n {badge}\n {toolbar}\n </span>\n );\n }\n\n // Action icons live in a hover popover above the tag — the cell stays\n // clean, and hovering reveals the magnifier-plus / magnifier-minus / copy\n // toolbar.\n return (\n <HoverCard placement=\"top\" delay={120} arrow={false} trigger={badge} cardClassName=\"!p-1\">\n {toolbar}\n </HoverCard>\n );\n}\n\nfunction TagActionButton({\n ariaLabel,\n onClick,\n active,\n activeClassName,\n icon,\n}: {\n ariaLabel: string;\n onClick: (event: React.MouseEvent) => void;\n active?: boolean;\n activeClassName?: string;\n icon: StaticIconComponent;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n onClick={onClick}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n active && activeClassName,\n )}\n >\n <Icon icon={icon} className=\"text-xs\" />\n </button>\n );\n}\n\nexport function TagList({\n tags,\n maxVisible = 3,\n className,\n actions = \"hover\",\n compact,\n}: TagListProps) {\n const density = useDensityValue();\n const isCompact = compact ?? density === \"compact\";\n\n if (tags.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>;\n }\n\n const visible = tags.slice(0, maxVisible);\n const overflow = tags.slice(maxVisible);\n\n return (\n <span className={cn(\"flex min-w-0 items-center gap-1\", className)}>\n <span className=\"flex min-w-0 flex-1 flex-nowrap items-center gap-1 overflow-hidden\">\n {visible.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n {overflow.length > 0 && (\n <HoverCard\n placement=\"top\"\n trigger={\n <Badge\n size=\"xs\"\n variant=\"outlined\"\n tone=\"neutral\"\n clickToCopy={false}\n className=\"shrink-0\"\n >\n +{overflow.length}\n </Badge>\n }\n cardClassName=\"min-w-72 max-w-[90vw] whitespace-normal !p-0\"\n >\n <TagPropertiesList tags={overflow} />\n </HoverCard>\n )}\n </span>\n );\n}\n\nfunction TagPropertiesList({ tags }: { tags: NormalizedTag[] }) {\n const tagActions = useTagActions();\n const items = useMemo<PropertiesItem<NormalizedTag>[]>(\n () => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),\n [tags],\n );\n\n return (\n <Properties<NormalizedTag>\n items={items}\n density=\"compact\"\n className=\"border-0 bg-transparent\"\n rowClassName=\"grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]\"\n renderLabel={(_key, tag) =>\n tag.key ? (\n <span className=\"font-mono\">{tag.key}</span>\n ) : (\n <span className=\"italic text-muted-foreground\">tag</span>\n )\n }\n renderValue={(_key, tag) => (\n <span className=\"block truncate font-mono\" title={tag.value}>\n {tag.value}\n </span>\n )}\n suffixActions={[\n {\n id: \"include\",\n icon: UiZoomIn,\n label: (_key, tag) => `Include ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleInclude(tag.token),\n },\n {\n id: \"exclude\",\n icon: UiZoomOut,\n label: (_key, tag) => `Exclude ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleExclude(tag.token),\n },\n {\n id: \"copy\",\n icon: UiCopy,\n label: (_key, tag) => `Copy ${tag.display}`,\n onClick: (_key, tag) => copyToClipboard(tag.value),\n },\n ]}\n />\n );\n}\n\n/**\n * Builds a TagActionsContextValue from a flat record-of-modes (the same\n * shape used by FilterBar's multi/nested-multi filters). Useful when\n * connecting DataTable's multiFilters state to the in-cell + / − buttons.\n */\nexport function tagActionsFromRecord(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return {\n getMode: (token) => value[token] ?? \"neutral\",\n toggleInclude: (token) => {\n const next = { ...value };\n if (next[token] === \"include\") delete next[token];\n else next[token] = \"include\";\n onChange(next);\n },\n toggleExclude: (token) => {\n const next = { ...value };\n if (next[token] === \"exclude\") delete next[token];\n else next[token] = \"exclude\";\n onChange(next);\n },\n };\n}\n\n// Memoizing the context value avoids re-rendering every TagList cell on\n// unrelated state changes.\nexport function useTagActionsValue(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return useMemo(() => tagActionsFromRecord(value, onChange), [value, onChange]);\n}\n"],"names":["key","value","createContext","jsx","useContext","useCallback","cn","Badge","jsxs","UiZoomIn","UiZoomOut","UiCopy","HoverCard","Icon","useDensityValue","useMemo","Properties"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TagList.cjs","sources":["../../../src/data/cells/TagList.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo } from \"react\";\nimport { Badge } from \"../Badge\";\nimport { HoverCard } from \"../../overlay/HoverCard\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { Properties, type PropertiesItem } from \"../Properties\";\nimport { UiCopy, UiZoomIn, UiZoomOut } from \"../../icons\";\nimport { useDensityValue } from \"../../hooks/use-density\";\nimport { cn } from \"../../lib/utils\";\nimport type { FilterMode } from \"../FilterPill\";\n\nexport type TagInput = string | { key: string; value: string } | { name: string; value: string };\n\nexport type NormalizedTag = {\n key?: string;\n value: string;\n display: string;\n token: string;\n};\n\nexport type TagsValue =\n | TagInput[]\n | Record<string, string | number | boolean | null>\n | null\n | undefined;\n\nexport type TagsOptions = {\n /** Number of tags to show inline before using the `+N` overflow popover. */\n maxVisible?: number;\n /** Separator between tag keys and values. Defaults to `=`. */\n separator?: string;\n};\n\nexport function normalizeTags(value: TagsValue, separator = \"=\"): NormalizedTag[] {\n if (value == null) return [];\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeOne(entry, separator)).filter((tag) => tag.value !== \"\");\n }\n\n if (typeof value === \"object\") {\n return Object.entries(value)\n .map(([key, raw]): NormalizedTag | null => {\n if (raw == null) return null;\n const valueStr = String(raw).trim();\n if (!valueStr) return null;\n return {\n key,\n value: valueStr,\n display: `${key}${separator}${valueStr}`,\n token: `${key}${separator}${valueStr}`,\n } satisfies NormalizedTag;\n })\n .filter((tag): tag is NormalizedTag => tag !== null);\n }\n\n return [];\n}\n\nfunction normalizeOne(entry: TagInput, separator: string): NormalizedTag {\n if (typeof entry === \"string\") {\n const trimmed = entry.trim();\n const eq = trimmed.indexOf(\"=\");\n const colon = trimmed.indexOf(\":\");\n const splitAt =\n eq >= 0 && (colon < 0 || eq < colon)\n ? eq\n : colon >= 0 && colon < trimmed.length - 1\n ? colon\n : -1;\n\n if (splitAt > 0) {\n const key = trimmed.slice(0, splitAt);\n const value = trimmed.slice(splitAt + 1);\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n\n return { value: trimmed, display: trimmed, token: trimmed };\n }\n\n const key =\n \"key\" in entry && typeof entry.key === \"string\"\n ? entry.key\n : \"name\" in entry && typeof entry.name === \"string\"\n ? entry.name\n : undefined;\n const value = String(entry.value ?? \"\").trim();\n\n if (key) {\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n return { value, display: value, token: value };\n}\n\nexport function tagFilterTokens(value: TagsValue, separator = \"=\"): string[] {\n return normalizeTags(value, separator).map((tag) => tag.token);\n}\n\n/**\n * Splits a token previously produced by `normalizeTags`/`tagFilterTokens`\n * back into its key and value parts using the same separator. Tokens\n * without a separator (bare tags) return `{ key: \"\", value: token }`.\n */\nexport function splitTagToken(token: string, separator = \"=\"): { key: string; value: string } {\n const idx = token.indexOf(separator);\n if (idx <= 0) return { key: \"\", value: token };\n return {\n key: token.slice(0, idx),\n value: token.slice(idx + separator.length),\n };\n}\n\n// Filter mode mapping (re-using FilterPill's tri-state vocabulary so the wire\n// shape matches DataTable's existing multiFilters state).\nexport type TagFilterMode = Extract<FilterMode, \"include\" | \"exclude\">;\n\nexport type TagActionsContextValue = {\n /** Returns the current mode for a token; \"neutral\" if the user hasn't acted on it. */\n getMode: (token: string) => FilterMode;\n /** Toggle include for this token (off → include → off). */\n toggleInclude: (token: string) => void;\n /** Toggle exclude for this token (off → exclude → off). */\n toggleExclude: (token: string) => void;\n};\n\nconst noop: TagActionsContextValue = {\n getMode: () => \"neutral\",\n toggleInclude: () => {},\n toggleExclude: () => {},\n};\n\nconst TagActionsContext = createContext<TagActionsContextValue>(noop);\n\nexport function TagActionsProvider({\n value,\n children,\n}: {\n value: TagActionsContextValue;\n children: React.ReactNode;\n}) {\n return <TagActionsContext.Provider value={value}>{children}</TagActionsContext.Provider>;\n}\n\nexport function useTagActions(): TagActionsContextValue {\n return useContext(TagActionsContext);\n}\n\nexport type TagListProps = {\n /** Normalized tags to render. */\n tags: NormalizedTag[];\n /** Number of tags to show inline before the overflow popover. */\n maxVisible?: number;\n /** Classes applied to the tag row. */\n className?: string;\n /** Whether copy/zoom actions appear on hover or inline. */\n actions?: \"hover\" | \"inline\";\n /**\n * When `true`, visible tag badges render only their value — the key is\n * dropped from the inline display. The full `key=value` text is still\n * available in `tag.display` (used for tooltips, copy actions, and the\n * overflow `+N` popover). Defaults to whether the surrounding density\n * (via {@link useDensityValue}) is `\"compact\"`.\n */\n compact?: boolean;\n /**\n * When `true`, tag badges wrap onto multiple lines instead of staying on a\n * single row with a `+N` overflow popover. Used by detail/expanded views\n * (e.g. LogsTable row details) where vertical space is available.\n */\n wrap?: boolean;\n};\n\nfunction copyToClipboard(text: string) {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => undefined);\n }\n}\n\nfunction TagBadge({\n tag,\n actions: actionMode,\n compact = false,\n}: {\n tag: NormalizedTag;\n actions: \"hover\" | \"inline\";\n compact?: boolean;\n}) {\n const actions = useTagActions();\n const mode = actions.getMode(tag.token);\n\n const onPlus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleInclude(tag.token);\n },\n [actions, tag.token],\n );\n const onMinus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleExclude(tag.token);\n },\n [actions, tag.token],\n );\n const onCopy = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n copyToClipboard(tag.value);\n },\n [tag.value],\n );\n\n // Active state colors mirror FilterPill: include = green, exclude = red.\n const includeActive = mode === \"include\";\n const excludeActive = mode === \"exclude\";\n\n const showTooltip = compact && !!tag.key;\n const badge = (\n <span\n {...(showTooltip ? { title: tag.display } : {})}\n className={cn(\n \"inline-flex items-center rounded-md border bg-background align-middle\",\n includeActive\n ? \"border-green-500/50 bg-green-500/5\"\n : excludeActive\n ? \"border-red-500/50 bg-red-500/5\"\n : \"border-border\",\n )}\n >\n {tag.key && !compact ? (\n <Badge\n size=\"xs\"\n variant=\"label\"\n tone=\"neutral\"\n label={tag.key}\n value={tag.value}\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n className=\"border-transparent shadow-none\"\n />\n ) : (\n <Badge\n size=\"xs\"\n variant=\"soft\"\n tone=\"neutral\"\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n >\n {tag.value}\n </Badge>\n )}\n </span>\n );\n\n const toolbar = (\n <span className=\"inline-flex items-center gap-0.5\">\n <TagActionButton\n ariaLabel={`Include ${tag.display}`}\n onClick={onPlus}\n active={includeActive}\n activeClassName=\"bg-green-500/20 text-green-700 dark:text-green-400\"\n icon={UiZoomIn}\n />\n <TagActionButton\n ariaLabel={`Exclude ${tag.display}`}\n onClick={onMinus}\n active={excludeActive}\n activeClassName=\"bg-red-500/20 text-red-700 dark:text-red-400\"\n icon={UiZoomOut}\n />\n <TagActionButton ariaLabel={`Copy ${tag.display}`} onClick={onCopy} icon={UiCopy} />\n </span>\n );\n\n if (actionMode === \"inline\") {\n return (\n <span className=\"inline-flex items-center gap-0.5 rounded-md border border-border bg-background pr-0.5\">\n {badge}\n {toolbar}\n </span>\n );\n }\n\n // Action icons live in a hover popover above the tag — the cell stays\n // clean, and hovering reveals the magnifier-plus / magnifier-minus / copy\n // toolbar.\n return (\n <HoverCard placement=\"top\" delay={120} arrow={false} trigger={badge} cardClassName=\"!p-1\">\n {toolbar}\n </HoverCard>\n );\n}\n\nfunction TagActionButton({\n ariaLabel,\n onClick,\n active,\n activeClassName,\n icon,\n}: {\n ariaLabel: string;\n onClick: (event: React.MouseEvent) => void;\n active?: boolean;\n activeClassName?: string;\n icon: StaticIconComponent;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n onClick={onClick}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n active && activeClassName,\n )}\n >\n <Icon icon={icon} className=\"text-xs\" />\n </button>\n );\n}\n\nexport function TagList({\n tags,\n maxVisible = 3,\n className,\n actions = \"hover\",\n compact,\n wrap = false,\n}: TagListProps) {\n const density = useDensityValue();\n const isCompact = compact ?? density === \"compact\";\n\n if (tags.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>;\n }\n\n if (wrap) {\n return (\n <span className={cn(\"flex min-w-0 flex-wrap items-center gap-1\", className)}>\n {tags.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n );\n }\n\n const visible = tags.slice(0, maxVisible);\n const overflow = tags.slice(maxVisible);\n\n return (\n <span className={cn(\"flex min-w-0 items-center gap-1\", className)}>\n <span className=\"flex min-w-0 flex-1 flex-nowrap items-center gap-1 overflow-hidden\">\n {visible.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n {overflow.length > 0 && (\n <HoverCard\n placement=\"top\"\n trigger={\n <Badge\n size=\"xs\"\n variant=\"outlined\"\n tone=\"neutral\"\n clickToCopy={false}\n className=\"shrink-0\"\n >\n +{overflow.length}\n </Badge>\n }\n cardClassName=\"min-w-72 max-w-[90vw] whitespace-normal !p-0\"\n >\n <TagPropertiesList tags={overflow} />\n </HoverCard>\n )}\n </span>\n );\n}\n\nexport function TagPropertiesList({\n tags,\n rowClassName = \"grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]\",\n}: {\n tags: NormalizedTag[];\n rowClassName?: string;\n}) {\n const tagActions = useTagActions();\n const items = useMemo<PropertiesItem<NormalizedTag>[]>(\n () => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),\n [tags],\n );\n\n return (\n <Properties<NormalizedTag>\n items={items}\n density=\"compact\"\n className=\"border-0 bg-transparent\"\n rowClassName={rowClassName}\n renderLabel={(_key, tag) =>\n tag.key ? (\n <span className=\"font-mono\">{tag.key}</span>\n ) : (\n <span className=\"italic text-muted-foreground\">tag</span>\n )\n }\n renderValue={(_key, tag) => (\n <span className=\"block truncate font-mono\" title={tag.value}>\n {tag.value}\n </span>\n )}\n suffixActions={[\n {\n id: \"include\",\n icon: UiZoomIn,\n label: (_key, tag) => `Include ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleInclude(tag.token),\n },\n {\n id: \"exclude\",\n icon: UiZoomOut,\n label: (_key, tag) => `Exclude ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleExclude(tag.token),\n },\n {\n id: \"copy\",\n icon: UiCopy,\n label: (_key, tag) => `Copy ${tag.display}`,\n onClick: (_key, tag) => copyToClipboard(tag.value),\n },\n ]}\n />\n );\n}\n\n/**\n * Builds a TagActionsContextValue from a flat record-of-modes (the same\n * shape used by FilterBar's multi/nested-multi filters). Useful when\n * connecting DataTable's multiFilters state to the in-cell + / − buttons.\n */\nexport function tagActionsFromRecord(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return {\n getMode: (token) => value[token] ?? \"neutral\",\n toggleInclude: (token) => {\n const next = { ...value };\n if (next[token] === \"include\") delete next[token];\n else next[token] = \"include\";\n onChange(next);\n },\n toggleExclude: (token) => {\n const next = { ...value };\n if (next[token] === \"exclude\") delete next[token];\n else next[token] = \"exclude\";\n onChange(next);\n },\n };\n}\n\n// Memoizing the context value avoids re-rendering every TagList cell on\n// unrelated state changes.\nexport function useTagActionsValue(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return useMemo(() => tagActionsFromRecord(value, onChange), [value, onChange]);\n}\n"],"names":["key","value","createContext","jsx","useContext","useCallback","cn","Badge","jsxs","UiZoomIn","UiZoomOut","UiCopy","HoverCard","Icon","useDensityValue","useMemo","Properties"],"mappings":"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"}
|
|
@@ -61,8 +61,18 @@ export type TagListProps = {
|
|
|
61
61
|
* (via {@link useDensityValue}) is `"compact"`.
|
|
62
62
|
*/
|
|
63
63
|
compact?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* When `true`, tag badges wrap onto multiple lines instead of staying on a
|
|
66
|
+
* single row with a `+N` overflow popover. Used by detail/expanded views
|
|
67
|
+
* (e.g. LogsTable row details) where vertical space is available.
|
|
68
|
+
*/
|
|
69
|
+
wrap?: boolean;
|
|
64
70
|
};
|
|
65
|
-
export declare function TagList({ tags, maxVisible, className, actions, compact, }: TagListProps): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
export declare function TagList({ tags, maxVisible, className, actions, compact, wrap, }: TagListProps): import("react/jsx-runtime").JSX.Element;
|
|
72
|
+
export declare function TagPropertiesList({ tags, rowClassName, }: {
|
|
73
|
+
tags: NormalizedTag[];
|
|
74
|
+
rowClassName?: string;
|
|
75
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
66
76
|
/**
|
|
67
77
|
* Builds a TagActionsContextValue from a flat record-of-modes (the same
|
|
68
78
|
* shape used by FilterBar's multi/nested-multi filters). Useful when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagList.d.ts","sourceRoot":"","sources":["../../../src/data/cells/TagList.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjG,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,SAAS,GACjB,QAAQ,EAAE,GACV,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,GAChD,IAAI,GACJ,SAAS,CAAC;AAEd,MAAM,MAAM,WAAW,GAAG;IACxB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,SAAM,GAAG,aAAa,EAAE,CAwBhF;AA+CD,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,SAAM,GAAG,MAAM,EAAE,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAO5F;AAID,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;AAEvE,MAAM,MAAM,sBAAsB,GAAG;IACnC,sFAAsF;IACtF,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC;IACvC,2DAA2D;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,2DAA2D;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAUF,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,sBAAsB,CAAC;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAEA;AAED,wBAAgB,aAAa,IAAI,sBAAsB,CAEtD;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,iCAAiC;IACjC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TagList.d.ts","sourceRoot":"","sources":["../../../src/data/cells/TagList.tsx"],"names":[],"mappings":"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"}
|
|
@@ -218,13 +218,25 @@ function TagList({
|
|
|
218
218
|
maxVisible = 3,
|
|
219
219
|
className,
|
|
220
220
|
actions = "hover",
|
|
221
|
-
compact
|
|
221
|
+
compact,
|
|
222
|
+
wrap = false
|
|
222
223
|
}) {
|
|
223
224
|
const density = useDensityValue();
|
|
224
225
|
const isCompact = compact ?? density === "compact";
|
|
225
226
|
if (tags.length === 0) {
|
|
226
227
|
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "—" });
|
|
227
228
|
}
|
|
229
|
+
if (wrap) {
|
|
230
|
+
return /* @__PURE__ */ jsx("span", { className: cn("flex min-w-0 flex-wrap items-center gap-1", className), children: tags.map((tag, index) => /* @__PURE__ */ jsx(
|
|
231
|
+
TagBadge,
|
|
232
|
+
{
|
|
233
|
+
tag,
|
|
234
|
+
actions,
|
|
235
|
+
compact: isCompact
|
|
236
|
+
},
|
|
237
|
+
`${tag.display}-${index}`
|
|
238
|
+
)) });
|
|
239
|
+
}
|
|
228
240
|
const visible = tags.slice(0, maxVisible);
|
|
229
241
|
const overflow = tags.slice(maxVisible);
|
|
230
242
|
return /* @__PURE__ */ jsxs("span", { className: cn("flex min-w-0 items-center gap-1", className), children: [
|
|
@@ -261,7 +273,10 @@ function TagList({
|
|
|
261
273
|
)
|
|
262
274
|
] });
|
|
263
275
|
}
|
|
264
|
-
function TagPropertiesList({
|
|
276
|
+
function TagPropertiesList({
|
|
277
|
+
tags,
|
|
278
|
+
rowClassName = "grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]"
|
|
279
|
+
}) {
|
|
265
280
|
const tagActions = useTagActions();
|
|
266
281
|
const items = useMemo(
|
|
267
282
|
() => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),
|
|
@@ -273,7 +288,7 @@ function TagPropertiesList({ tags }) {
|
|
|
273
288
|
items,
|
|
274
289
|
density: "compact",
|
|
275
290
|
className: "border-0 bg-transparent",
|
|
276
|
-
rowClassName
|
|
291
|
+
rowClassName,
|
|
277
292
|
renderLabel: (_key, tag) => tag.key ? /* @__PURE__ */ jsx("span", { className: "font-mono", children: tag.key }) : /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: "tag" }),
|
|
278
293
|
renderValue: (_key, tag) => /* @__PURE__ */ jsx("span", { className: "block truncate font-mono", title: tag.value, children: tag.value }),
|
|
279
294
|
suffixActions: [
|
|
@@ -322,6 +337,7 @@ function useTagActionsValue(value, onChange) {
|
|
|
322
337
|
export {
|
|
323
338
|
TagActionsProvider,
|
|
324
339
|
TagList,
|
|
340
|
+
TagPropertiesList,
|
|
325
341
|
normalizeTags,
|
|
326
342
|
splitTagToken,
|
|
327
343
|
tagActionsFromRecord,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagList.js","sources":["../../../src/data/cells/TagList.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo } from \"react\";\nimport { Badge } from \"../Badge\";\nimport { HoverCard } from \"../../overlay/HoverCard\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { Properties, type PropertiesItem } from \"../Properties\";\nimport { UiCopy, UiZoomIn, UiZoomOut } from \"../../icons\";\nimport { useDensityValue } from \"../../hooks/use-density\";\nimport { cn } from \"../../lib/utils\";\nimport type { FilterMode } from \"../FilterPill\";\n\nexport type TagInput = string | { key: string; value: string } | { name: string; value: string };\n\nexport type NormalizedTag = {\n key?: string;\n value: string;\n display: string;\n token: string;\n};\n\nexport type TagsValue =\n | TagInput[]\n | Record<string, string | number | boolean | null>\n | null\n | undefined;\n\nexport type TagsOptions = {\n /** Number of tags to show inline before using the `+N` overflow popover. */\n maxVisible?: number;\n /** Separator between tag keys and values. Defaults to `=`. */\n separator?: string;\n};\n\nexport function normalizeTags(value: TagsValue, separator = \"=\"): NormalizedTag[] {\n if (value == null) return [];\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeOne(entry, separator)).filter((tag) => tag.value !== \"\");\n }\n\n if (typeof value === \"object\") {\n return Object.entries(value)\n .map(([key, raw]): NormalizedTag | null => {\n if (raw == null) return null;\n const valueStr = String(raw).trim();\n if (!valueStr) return null;\n return {\n key,\n value: valueStr,\n display: `${key}${separator}${valueStr}`,\n token: `${key}${separator}${valueStr}`,\n } satisfies NormalizedTag;\n })\n .filter((tag): tag is NormalizedTag => tag !== null);\n }\n\n return [];\n}\n\nfunction normalizeOne(entry: TagInput, separator: string): NormalizedTag {\n if (typeof entry === \"string\") {\n const trimmed = entry.trim();\n const eq = trimmed.indexOf(\"=\");\n const colon = trimmed.indexOf(\":\");\n const splitAt =\n eq >= 0 && (colon < 0 || eq < colon)\n ? eq\n : colon >= 0 && colon < trimmed.length - 1\n ? colon\n : -1;\n\n if (splitAt > 0) {\n const key = trimmed.slice(0, splitAt);\n const value = trimmed.slice(splitAt + 1);\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n\n return { value: trimmed, display: trimmed, token: trimmed };\n }\n\n const key =\n \"key\" in entry && typeof entry.key === \"string\"\n ? entry.key\n : \"name\" in entry && typeof entry.name === \"string\"\n ? entry.name\n : undefined;\n const value = String(entry.value ?? \"\").trim();\n\n if (key) {\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n return { value, display: value, token: value };\n}\n\nexport function tagFilterTokens(value: TagsValue, separator = \"=\"): string[] {\n return normalizeTags(value, separator).map((tag) => tag.token);\n}\n\n/**\n * Splits a token previously produced by `normalizeTags`/`tagFilterTokens`\n * back into its key and value parts using the same separator. Tokens\n * without a separator (bare tags) return `{ key: \"\", value: token }`.\n */\nexport function splitTagToken(token: string, separator = \"=\"): { key: string; value: string } {\n const idx = token.indexOf(separator);\n if (idx <= 0) return { key: \"\", value: token };\n return {\n key: token.slice(0, idx),\n value: token.slice(idx + separator.length),\n };\n}\n\n// Filter mode mapping (re-using FilterPill's tri-state vocabulary so the wire\n// shape matches DataTable's existing multiFilters state).\nexport type TagFilterMode = Extract<FilterMode, \"include\" | \"exclude\">;\n\nexport type TagActionsContextValue = {\n /** Returns the current mode for a token; \"neutral\" if the user hasn't acted on it. */\n getMode: (token: string) => FilterMode;\n /** Toggle include for this token (off → include → off). */\n toggleInclude: (token: string) => void;\n /** Toggle exclude for this token (off → exclude → off). */\n toggleExclude: (token: string) => void;\n};\n\nconst noop: TagActionsContextValue = {\n getMode: () => \"neutral\",\n toggleInclude: () => {},\n toggleExclude: () => {},\n};\n\nconst TagActionsContext = createContext<TagActionsContextValue>(noop);\n\nexport function TagActionsProvider({\n value,\n children,\n}: {\n value: TagActionsContextValue;\n children: React.ReactNode;\n}) {\n return <TagActionsContext.Provider value={value}>{children}</TagActionsContext.Provider>;\n}\n\nexport function useTagActions(): TagActionsContextValue {\n return useContext(TagActionsContext);\n}\n\nexport type TagListProps = {\n /** Normalized tags to render. */\n tags: NormalizedTag[];\n /** Number of tags to show inline before the overflow popover. */\n maxVisible?: number;\n /** Classes applied to the tag row. */\n className?: string;\n /** Whether copy/zoom actions appear on hover or inline. */\n actions?: \"hover\" | \"inline\";\n /**\n * When `true`, visible tag badges render only their value — the key is\n * dropped from the inline display. The full `key=value` text is still\n * available in `tag.display` (used for tooltips, copy actions, and the\n * overflow `+N` popover). Defaults to whether the surrounding density\n * (via {@link useDensityValue}) is `\"compact\"`.\n */\n compact?: boolean;\n};\n\nfunction copyToClipboard(text: string) {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => undefined);\n }\n}\n\nfunction TagBadge({\n tag,\n actions: actionMode,\n compact = false,\n}: {\n tag: NormalizedTag;\n actions: \"hover\" | \"inline\";\n compact?: boolean;\n}) {\n const actions = useTagActions();\n const mode = actions.getMode(tag.token);\n\n const onPlus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleInclude(tag.token);\n },\n [actions, tag.token],\n );\n const onMinus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleExclude(tag.token);\n },\n [actions, tag.token],\n );\n const onCopy = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n copyToClipboard(tag.value);\n },\n [tag.value],\n );\n\n // Active state colors mirror FilterPill: include = green, exclude = red.\n const includeActive = mode === \"include\";\n const excludeActive = mode === \"exclude\";\n\n const showTooltip = compact && !!tag.key;\n const badge = (\n <span\n {...(showTooltip ? { title: tag.display } : {})}\n className={cn(\n \"inline-flex items-center rounded-md border bg-background align-middle\",\n includeActive\n ? \"border-green-500/50 bg-green-500/5\"\n : excludeActive\n ? \"border-red-500/50 bg-red-500/5\"\n : \"border-border\",\n )}\n >\n {tag.key && !compact ? (\n <Badge\n size=\"xs\"\n variant=\"label\"\n tone=\"neutral\"\n label={tag.key}\n value={tag.value}\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n className=\"border-transparent shadow-none\"\n />\n ) : (\n <Badge\n size=\"xs\"\n variant=\"soft\"\n tone=\"neutral\"\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n >\n {tag.value}\n </Badge>\n )}\n </span>\n );\n\n const toolbar = (\n <span className=\"inline-flex items-center gap-0.5\">\n <TagActionButton\n ariaLabel={`Include ${tag.display}`}\n onClick={onPlus}\n active={includeActive}\n activeClassName=\"bg-green-500/20 text-green-700 dark:text-green-400\"\n icon={UiZoomIn}\n />\n <TagActionButton\n ariaLabel={`Exclude ${tag.display}`}\n onClick={onMinus}\n active={excludeActive}\n activeClassName=\"bg-red-500/20 text-red-700 dark:text-red-400\"\n icon={UiZoomOut}\n />\n <TagActionButton ariaLabel={`Copy ${tag.display}`} onClick={onCopy} icon={UiCopy} />\n </span>\n );\n\n if (actionMode === \"inline\") {\n return (\n <span className=\"inline-flex items-center gap-0.5 rounded-md border border-border bg-background pr-0.5\">\n {badge}\n {toolbar}\n </span>\n );\n }\n\n // Action icons live in a hover popover above the tag — the cell stays\n // clean, and hovering reveals the magnifier-plus / magnifier-minus / copy\n // toolbar.\n return (\n <HoverCard placement=\"top\" delay={120} arrow={false} trigger={badge} cardClassName=\"!p-1\">\n {toolbar}\n </HoverCard>\n );\n}\n\nfunction TagActionButton({\n ariaLabel,\n onClick,\n active,\n activeClassName,\n icon,\n}: {\n ariaLabel: string;\n onClick: (event: React.MouseEvent) => void;\n active?: boolean;\n activeClassName?: string;\n icon: StaticIconComponent;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n onClick={onClick}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n active && activeClassName,\n )}\n >\n <Icon icon={icon} className=\"text-xs\" />\n </button>\n );\n}\n\nexport function TagList({\n tags,\n maxVisible = 3,\n className,\n actions = \"hover\",\n compact,\n}: TagListProps) {\n const density = useDensityValue();\n const isCompact = compact ?? density === \"compact\";\n\n if (tags.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>;\n }\n\n const visible = tags.slice(0, maxVisible);\n const overflow = tags.slice(maxVisible);\n\n return (\n <span className={cn(\"flex min-w-0 items-center gap-1\", className)}>\n <span className=\"flex min-w-0 flex-1 flex-nowrap items-center gap-1 overflow-hidden\">\n {visible.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n {overflow.length > 0 && (\n <HoverCard\n placement=\"top\"\n trigger={\n <Badge\n size=\"xs\"\n variant=\"outlined\"\n tone=\"neutral\"\n clickToCopy={false}\n className=\"shrink-0\"\n >\n +{overflow.length}\n </Badge>\n }\n cardClassName=\"min-w-72 max-w-[90vw] whitespace-normal !p-0\"\n >\n <TagPropertiesList tags={overflow} />\n </HoverCard>\n )}\n </span>\n );\n}\n\nfunction TagPropertiesList({ tags }: { tags: NormalizedTag[] }) {\n const tagActions = useTagActions();\n const items = useMemo<PropertiesItem<NormalizedTag>[]>(\n () => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),\n [tags],\n );\n\n return (\n <Properties<NormalizedTag>\n items={items}\n density=\"compact\"\n className=\"border-0 bg-transparent\"\n rowClassName=\"grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]\"\n renderLabel={(_key, tag) =>\n tag.key ? (\n <span className=\"font-mono\">{tag.key}</span>\n ) : (\n <span className=\"italic text-muted-foreground\">tag</span>\n )\n }\n renderValue={(_key, tag) => (\n <span className=\"block truncate font-mono\" title={tag.value}>\n {tag.value}\n </span>\n )}\n suffixActions={[\n {\n id: \"include\",\n icon: UiZoomIn,\n label: (_key, tag) => `Include ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleInclude(tag.token),\n },\n {\n id: \"exclude\",\n icon: UiZoomOut,\n label: (_key, tag) => `Exclude ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleExclude(tag.token),\n },\n {\n id: \"copy\",\n icon: UiCopy,\n label: (_key, tag) => `Copy ${tag.display}`,\n onClick: (_key, tag) => copyToClipboard(tag.value),\n },\n ]}\n />\n );\n}\n\n/**\n * Builds a TagActionsContextValue from a flat record-of-modes (the same\n * shape used by FilterBar's multi/nested-multi filters). Useful when\n * connecting DataTable's multiFilters state to the in-cell + / − buttons.\n */\nexport function tagActionsFromRecord(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return {\n getMode: (token) => value[token] ?? \"neutral\",\n toggleInclude: (token) => {\n const next = { ...value };\n if (next[token] === \"include\") delete next[token];\n else next[token] = \"include\";\n onChange(next);\n },\n toggleExclude: (token) => {\n const next = { ...value };\n if (next[token] === \"exclude\") delete next[token];\n else next[token] = \"exclude\";\n onChange(next);\n },\n };\n}\n\n// Memoizing the context value avoids re-rendering every TagList cell on\n// unrelated state changes.\nexport function useTagActionsValue(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return useMemo(() => tagActionsFromRecord(value, onChange), [value, onChange]);\n}\n"],"names":["key","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TagList.js","sources":["../../../src/data/cells/TagList.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo } from \"react\";\nimport { Badge } from \"../Badge\";\nimport { HoverCard } from \"../../overlay/HoverCard\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { Properties, type PropertiesItem } from \"../Properties\";\nimport { UiCopy, UiZoomIn, UiZoomOut } from \"../../icons\";\nimport { useDensityValue } from \"../../hooks/use-density\";\nimport { cn } from \"../../lib/utils\";\nimport type { FilterMode } from \"../FilterPill\";\n\nexport type TagInput = string | { key: string; value: string } | { name: string; value: string };\n\nexport type NormalizedTag = {\n key?: string;\n value: string;\n display: string;\n token: string;\n};\n\nexport type TagsValue =\n | TagInput[]\n | Record<string, string | number | boolean | null>\n | null\n | undefined;\n\nexport type TagsOptions = {\n /** Number of tags to show inline before using the `+N` overflow popover. */\n maxVisible?: number;\n /** Separator between tag keys and values. Defaults to `=`. */\n separator?: string;\n};\n\nexport function normalizeTags(value: TagsValue, separator = \"=\"): NormalizedTag[] {\n if (value == null) return [];\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeOne(entry, separator)).filter((tag) => tag.value !== \"\");\n }\n\n if (typeof value === \"object\") {\n return Object.entries(value)\n .map(([key, raw]): NormalizedTag | null => {\n if (raw == null) return null;\n const valueStr = String(raw).trim();\n if (!valueStr) return null;\n return {\n key,\n value: valueStr,\n display: `${key}${separator}${valueStr}`,\n token: `${key}${separator}${valueStr}`,\n } satisfies NormalizedTag;\n })\n .filter((tag): tag is NormalizedTag => tag !== null);\n }\n\n return [];\n}\n\nfunction normalizeOne(entry: TagInput, separator: string): NormalizedTag {\n if (typeof entry === \"string\") {\n const trimmed = entry.trim();\n const eq = trimmed.indexOf(\"=\");\n const colon = trimmed.indexOf(\":\");\n const splitAt =\n eq >= 0 && (colon < 0 || eq < colon)\n ? eq\n : colon >= 0 && colon < trimmed.length - 1\n ? colon\n : -1;\n\n if (splitAt > 0) {\n const key = trimmed.slice(0, splitAt);\n const value = trimmed.slice(splitAt + 1);\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n\n return { value: trimmed, display: trimmed, token: trimmed };\n }\n\n const key =\n \"key\" in entry && typeof entry.key === \"string\"\n ? entry.key\n : \"name\" in entry && typeof entry.name === \"string\"\n ? entry.name\n : undefined;\n const value = String(entry.value ?? \"\").trim();\n\n if (key) {\n return {\n key,\n value,\n display: `${key}${separator}${value}`,\n token: `${key}${separator}${value}`,\n };\n }\n return { value, display: value, token: value };\n}\n\nexport function tagFilterTokens(value: TagsValue, separator = \"=\"): string[] {\n return normalizeTags(value, separator).map((tag) => tag.token);\n}\n\n/**\n * Splits a token previously produced by `normalizeTags`/`tagFilterTokens`\n * back into its key and value parts using the same separator. Tokens\n * without a separator (bare tags) return `{ key: \"\", value: token }`.\n */\nexport function splitTagToken(token: string, separator = \"=\"): { key: string; value: string } {\n const idx = token.indexOf(separator);\n if (idx <= 0) return { key: \"\", value: token };\n return {\n key: token.slice(0, idx),\n value: token.slice(idx + separator.length),\n };\n}\n\n// Filter mode mapping (re-using FilterPill's tri-state vocabulary so the wire\n// shape matches DataTable's existing multiFilters state).\nexport type TagFilterMode = Extract<FilterMode, \"include\" | \"exclude\">;\n\nexport type TagActionsContextValue = {\n /** Returns the current mode for a token; \"neutral\" if the user hasn't acted on it. */\n getMode: (token: string) => FilterMode;\n /** Toggle include for this token (off → include → off). */\n toggleInclude: (token: string) => void;\n /** Toggle exclude for this token (off → exclude → off). */\n toggleExclude: (token: string) => void;\n};\n\nconst noop: TagActionsContextValue = {\n getMode: () => \"neutral\",\n toggleInclude: () => {},\n toggleExclude: () => {},\n};\n\nconst TagActionsContext = createContext<TagActionsContextValue>(noop);\n\nexport function TagActionsProvider({\n value,\n children,\n}: {\n value: TagActionsContextValue;\n children: React.ReactNode;\n}) {\n return <TagActionsContext.Provider value={value}>{children}</TagActionsContext.Provider>;\n}\n\nexport function useTagActions(): TagActionsContextValue {\n return useContext(TagActionsContext);\n}\n\nexport type TagListProps = {\n /** Normalized tags to render. */\n tags: NormalizedTag[];\n /** Number of tags to show inline before the overflow popover. */\n maxVisible?: number;\n /** Classes applied to the tag row. */\n className?: string;\n /** Whether copy/zoom actions appear on hover or inline. */\n actions?: \"hover\" | \"inline\";\n /**\n * When `true`, visible tag badges render only their value — the key is\n * dropped from the inline display. The full `key=value` text is still\n * available in `tag.display` (used for tooltips, copy actions, and the\n * overflow `+N` popover). Defaults to whether the surrounding density\n * (via {@link useDensityValue}) is `\"compact\"`.\n */\n compact?: boolean;\n /**\n * When `true`, tag badges wrap onto multiple lines instead of staying on a\n * single row with a `+N` overflow popover. Used by detail/expanded views\n * (e.g. LogsTable row details) where vertical space is available.\n */\n wrap?: boolean;\n};\n\nfunction copyToClipboard(text: string) {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => undefined);\n }\n}\n\nfunction TagBadge({\n tag,\n actions: actionMode,\n compact = false,\n}: {\n tag: NormalizedTag;\n actions: \"hover\" | \"inline\";\n compact?: boolean;\n}) {\n const actions = useTagActions();\n const mode = actions.getMode(tag.token);\n\n const onPlus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleInclude(tag.token);\n },\n [actions, tag.token],\n );\n const onMinus = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n actions.toggleExclude(tag.token);\n },\n [actions, tag.token],\n );\n const onCopy = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n copyToClipboard(tag.value);\n },\n [tag.value],\n );\n\n // Active state colors mirror FilterPill: include = green, exclude = red.\n const includeActive = mode === \"include\";\n const excludeActive = mode === \"exclude\";\n\n const showTooltip = compact && !!tag.key;\n const badge = (\n <span\n {...(showTooltip ? { title: tag.display } : {})}\n className={cn(\n \"inline-flex items-center rounded-md border bg-background align-middle\",\n includeActive\n ? \"border-green-500/50 bg-green-500/5\"\n : excludeActive\n ? \"border-red-500/50 bg-red-500/5\"\n : \"border-border\",\n )}\n >\n {tag.key && !compact ? (\n <Badge\n size=\"xs\"\n variant=\"label\"\n tone=\"neutral\"\n label={tag.key}\n value={tag.value}\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n className=\"border-transparent shadow-none\"\n />\n ) : (\n <Badge\n size=\"xs\"\n variant=\"soft\"\n tone=\"neutral\"\n maxWidth={32}\n truncate=\"auto\"\n clickToCopy={false}\n >\n {tag.value}\n </Badge>\n )}\n </span>\n );\n\n const toolbar = (\n <span className=\"inline-flex items-center gap-0.5\">\n <TagActionButton\n ariaLabel={`Include ${tag.display}`}\n onClick={onPlus}\n active={includeActive}\n activeClassName=\"bg-green-500/20 text-green-700 dark:text-green-400\"\n icon={UiZoomIn}\n />\n <TagActionButton\n ariaLabel={`Exclude ${tag.display}`}\n onClick={onMinus}\n active={excludeActive}\n activeClassName=\"bg-red-500/20 text-red-700 dark:text-red-400\"\n icon={UiZoomOut}\n />\n <TagActionButton ariaLabel={`Copy ${tag.display}`} onClick={onCopy} icon={UiCopy} />\n </span>\n );\n\n if (actionMode === \"inline\") {\n return (\n <span className=\"inline-flex items-center gap-0.5 rounded-md border border-border bg-background pr-0.5\">\n {badge}\n {toolbar}\n </span>\n );\n }\n\n // Action icons live in a hover popover above the tag — the cell stays\n // clean, and hovering reveals the magnifier-plus / magnifier-minus / copy\n // toolbar.\n return (\n <HoverCard placement=\"top\" delay={120} arrow={false} trigger={badge} cardClassName=\"!p-1\">\n {toolbar}\n </HoverCard>\n );\n}\n\nfunction TagActionButton({\n ariaLabel,\n onClick,\n active,\n activeClassName,\n icon,\n}: {\n ariaLabel: string;\n onClick: (event: React.MouseEvent) => void;\n active?: boolean;\n activeClassName?: string;\n icon: StaticIconComponent;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n onClick={onClick}\n className={cn(\n \"inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground\",\n active && activeClassName,\n )}\n >\n <Icon icon={icon} className=\"text-xs\" />\n </button>\n );\n}\n\nexport function TagList({\n tags,\n maxVisible = 3,\n className,\n actions = \"hover\",\n compact,\n wrap = false,\n}: TagListProps) {\n const density = useDensityValue();\n const isCompact = compact ?? density === \"compact\";\n\n if (tags.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>;\n }\n\n if (wrap) {\n return (\n <span className={cn(\"flex min-w-0 flex-wrap items-center gap-1\", className)}>\n {tags.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n );\n }\n\n const visible = tags.slice(0, maxVisible);\n const overflow = tags.slice(maxVisible);\n\n return (\n <span className={cn(\"flex min-w-0 items-center gap-1\", className)}>\n <span className=\"flex min-w-0 flex-1 flex-nowrap items-center gap-1 overflow-hidden\">\n {visible.map((tag, index) => (\n <TagBadge\n key={`${tag.display}-${index}`}\n tag={tag}\n actions={actions}\n compact={isCompact}\n />\n ))}\n </span>\n {overflow.length > 0 && (\n <HoverCard\n placement=\"top\"\n trigger={\n <Badge\n size=\"xs\"\n variant=\"outlined\"\n tone=\"neutral\"\n clickToCopy={false}\n className=\"shrink-0\"\n >\n +{overflow.length}\n </Badge>\n }\n cardClassName=\"min-w-72 max-w-[90vw] whitespace-normal !p-0\"\n >\n <TagPropertiesList tags={overflow} />\n </HoverCard>\n )}\n </span>\n );\n}\n\nexport function TagPropertiesList({\n tags,\n rowClassName = \"grid-cols-[minmax(4rem,8rem)_minmax(0,1fr)]\",\n}: {\n tags: NormalizedTag[];\n rowClassName?: string;\n}) {\n const tagActions = useTagActions();\n const items = useMemo<PropertiesItem<NormalizedTag>[]>(\n () => tags.map((tag, index) => ({ key: `${tag.token}-${index}`, value: tag })),\n [tags],\n );\n\n return (\n <Properties<NormalizedTag>\n items={items}\n density=\"compact\"\n className=\"border-0 bg-transparent\"\n rowClassName={rowClassName}\n renderLabel={(_key, tag) =>\n tag.key ? (\n <span className=\"font-mono\">{tag.key}</span>\n ) : (\n <span className=\"italic text-muted-foreground\">tag</span>\n )\n }\n renderValue={(_key, tag) => (\n <span className=\"block truncate font-mono\" title={tag.value}>\n {tag.value}\n </span>\n )}\n suffixActions={[\n {\n id: \"include\",\n icon: UiZoomIn,\n label: (_key, tag) => `Include ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleInclude(tag.token),\n },\n {\n id: \"exclude\",\n icon: UiZoomOut,\n label: (_key, tag) => `Exclude ${tag.display}`,\n onClick: (_key, tag) => tagActions.toggleExclude(tag.token),\n },\n {\n id: \"copy\",\n icon: UiCopy,\n label: (_key, tag) => `Copy ${tag.display}`,\n onClick: (_key, tag) => copyToClipboard(tag.value),\n },\n ]}\n />\n );\n}\n\n/**\n * Builds a TagActionsContextValue from a flat record-of-modes (the same\n * shape used by FilterBar's multi/nested-multi filters). Useful when\n * connecting DataTable's multiFilters state to the in-cell + / − buttons.\n */\nexport function tagActionsFromRecord(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return {\n getMode: (token) => value[token] ?? \"neutral\",\n toggleInclude: (token) => {\n const next = { ...value };\n if (next[token] === \"include\") delete next[token];\n else next[token] = \"include\";\n onChange(next);\n },\n toggleExclude: (token) => {\n const next = { ...value };\n if (next[token] === \"exclude\") delete next[token];\n else next[token] = \"exclude\";\n onChange(next);\n },\n };\n}\n\n// Memoizing the context value avoids re-rendering every TagList cell on\n// unrelated state changes.\nexport function useTagActionsValue(\n value: Record<string, TagFilterMode>,\n onChange: (next: Record<string, TagFilterMode>) => void,\n): TagActionsContextValue {\n return useMemo(() => tagActionsFromRecord(value, onChange), [value, onChange]);\n}\n"],"names":["key","value"],"mappings":"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"}
|
|
@@ -118,7 +118,7 @@ function chooseTimestampFormat(values) {
|
|
|
118
118
|
function formatTimestamp(date, format, now = /* @__PURE__ */ new Date()) {
|
|
119
119
|
switch (format) {
|
|
120
120
|
case "relative":
|
|
121
|
-
return
|
|
121
|
+
return formatRelativeTime(date, now);
|
|
122
122
|
case "time":
|
|
123
123
|
return date.toLocaleTimeString(void 0, {
|
|
124
124
|
hour: "2-digit",
|
|
@@ -145,7 +145,7 @@ function formatIso(date) {
|
|
|
145
145
|
const min = String(date.getMinutes()).padStart(2, "0");
|
|
146
146
|
return `${yyyy}-${mm}-${dd} ${hh}:${min}`;
|
|
147
147
|
}
|
|
148
|
-
function
|
|
148
|
+
function formatRelativeTime(date, now = /* @__PURE__ */ new Date()) {
|
|
149
149
|
const diffMs = now.getTime() - date.getTime();
|
|
150
150
|
const future = diffMs < 0;
|
|
151
151
|
const abs = Math.abs(diffMs);
|
|
@@ -159,6 +159,19 @@ function formatRelative(date, now) {
|
|
|
159
159
|
if (value === "just now") return value;
|
|
160
160
|
return future ? `in ${value}` : `${value} ago`;
|
|
161
161
|
}
|
|
162
|
+
function formatDateTimeRelative(value, now = /* @__PURE__ */ new Date()) {
|
|
163
|
+
const parsed = parseTimestamp(value);
|
|
164
|
+
if (!parsed) return typeof value === "string" ? value : "";
|
|
165
|
+
const absolute = parsed.toLocaleString(void 0, {
|
|
166
|
+
year: "numeric",
|
|
167
|
+
month: "short",
|
|
168
|
+
day: "numeric",
|
|
169
|
+
hour: "2-digit",
|
|
170
|
+
minute: "2-digit"
|
|
171
|
+
});
|
|
172
|
+
const relative = formatRelativeTime(parsed, now);
|
|
173
|
+
return relative === "just now" ? absolute : `${absolute} (${relative})`;
|
|
174
|
+
}
|
|
162
175
|
function modeToFormat(mode, dataFormat) {
|
|
163
176
|
switch (mode) {
|
|
164
177
|
case "absolute":
|
|
@@ -188,6 +201,8 @@ function Timestamp({ value, format, showTitleOnHover = true, className }) {
|
|
|
188
201
|
}
|
|
189
202
|
exports.Timestamp = Timestamp;
|
|
190
203
|
exports.chooseTimestampFormat = chooseTimestampFormat;
|
|
204
|
+
exports.formatDateTimeRelative = formatDateTimeRelative;
|
|
205
|
+
exports.formatRelativeTime = formatRelativeTime;
|
|
191
206
|
exports.formatTimestamp = formatTimestamp;
|
|
192
207
|
exports.modeToFormat = modeToFormat;
|
|
193
208
|
exports.parseTimestamp = parseTimestamp;
|