@asteby/metacore-runtime-react 14.0.0 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 16.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5f864d9: Make declarative dynamic-table option badges and relation chips feel alive.
|
|
8
|
+
|
|
9
|
+
Options/select/status badges that ship without an explicit `color` from the
|
|
10
|
+
backend now get a deterministic, cohesive color derived from the option value
|
|
11
|
+
(fallback label) instead of rendering as dead gray text. Same value always maps
|
|
12
|
+
to the same hue, and equal words share a color.
|
|
13
|
+
- `@asteby/metacore-ui/lib` adds `optionColor(key)` (curated 16-hue Tailwind-500
|
|
14
|
+
palette, FNV-1a hash, light/dark safe), plus `optionColorBadgeStyles`,
|
|
15
|
+
`relationChipStyles`, and the exported `OPTION_PALETTE`.
|
|
16
|
+
- `OptionBadge` uses the explicit `color` when present, otherwise derives one via
|
|
17
|
+
`optionColor`, and renders the option's lucide `icon` before the label.
|
|
18
|
+
- `RelationCell` (resolved FK chips for category/brand/supplier/…) now gets a
|
|
19
|
+
subtle deterministic color keyed on the related label — kept lighter than enum
|
|
20
|
+
badges (soft tint, no heavy fill) so the two remain distinguishable.
|
|
21
|
+
|
|
22
|
+
All colors come from hex-derived inline styles, so they render correctly
|
|
23
|
+
regardless of the host's tailwind safelist.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [5f864d9]
|
|
28
|
+
- @asteby/metacore-ui@2.4.0
|
|
29
|
+
|
|
30
|
+
## 15.0.0
|
|
31
|
+
|
|
32
|
+
### Minor Changes
|
|
33
|
+
|
|
34
|
+
- ab41d75: Make declarative dynamic-table option badges and relation chips feel alive.
|
|
35
|
+
|
|
36
|
+
Options/select/status badges that ship without an explicit `color` from the
|
|
37
|
+
backend now get a deterministic, cohesive color derived from the option value
|
|
38
|
+
(fallback label) instead of rendering as dead gray text. Same value always maps
|
|
39
|
+
to the same hue, and equal words share a color.
|
|
40
|
+
- `@asteby/metacore-ui/lib` adds `optionColor(key)` (curated 16-hue Tailwind-500
|
|
41
|
+
palette, FNV-1a hash, light/dark safe), plus `optionColorBadgeStyles`,
|
|
42
|
+
`relationChipStyles`, and the exported `OPTION_PALETTE`.
|
|
43
|
+
- `OptionBadge` uses the explicit `color` when present, otherwise derives one via
|
|
44
|
+
`optionColor`, and renders the option's lucide `icon` before the label.
|
|
45
|
+
- `RelationCell` (resolved FK chips for category/brand/supplier/…) now gets a
|
|
46
|
+
subtle deterministic color keyed on the related label — kept lighter than enum
|
|
47
|
+
badges (soft tint, no heavy fill) so the two remain distinguishable.
|
|
48
|
+
|
|
49
|
+
All colors come from hex-derived inline styles, so they render correctly
|
|
50
|
+
regardless of the host's tailwind safelist.
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [ab41d75]
|
|
55
|
+
- @asteby/metacore-ui@2.3.0
|
|
56
|
+
|
|
3
57
|
## 14.0.0
|
|
4
58
|
|
|
5
59
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAgGD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAwHD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAStE,CAAA;AAiED;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAgmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
|
package/dist/dynamic-columns.js
CHANGED
|
@@ -19,7 +19,7 @@ import * as icons from 'lucide-react';
|
|
|
19
19
|
import { MoreHorizontal } from 'lucide-react';
|
|
20
20
|
import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Checkbox, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@asteby/metacore-ui';
|
|
21
21
|
import { DataTableColumnHeader, FilterableColumnHeader, } from '@asteby/metacore-ui/data-table';
|
|
22
|
-
import { generateBadgeStyles, getInitials } from '@asteby/metacore-ui/lib';
|
|
22
|
+
import { generateBadgeStyles, getInitials, optionColor, relationChipStyles, } from '@asteby/metacore-ui/lib';
|
|
23
23
|
import { Progress } from './dialogs/_primitives';
|
|
24
24
|
import { OptionsContext } from './options-context';
|
|
25
25
|
import { DynamicIcon } from './dynamic-icon';
|
|
@@ -169,7 +169,12 @@ const renderRelationBadges = (items, col) => {
|
|
|
169
169
|
};
|
|
170
170
|
const OptionBadge = ({ option }) => {
|
|
171
171
|
const isDark = useIsDarkTheme();
|
|
172
|
-
|
|
172
|
+
// Explicit backend color wins; otherwise derive a stable, cohesive color
|
|
173
|
+
// from the option's value (fallback label) so "dead" gray badges come
|
|
174
|
+
// alive. Inline style (hex-derived) so it works regardless of the host's
|
|
175
|
+
// tailwind safelist — addon-arbitrary classes aren't in the host scan.
|
|
176
|
+
const colorSource = option.color || optionColor(option.value || option.label);
|
|
177
|
+
const colorStyles = generateBadgeStyles(colorSource, { isDark });
|
|
173
178
|
return (_jsxs(Badge, { variant: "outline", className: "flex items-center gap-1 border-0", style: colorStyles, children: [option.icon && _jsx(DynamicIcon, { name: option.icon, className: "h-3.5 w-3.5" }), _jsx("span", { children: option.label })] }));
|
|
174
179
|
};
|
|
175
180
|
const BadgeWithEndpointOptions = ({ endpoint, value }) => {
|
|
@@ -218,10 +223,16 @@ export const resolveRelationLabel = (col, row) => {
|
|
|
218
223
|
* `belongs_to` column (category, supplier, warehouse, …) without per-addon code.
|
|
219
224
|
*/
|
|
220
225
|
const RelationCell = ({ col, row }) => {
|
|
226
|
+
const isDark = useIsDarkTheme();
|
|
221
227
|
const display = resolveRelationLabel(col, row);
|
|
222
228
|
if (!display)
|
|
223
229
|
return _jsx(EmptyCell, {});
|
|
224
|
-
|
|
230
|
+
// Deterministic, SUBTLE color keyed on the resolved label — lighter than
|
|
231
|
+
// enum badges (soft tint, no heavy fill) so category/brand chips read as
|
|
232
|
+
// alive yet stay visually distinct from option/status badges. Inline style
|
|
233
|
+
// (hex-derived) bypasses the host tailwind safelist.
|
|
234
|
+
const chipStyles = relationChipStyles(display, { isDark });
|
|
235
|
+
return (_jsx("span", { className: "inline-flex max-w-[220px] items-center truncate rounded-md px-2 py-0.5 text-sm font-medium", style: chipStyles, title: display, children: _jsx("span", { className: "truncate", children: display }) }));
|
|
225
236
|
};
|
|
226
237
|
/**
|
|
227
238
|
* Generic avatar-style cell: round/rounded photo (or initials fallback) +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"date-fns": ">=3",
|
|
35
35
|
"react-day-picker": ">=8",
|
|
36
36
|
"@asteby/metacore-sdk": "^3.1.0",
|
|
37
|
-
"@asteby/metacore-ui": "^2.
|
|
37
|
+
"@asteby/metacore-ui": "^2.4.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@tanstack/react-router": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"vitest": "^4.0.0",
|
|
63
63
|
"zustand": "^5.0.0",
|
|
64
64
|
"@asteby/metacore-sdk": "3.1.0",
|
|
65
|
-
"@asteby/metacore-ui": "2.
|
|
65
|
+
"@asteby/metacore-ui": "2.4.0"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsc -p tsconfig.json",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Determinism + spread coverage for the curated option-color palette that
|
|
2
|
+
// makes option/relation badges "alive" instead of dead gray. These utilities
|
|
3
|
+
// live in @asteby/metacore-ui/lib and are consumed by the OptionBadge and
|
|
4
|
+
// RelationCell renderers in dynamic-columns.tsx.
|
|
5
|
+
import { describe, it, expect } from 'vitest'
|
|
6
|
+
import { optionColor, OPTION_PALETTE } from '@asteby/metacore-ui/lib'
|
|
7
|
+
|
|
8
|
+
describe('optionColor', () => {
|
|
9
|
+
it('is deterministic — same input always yields the same color', () => {
|
|
10
|
+
const a = optionColor('storable')
|
|
11
|
+
const b = optionColor('storable')
|
|
12
|
+
expect(a).toBe(b)
|
|
13
|
+
// Stable across many repeats (no Math.random / time dependence).
|
|
14
|
+
for (let i = 0; i < 50; i++) {
|
|
15
|
+
expect(optionColor('in_progress')).toBe(optionColor('in_progress'))
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('normalizes case/whitespace so equal words collapse to one color', () => {
|
|
20
|
+
expect(optionColor('Active')).toBe(optionColor('active'))
|
|
21
|
+
expect(optionColor(' active ')).toBe(optionColor('active'))
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('always returns a color from the curated palette', () => {
|
|
25
|
+
const keys = [
|
|
26
|
+
'storable', 'consumable', 'service', 'active', 'inactive', 'pending',
|
|
27
|
+
'frenos', 'llantas', 'suspension', 'aceite', 'filtros', 'baterias',
|
|
28
|
+
'uuid-1234', 'category', 'brand', 'supplier', 'warehouse', '',
|
|
29
|
+
]
|
|
30
|
+
for (const key of keys) {
|
|
31
|
+
expect(OPTION_PALETTE).toContain(optionColor(key))
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('returns a 6-digit hex with no leading #', () => {
|
|
36
|
+
expect(optionColor('anything')).toMatch(/^[0-9a-f]{6}$/)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('falls back to a stable palette color for empty/nullish input', () => {
|
|
40
|
+
expect(optionColor('')).toBe(OPTION_PALETTE[0])
|
|
41
|
+
// @ts-expect-error exercising the nullish guard
|
|
42
|
+
expect(optionColor(undefined)).toBe(OPTION_PALETTE[0])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('spreads distinct inputs across the palette (not one color for all)', () => {
|
|
46
|
+
const inputs = Array.from({ length: 200 }, (_, i) => `option_${i}`)
|
|
47
|
+
const used = new Set(inputs.map(optionColor))
|
|
48
|
+
// With 16 hues and 200 distinct keys we expect broad coverage; require
|
|
49
|
+
// at least half the palette to be exercised so a degenerate hash that
|
|
50
|
+
// collapses everything is caught.
|
|
51
|
+
expect(used.size).toBeGreaterThanOrEqual(OPTION_PALETTE.length / 2)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('different similar inputs do not all collide to one hue', () => {
|
|
55
|
+
const c1 = optionColor('red')
|
|
56
|
+
const c2 = optionColor('blue')
|
|
57
|
+
const c3 = optionColor('green')
|
|
58
|
+
const distinct = new Set([c1, c2, c3])
|
|
59
|
+
expect(distinct.size).toBeGreaterThan(1)
|
|
60
|
+
})
|
|
61
|
+
})
|
package/src/dynamic-columns.tsx
CHANGED
|
@@ -35,7 +35,12 @@ import {
|
|
|
35
35
|
FilterableColumnHeader,
|
|
36
36
|
type ColumnFilterMeta,
|
|
37
37
|
} from '@asteby/metacore-ui/data-table'
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
generateBadgeStyles,
|
|
40
|
+
getInitials,
|
|
41
|
+
optionColor,
|
|
42
|
+
relationChipStyles,
|
|
43
|
+
} from '@asteby/metacore-ui/lib'
|
|
39
44
|
import { Progress } from './dialogs/_primitives'
|
|
40
45
|
import { OptionsContext } from './options-context'
|
|
41
46
|
import { DynamicIcon } from './dynamic-icon'
|
|
@@ -272,7 +277,12 @@ interface OptionBadgeProps {
|
|
|
272
277
|
|
|
273
278
|
const OptionBadge: React.FC<OptionBadgeProps> = ({ option }) => {
|
|
274
279
|
const isDark = useIsDarkTheme()
|
|
275
|
-
|
|
280
|
+
// Explicit backend color wins; otherwise derive a stable, cohesive color
|
|
281
|
+
// from the option's value (fallback label) so "dead" gray badges come
|
|
282
|
+
// alive. Inline style (hex-derived) so it works regardless of the host's
|
|
283
|
+
// tailwind safelist — addon-arbitrary classes aren't in the host scan.
|
|
284
|
+
const colorSource = option.color || optionColor(option.value || option.label)
|
|
285
|
+
const colorStyles = generateBadgeStyles(colorSource, { isDark })
|
|
276
286
|
return (
|
|
277
287
|
<Badge variant="outline" className="flex items-center gap-1 border-0" style={colorStyles}>
|
|
278
288
|
{option.icon && <DynamicIcon name={option.icon} className="h-3.5 w-3.5" />}
|
|
@@ -329,11 +339,18 @@ export const resolveRelationLabel = (col: ColumnDefinition, row: any): string =>
|
|
|
329
339
|
* `belongs_to` column (category, supplier, warehouse, …) without per-addon code.
|
|
330
340
|
*/
|
|
331
341
|
const RelationCell: React.FC<{ col: ColumnDefinition; row: any }> = ({ col, row }) => {
|
|
342
|
+
const isDark = useIsDarkTheme()
|
|
332
343
|
const display = resolveRelationLabel(col, row)
|
|
333
344
|
if (!display) return <EmptyCell />
|
|
345
|
+
// Deterministic, SUBTLE color keyed on the resolved label — lighter than
|
|
346
|
+
// enum badges (soft tint, no heavy fill) so category/brand chips read as
|
|
347
|
+
// alive yet stay visually distinct from option/status badges. Inline style
|
|
348
|
+
// (hex-derived) bypasses the host tailwind safelist.
|
|
349
|
+
const chipStyles = relationChipStyles(display, { isDark })
|
|
334
350
|
return (
|
|
335
351
|
<span
|
|
336
|
-
className="inline-flex max-w-[220px] items-center truncate rounded-md
|
|
352
|
+
className="inline-flex max-w-[220px] items-center truncate rounded-md px-2 py-0.5 text-sm font-medium"
|
|
353
|
+
style={chipStyles}
|
|
337
354
|
title={display}
|
|
338
355
|
>
|
|
339
356
|
<span className="truncate">{display}</span>
|