@wonderlandlabs-pixi-ux/box 1.2.7 → 1.2.8

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.
Files changed (70) hide show
  1. package/README.STYLES.md +87 -78
  2. package/README.md +17 -1
  3. package/TODO.md +2 -3
  4. package/dist/BorderThickness.d.ts +11 -0
  5. package/dist/BorderThickness.js +57 -0
  6. package/dist/BoxPixi.stories.d.ts +9 -0
  7. package/dist/BoxPixi.stories.js +883 -0
  8. package/dist/BoxStore.d.ts +41 -4
  9. package/dist/BoxStore.js +249 -45
  10. package/dist/BoxStore.stories.d.ts +12 -0
  11. package/dist/BoxStore.stories.js +536 -31
  12. package/dist/ComputeAxis.d.ts +11 -4
  13. package/dist/ComputeAxis.js +25 -13
  14. package/dist/ComputeAxis.stories.js +8 -22
  15. package/dist/InsetDigest.d.ts +12 -0
  16. package/dist/InsetDigest.js +84 -0
  17. package/dist/boxPixiStoryStyles.json +250 -0
  18. package/dist/constants.d.ts +12 -0
  19. package/dist/constants.js +12 -0
  20. package/dist/helpers.d.ts +14 -4
  21. package/dist/helpers.js +180 -1
  22. package/dist/index.d.ts +10 -12
  23. package/dist/index.js +10 -11
  24. package/dist/storyStyles.d.ts +2 -0
  25. package/dist/storyStyles.js +5 -0
  26. package/dist/storyStyles.json +286 -0
  27. package/dist/styleHelpers.d.ts +11 -0
  28. package/dist/styleHelpers.js +158 -0
  29. package/dist/toPixi.d.ts +3 -0
  30. package/dist/toPixi.helpers.d.ts +24 -0
  31. package/dist/toPixi.helpers.js +258 -0
  32. package/dist/toPixi.js +315 -0
  33. package/dist/toPixi.killlist.helpers.d.ts +5 -0
  34. package/dist/toPixi.killlist.helpers.js +28 -0
  35. package/dist/toSVG.d.ts +11 -0
  36. package/dist/toSVG.js +172 -0
  37. package/dist/types.d.ts +495 -13
  38. package/dist/types.js +68 -8
  39. package/package.json +3 -1
  40. package/src/BoxPixi.stories.ts +1002 -0
  41. package/src/BoxStore.stories.ts +566 -35
  42. package/src/BoxStore.ts +287 -53
  43. package/src/ComputeAxis.ts +29 -13
  44. package/src/InsetDigest.ts +110 -0
  45. package/src/_deprecated/ComputeAxis.story-archive.ts +20 -0
  46. package/src/boxPixiStoryStyles.json +250 -0
  47. package/src/constants.ts +15 -0
  48. package/src/helpers.ts +270 -7
  49. package/src/index.ts +10 -0
  50. package/src/storyStyles.json +286 -0
  51. package/src/storyStyles.ts +7 -0
  52. package/src/styleHelpers.ts +211 -0
  53. package/src/toPixi.helpers.ts +372 -0
  54. package/src/toPixi.killlist.helpers.ts +41 -0
  55. package/src/toPixi.ts +473 -0
  56. package/src/toSVG.ts +236 -0
  57. package/src/types.ts +198 -25
  58. package/src/uuid.d.ts +3 -0
  59. package/test/BorderThickness.test.ts +46 -0
  60. package/test/BoxStore.test.ts +175 -0
  61. package/test/BoxTree.test.ts +958 -0
  62. package/test/BoxUx.test.ts +436 -0
  63. package/test/ComputeAxis.test.ts +119 -5
  64. package/test/boxTreeRenderers.test.ts +80 -0
  65. package/test/gradient.test.ts +67 -0
  66. package/test/helpers.test.ts +138 -2
  67. package/test/sizeUtils.test.ts +132 -0
  68. package/test/styleHelpers.test.ts +33 -0
  69. package/test/toSVG.test.ts +134 -0
  70. package/src/ComputeAxis.stories.ts +0 -319
package/README.STYLES.md CHANGED
@@ -1,115 +1,124 @@
1
- # Box Styles and Composition
1
+ # Box Styles
2
2
 
3
- This page documents how the default `BoxUxPixi` composes render layers and which style keys it reads.
3
+ The rebuilt `box` package does not ship a default renderer.
4
4
 
5
- Typical flow:
5
+ Instead, [`BoxStore`](/Users/bingomanatee/Documents/repos/wonderlandlabs-pixi-ux/packages/box/src/BoxStore.ts) provides a small style-resolution surface that renderers can use after layout has been computed.
6
6
 
7
- 1. `root.styles = styles`
8
- 2. `root.assignUx((box) => new BoxUxPixi(box))`
9
- 3. `root.render()`
7
+ ## Current Contract
10
8
 
11
- ## Style Path Resolution
9
+ A box cell can carry:
12
10
 
13
- Each `BoxTree` node has:
11
+ - `name`
12
+ - `variant`
13
+ - `states`
14
+ - `content`
15
+ - `children`
14
16
 
15
- - `styleName`
16
- - `modeVerb[]`
17
- - root-level `globalVerb[]`
17
+ `BoxStore` contributes:
18
18
 
19
- When UX code resolves a style property:
19
+ - `styles`
20
+ - `styleStates`
21
+ - `styleNouns`
22
+ - `variant`
23
+ - `resolveStyle(propertyPath, { states?, extraNouns? })`
20
24
 
21
- 1. Try hierarchical path + property, e.g. `button.icon.bgColor`
22
- 2. Try hierarchical object, e.g. `button.icon` then pick `bgColor`
23
- 3. Fallback to atomic path + property, e.g. `icon.bgColor`
24
- 4. Fallback to atomic object, e.g. `icon` then pick `bgColor`
25
+ The intent is:
25
26
 
26
- State list is:
27
+ - cells define nouns and state
28
+ - parents compute layout
29
+ - renderers ask the store for appearance
27
30
 
28
- - `globalVerb + modeVerb`
31
+ ## Style Resolution
29
32
 
30
- ## Default Style Keys
33
+ `resolveStyle()` builds noun paths from the current box node.
31
34
 
32
- The default UX reads:
35
+ For a box named `button` with:
33
36
 
34
- - `bgColor`
35
- - `bgAlpha`
36
- - `bgStrokeColor`
37
- - `bgStrokeAlpha`
38
- - `bgStrokeSize`
37
+ - `variant: 'primary'`
38
+ - `states: ['hover']`
39
39
 
40
- From resolved paths, this means keys like:
40
+ and a renderer request of:
41
41
 
42
- - `panel.bgColor`
43
- - `button.icon.bgStrokeColor`
44
- - `icon.bgStrokeSize`
42
+ ```ts
43
+ box.resolveStyle(['fill', 'color']);
44
+ ```
45
+
46
+ the store will try, in order:
45
47
 
46
- ## Composition Model
48
+ 1. `button.primary.fill.color` with `['hover']`
49
+ 2. `button.fill.color` with `['hover']`
50
+ 3. `color` with `['hover']`
47
51
 
48
- `BoxUxPixi` exposes:
52
+ If `extraNouns` are provided, they are inserted between the box noun path and the property path. That is how higher-level components can express mode-specific branches such as:
49
53
 
50
- - `content: MapEnhanced` (`Map<string, unknown>` subclass)
51
- - `content.$box` for the owner node
52
- - `getContainer(key): unknown` with keys:
53
- - `ROOT_CONTAINER`, `BACKGROUND_CONTAINER`, `CHILD_CONTAINER`, `CONTENT_CONTAINER`, `OVERLAY_CONTAINER`, `STROKE_CONTAINER`
54
- - `attach(targetContainer?)` to mount root container to a parent (or `ux.app?.stage`)
55
- - `generateStyleMap(box)` -> `{ fill: { color, alpha }, stroke: { color, alpha, width } }`
54
+ - `button.inline.fill.color`
55
+ - `button.icon.vertical.icon.size.x`
56
56
 
57
- Default reserved layer keys:
57
+ ## Expected Style Manager Shape
58
58
 
59
- - `BOX_RENDER_CONTENT_ORDER.BACKGROUND = 0`
60
- - `BOX_RENDER_CONTENT_ORDER.CHILDREN = 50`
61
- - `BOX_RENDER_CONTENT_ORDER.CONTENT = 75`
62
- - `BOX_RENDER_CONTENT_ORDER.OVERLAY = 100`
63
- - `BOX_UX_ORDER` map + `setUxOrder(name, zIndex)` + `getUxOrder(name)` for named custom layers
59
+ The style object attached to `box.styles` only needs to satisfy:
64
60
 
65
- On every `render()`:
61
+ ```ts
62
+ type BoxStyleQueryLike = {
63
+ nouns: string[];
64
+ states: string[];
65
+ };
66
+
67
+ type BoxStyleManagerLike = {
68
+ match: (query: BoxStyleQueryLike) => unknown;
69
+ matchHierarchy?: (query: BoxStyleQueryLike) => unknown;
70
+ };
71
+ ```
66
72
 
67
- 1. Default layers are pre-populated if missing
68
- 2. Child UX instances are updated
69
- 3. Child container is cleared and rebuilt from current child UX containers
70
- 4. Optional `box.content` is rendered into the `CONTENT` layer (`text` and image URL content)
71
- 5. Background/stroke are redrawn from styles
72
- 6. `content` is sorted by `zIndex` and non-empty items are attached to the root container
73
+ [`@wonderlandlabs-pixi-ux/style-tree`](https://www.npmjs.com/package/@wonderlandlabs-pixi-ux/style-tree) already matches that shape and is the intended default pairing.
73
74
 
74
- Render queueing is owned by `BoxTree` (store-level watcher), not by `BoxUxPixi`.
75
+ ## Renderer Pattern
75
76
 
76
- `BoxUxPixi` extends `BoxUxBase`; custom renderers can usually start from `BoxUxBase`
77
- to reuse lifecycle patterns (`init`/`render`/`clear` and visible up/down flow).
77
+ The happy-path renderer flow is:
78
78
 
79
- ## Built-in Layers
79
+ 1. Build or mutate the `BoxStore` tree.
80
+ 2. Attach `styles` to the root store.
81
+ 3. Call `box.update()` to compute locations.
82
+ 4. Walk the resulting tree and render from `location`, `content`, and resolved styles.
80
83
 
81
- - `BACKGROUND`:
82
- - `Graphics`
83
- - draws fill from `bgColor`
84
- - `CHILDREN`:
85
- - `Container`
86
- - receives child renderer containers each frame
87
- - `CONTENT`:
88
- - `Container`
89
- - receives optional `box.content` rendering (`text` and image URL content)
90
- - `OVERLAY`:
91
- - `Container`
92
- - contains stroke `Graphics` drawing from `bgStrokeColor` + `bgStrokeSize`
84
+ That keeps the responsibilities separated:
93
85
 
94
- ## Extending With Custom Layers
86
+ - `ComputeAxis` resolves spans and alignment
87
+ - `BoxStore` owns the layout tree and style lookup context
88
+ - your renderer owns Pixi, HTML, SVG, canvas, or any other output target
95
89
 
96
- You can inject custom layers by setting additional `content` entries:
90
+ ## Example
97
91
 
98
92
  ```ts
99
- import { Graphics } from 'pixi.js';
100
- import { BoxUxPixi } from '@wonderlandlabs-pixi-ux/box';
93
+ import { BoxStore } from '@wonderlandlabs-pixi-ux/box';
94
+ import { fromJSON } from '@wonderlandlabs-pixi-ux/style-tree';
95
+
96
+ const styles = fromJSON({
97
+ button: {
98
+ fill: {
99
+ $*: { color: { r: 0.2, g: 0.4, b: 0.8 }, alpha: 1 }
100
+ }
101
+ }
102
+ });
103
+
104
+ const box = new BoxStore({
105
+ value: {
106
+ name: 'button',
107
+ absolute: true,
108
+ dim: { x: 0, y: 0, w: 120, h: 40 },
109
+ align: { direction: 'horizontal', xPosition: 'center', yPosition: 'center' },
110
+ children: [],
111
+ },
112
+ });
101
113
 
102
114
  box.styles = styles;
103
- const ux = new BoxUxPixi(box);
104
- const custom = new Graphics();
105
- custom.visible = true;
115
+ box.update();
106
116
 
107
- custom.zIndex = 76; // 75 is reserved for CONTENT
108
- ux.content.set('CUSTOM', custom);
109
- ux.box.render();
117
+ const fillColor = box.resolveStyle(['fill', 'color']);
110
118
  ```
111
119
 
112
120
  ## Notes
113
121
 
114
- - The default UX is intentionally simple: background + children + overlay stroke.
115
- - If you need richer visuals, return a custom UX object from `assignUx`.
122
+ - The old `BoxTree` / `BoxUxPixi` renderer stack still exists under [`src/_deprecated`](/Users/bingomanatee/Documents/repos/wonderlandlabs-pixi-ux/packages/box/src/_deprecated), but it is legacy code now.
123
+ - The new architecture is intentionally renderer-agnostic.
124
+ - `content` is descriptive data, not a renderer by itself.
package/README.md CHANGED
@@ -29,13 +29,29 @@ The current layout pass is:
29
29
  1. Resolve the main-axis spans for the children.
30
30
  2. Complete unresolved fractional spans.
31
31
  3. Place children from the parent alignment and the resolved spans.
32
+ 4. Recurse into child boxes using the computed location rectangles.
32
33
 
33
- `ComputeAxis` currently handles the green path for:
34
+ `ComputeAxis` currently handles:
34
35
 
35
36
  - absolute pixel dimensions
36
37
  - percentage dimensions
38
+ - fractional dimensions on the main axis by weighted remainder distribution
39
+ - fractional dimensions on the cross axis by resolving to the largest resolved peer span
40
+ - cross-axis `fill`
37
41
  - parent-owned start / center / end alignment
38
42
 
43
+ ## Styles
44
+
45
+ `BoxStore` also exposes style context for renderers:
46
+
47
+ - `styles`
48
+ - `variant`
49
+ - `styleStates`
50
+ - `styleNouns`
51
+ - `resolveStyle(...)`
52
+
53
+ See [`README.STYLES.md`](/Users/bingomanatee/Documents/repos/wonderlandlabs-pixi-ux/packages/box/README.STYLES.md) for the renderer-facing contract.
54
+
39
55
  ## Status
40
56
 
41
57
  This package is mid-refactor.
package/TODO.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # TODO
2
2
 
3
- - Achieve recursive layout so each child can compute and apply layout to its own children.
4
3
  - use Immer/diff to only recompute the children that have changed or the children of changed nodes.
5
- - Properly compute fractional dimensions on the main axis.
6
- - Replace the current no-op fractional completion step with remainder distribution across unresolved spans.
7
4
  - Decide how overflow should resolve when content exceeds the parent container:
8
5
  squash content to fit, crop content to the container, or allow overflow without intervention.
9
6
  - Consider adding min/max sizing constraints, especially if fractional sizing makes alignment too inert.
10
7
  - Keep the parent-owned alignment model strict: children define dimensions, parents place them.
8
+ - Add a first-class renderer package or reference renderer examples for Pixi, HTML, and SVG.
9
+ - Normalize package-root exports so sibling packages do not need `dist` subpath imports during local compilation.
@@ -0,0 +1,11 @@
1
+ import type { BorderPartType, BoxBorderType } from './types.js';
2
+ export type BorderInsets = Record<BorderPartType, number>;
3
+ export declare class BorderThickness {
4
+ #private;
5
+ readonly top: number;
6
+ readonly right: number;
7
+ readonly bottom: number;
8
+ readonly left: number;
9
+ constructor(defs?: BoxBorderType);
10
+ toInsets(): BorderInsets;
11
+ }
@@ -0,0 +1,57 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _a, _BorderThickness_apply;
7
+ import { BORDER_PART_BOTTOM, BORDER_PART_LEFT, BORDER_PART_RIGHT, BORDER_PART_TOP, BORDER_SCOPE_ALL, BORDER_SCOPE_BOTTOM, BORDER_SCOPE_HORIZ, BORDER_SCOPE_LEFT, BORDER_SCOPE_RIGHT, BORDER_SCOPE_TOP, BORDER_SCOPE_VERT, } from './constants.js';
8
+ export class BorderThickness {
9
+ constructor(defs = []) {
10
+ const insets = defs.reduce((nextInsets, def) => {
11
+ return __classPrivateFieldGet(_a, _a, "m", _BorderThickness_apply).call(_a, nextInsets, def);
12
+ }, {
13
+ [BORDER_PART_TOP]: 0,
14
+ [BORDER_PART_RIGHT]: 0,
15
+ [BORDER_PART_BOTTOM]: 0,
16
+ [BORDER_PART_LEFT]: 0,
17
+ });
18
+ this.top = insets[BORDER_PART_TOP];
19
+ this.right = insets[BORDER_PART_RIGHT];
20
+ this.bottom = insets[BORDER_PART_BOTTOM];
21
+ this.left = insets[BORDER_PART_LEFT];
22
+ }
23
+ toInsets() {
24
+ return {
25
+ [BORDER_PART_TOP]: this.top,
26
+ [BORDER_PART_RIGHT]: this.right,
27
+ [BORDER_PART_BOTTOM]: this.bottom,
28
+ [BORDER_PART_LEFT]: this.left,
29
+ };
30
+ }
31
+ }
32
+ _a = BorderThickness, _BorderThickness_apply = function _BorderThickness_apply(insets, def) {
33
+ const { scope, value } = def;
34
+ switch (scope) {
35
+ case BORDER_SCOPE_ALL:
36
+ return {
37
+ [BORDER_PART_TOP]: value,
38
+ [BORDER_PART_RIGHT]: value,
39
+ [BORDER_PART_BOTTOM]: value,
40
+ [BORDER_PART_LEFT]: value,
41
+ };
42
+ case BORDER_SCOPE_HORIZ:
43
+ return { ...insets, [BORDER_PART_LEFT]: value, [BORDER_PART_RIGHT]: value };
44
+ case BORDER_SCOPE_VERT:
45
+ return { ...insets, [BORDER_PART_TOP]: value, [BORDER_PART_BOTTOM]: value };
46
+ case BORDER_SCOPE_TOP:
47
+ return { ...insets, [BORDER_PART_TOP]: value };
48
+ case BORDER_SCOPE_RIGHT:
49
+ return { ...insets, [BORDER_PART_RIGHT]: value };
50
+ case BORDER_SCOPE_BOTTOM:
51
+ return { ...insets, [BORDER_PART_BOTTOM]: value };
52
+ case BORDER_SCOPE_LEFT:
53
+ return { ...insets, [BORDER_PART_LEFT]: value };
54
+ default:
55
+ return insets;
56
+ }
57
+ };
@@ -0,0 +1,9 @@
1
+ import type { Meta, StoryObj } from '@storybook/html';
2
+ type Story = StoryObj;
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ export declare const PixiBoxRendererPOC: Story;
6
+ export declare const ProductCatalogGrid: Story;
7
+ export declare const ProductDetailHero: Story;
8
+ export declare const ProductComparisonStrip: Story;
9
+ export declare const BoxDebugLoop: Story;