@pihanga2/shadcn 0.2.0 → 0.2.2

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 (130) hide show
  1. package/AGENT.building-cards.md +208 -3
  2. package/AGENT.md +66 -4
  3. package/AGENT.using-cards.md +197 -250
  4. package/README.md +4 -1
  5. package/cards/badge/badge.component.js +1 -0
  6. package/cards/badge/badge.component.js.map +1 -1
  7. package/cards/box/box.component.js +2 -2
  8. package/cards/box/box.component.js.map +1 -1
  9. package/cards/box/box.types.d.ts +0 -8
  10. package/cards/box/box.types.js.map +1 -1
  11. package/cards/button/button.types.d.ts +4 -0
  12. package/cards/button/button.types.js +2 -2
  13. package/cards/button/button.types.js.map +1 -1
  14. package/cards/button/index.js +5 -5
  15. package/cards/chart/chart.component.d.ts +4 -0
  16. package/cards/chart/chart.types.d.ts +223 -0
  17. package/cards/chart/index.d.ts +1 -0
  18. package/cards/checkbox/checkbox.component.js +1 -0
  19. package/cards/checkbox/checkbox.component.js.map +1 -1
  20. package/cards/checkbox/checkbox.types.d.ts +4 -0
  21. package/cards/checkbox/checkbox.types.js +2 -2
  22. package/cards/checkbox/checkbox.types.js.map +1 -1
  23. package/cards/checkbox/index.js +5 -5
  24. package/cards/conditional/conditional.component.d.ts +14 -3
  25. package/cards/conditional/conditional.component.js +17 -9
  26. package/cards/conditional/conditional.component.js.map +1 -1
  27. package/cards/conditional/conditional.types.d.ts +115 -8
  28. package/cards/conditional/conditional.types.js.map +1 -1
  29. package/cards/core-index.d.ts +3 -0
  30. package/cards/core-index.js +38 -31
  31. package/cards/dataTable/dataTable.types.js.map +1 -1
  32. package/cards/dataTable/dataTableRowDetail.component.js +1 -0
  33. package/cards/dataTable/dataTableRowDetail.component.js.map +1 -1
  34. package/cards/field/field.types.js.map +1 -1
  35. package/cards/fileDrop/fileDrop.component.js +90 -0
  36. package/cards/fileDrop/fileDrop.component.js.map +1 -0
  37. package/cards/fileDrop/fileDrop.types.js +11 -0
  38. package/cards/fileDrop/fileDrop.types.js.map +1 -0
  39. package/cards/fileDrop/index.js +13 -0
  40. package/cards/fileDrop/index.js.map +1 -0
  41. package/cards/flexGrid/flexGrid.types.js.map +1 -1
  42. package/cards/form/form.types.d.ts +4 -0
  43. package/cards/form/form.types.js +2 -2
  44. package/cards/form/form.types.js.map +1 -1
  45. package/cards/form/index.js +5 -5
  46. package/cards/graphin/contextMenu.component.d.ts +28 -0
  47. package/cards/graphin/eventDispatcher.component.d.ts +8 -0
  48. package/cards/graphin/graphin.component.d.ts +2 -2
  49. package/cards/graphin/graphin.playground-cards.d.ts +1 -0
  50. package/cards/graphin/graphin.types.d.ts +477 -4
  51. package/cards/graphin/graphinOpHandler.component.d.ts +8 -0
  52. package/cards/graphin/index.d.ts +1 -0
  53. package/cards/graphin/tooltip.component.d.ts +13 -1
  54. package/cards/input/index.js +5 -5
  55. package/cards/input/input.component.js +1 -0
  56. package/cards/input/input.component.js.map +1 -1
  57. package/cards/input/input.types.d.ts +10 -2
  58. package/cards/input/input.types.js +2 -2
  59. package/cards/input/input.types.js.map +1 -1
  60. package/cards/list/list.types.js +1 -0
  61. package/cards/list/list.types.js.map +1 -1
  62. package/cards/pageWithNavbar/pageWithNavbar.component.d.ts +5 -7
  63. package/cards/pageWithNavbar/pageWithNavbar.component.js +1 -1
  64. package/cards/pageWithNavbar/pageWithNavbar.component.js.map +1 -1
  65. package/cards/pasteTarget/pasteTarget.component.js.map +1 -1
  66. package/cards/select/index.js +5 -5
  67. package/cards/select/select.component.js +1 -0
  68. package/cards/select/select.component.js.map +1 -1
  69. package/cards/select/select.types.d.ts +12 -0
  70. package/cards/select/select.types.js +2 -2
  71. package/cards/select/select.types.js.map +1 -1
  72. package/cards/slider/index.d.ts +1 -0
  73. package/cards/slider/index.js +13 -0
  74. package/cards/slider/index.js.map +1 -0
  75. package/cards/slider/slider.component.d.ts +4 -0
  76. package/cards/slider/slider.component.js +64 -0
  77. package/cards/slider/slider.component.js.map +1 -0
  78. package/cards/slider/slider.types.d.ts +110 -0
  79. package/cards/slider/slider.types.js +7 -0
  80. package/cards/slider/slider.types.js.map +1 -0
  81. package/cards/sliderValue/index.d.ts +1 -0
  82. package/cards/sliderValue/index.js +12 -0
  83. package/cards/sliderValue/index.js.map +1 -0
  84. package/cards/sliderValue/sliderValue.component.d.ts +15 -0
  85. package/cards/sliderValue/sliderValue.component.js +33 -0
  86. package/cards/sliderValue/sliderValue.component.js.map +1 -0
  87. package/cards/sliderValue/sliderValue.types.d.ts +21 -0
  88. package/cards/sliderValue/sliderValue.types.js +7 -0
  89. package/cards/sliderValue/sliderValue.types.js.map +1 -0
  90. package/cards/stack/stack.component.js +1 -1
  91. package/cards/stack/stack.component.js.map +1 -1
  92. package/cards/stack/stack.types.js.map +1 -1
  93. package/cards/switch/index.js +5 -5
  94. package/cards/switch/switch.component.js +1 -0
  95. package/cards/switch/switch.component.js.map +1 -1
  96. package/cards/switch/switch.types.d.ts +4 -0
  97. package/cards/switch/switch.types.js +2 -2
  98. package/cards/switch/switch.types.js.map +1 -1
  99. package/cards/tabs/tabs.component.js +51 -25
  100. package/cards/tabs/tabs.component.js.map +1 -1
  101. package/cards/tabs/tabs.types.d.ts +10 -0
  102. package/cards/tabs/tabs.types.js.map +1 -1
  103. package/cards/textField/index.js +5 -5
  104. package/cards/textField/textField.component.js +1 -0
  105. package/cards/textField/textField.component.js.map +1 -1
  106. package/cards/textField/textField.types.d.ts +4 -0
  107. package/cards/textField/textField.types.js +2 -2
  108. package/cards/textField/textField.types.js.map +1 -1
  109. package/cards/toast/index.js +7 -7
  110. package/cards/toast/toast.component.js.map +1 -1
  111. package/cards/toast/toast.types.d.ts +4 -0
  112. package/cards/toast/toast.types.js +2 -2
  113. package/cards/toast/toast.types.js.map +1 -1
  114. package/cards/toggleGroup/index.js +5 -5
  115. package/cards/toggleGroup/toggleGroup.component.js +1 -0
  116. package/cards/toggleGroup/toggleGroup.component.js.map +1 -1
  117. package/cards/toggleGroup/toggleGroup.types.d.ts +4 -0
  118. package/cards/toggleGroup/toggleGroup.types.js +2 -2
  119. package/cards/toggleGroup/toggleGroup.types.js.map +1 -1
  120. package/cards/typography/typography.types.js +1 -0
  121. package/cards/typography/typography.types.js.map +1 -1
  122. package/components/hooks/use-breakpoint.d.ts +49 -0
  123. package/components/hooks/use-breakpoint.js +73 -0
  124. package/components/hooks/use-breakpoint.js.map +1 -0
  125. package/components/ui/slider.js +36 -0
  126. package/components/ui/slider.js.map +1 -0
  127. package/components/ui/toggle-group.js +1 -0
  128. package/components/ui/toggle-group.js.map +1 -1
  129. package/package.json +19 -2
  130. package/pihanga-shadcn.css +1 -1
@@ -1,13 +1,217 @@
1
1
  # AGENT.building-cards.md — creating new pihanga-shadcn cards
2
2
 
3
- > **Scope:** adding a new card *type* to `src/cards/` in this repository.
3
+ > **Scope:** building new cards either by composing existing cards into a
4
+ > **meta card** (recommended, TypeScript-only) or by adding a brand-new
5
+ > React-based primitive card to `src/cards/`.
4
6
  > Read [`AGENT.md`](./AGENT.md) first for orientation and universal rules.
5
7
  > If you only need to *use* existing cards in an app, switch to
6
8
  > [`AGENT.using-cards.md`](./AGENT.using-cards.md).
7
9
 
8
10
  ---
9
11
 
10
- ## ⚠️ Hard constraints before you write a single line
12
+ ## Meta cards composing new widgets from existing cards
13
+
14
+ > **This is the recommended approach for building any complex or reusable UI
15
+ > widget.** A meta card is assembled entirely from existing cards using a
16
+ > plain TypeScript mapper function — no JSX, no React imports, no DOM
17
+ > knowledge required. Read this section before considering a primitive card.
18
+
19
+ ### Why meta cards?
20
+
21
+ | Concern | Meta card | New primitive card (React component) |
22
+ |---|---|---|
23
+ | Code volume | ~50 lines TypeScript | ~200 lines JSX + hooks |
24
+ | Maintenance | Inherits upstream card fixes automatically | Must track shadcn/Radix updates manually |
25
+ | Reusability | Portable across any Pihanga app unchanged | Tightly coupled to one project |
26
+ | Testability | Pure function — mock props, assert card tree | Requires DOM + Redux environment |
27
+ | Extendibility | Swap any inner card without touching consumers | Requires internal refactoring |
28
+ | Learning curve | Pihanga config patterns only | JSX, React hooks, Pihanga internals |
29
+
30
+ **Rule of thumb:** if you can describe the widget as *"a layout card containing
31
+ some input and display cards"*, build it as a meta card. Only drop to a
32
+ primitive React component when the widget genuinely needs a DOM API, a
33
+ third-party React hook, or a non-card rendering strategy.
34
+
35
+ ### How meta cards work
36
+
37
+ A meta card has three ingredients:
38
+
39
+ 1. **Card declaration + actions** — same as any card; use
40
+ `createCardDeclaration`, `registerActions`, `createOnAction`.
41
+ 2. **Mapper function** — a plain function `(name, props, registerCard) =>
42
+ PiCardDef` that assembles child cards and returns the root card definition.
43
+ 3. **Registration** — `registerMetaCard({type, mapper, events})`.
44
+
45
+ The Pihanga runtime calls the mapper whenever the meta card needs to be
46
+ rendered or re-evaluated. Child cards can use `resolve(props.foo)` inside
47
+ their own prop functions to lazily read a value that may itself be a state
48
+ selector.
49
+
50
+ ### Full worked example — `Counter` meta card
51
+
52
+ The complete, annotated source lives at
53
+ [`example/src/counter.card.ts`](./example/src/counter.card.ts).
54
+ Abbreviated for reference:
55
+
56
+ ```ts
57
+ import {
58
+ createCardDeclaration, createOnAction, registerActions, registerMetaCard,
59
+ } from "@pihanga2/core";
60
+ import type {PiCardDef, PiMapProps, PiRegisterMetaCard, ReduxState, RegisterCardF} from "@pihanga2/core";
61
+ import {Stack, Button, Typography} from "@pihanga2/shadcn";
62
+
63
+ // ── 1. Card identity ─────────────────────────────────────────────────────────
64
+ const COUNTER_CARD = "meta/counter"; // convention: "meta/<name>" for app-level
65
+
66
+ // ── 2. Public factory (consumers call Counter({value: ...})) ────────────────
67
+ export const Counter = createCardDeclaration<CounterProps, CounterEvents>(COUNTER_CARD);
68
+
69
+ // ── 3. Actions + typed event helper ─────────────────────────────────────────
70
+ export const COUNTER_ACTION = registerActions(COUNTER_CARD, ["changed"]);
71
+ export const onCounterChanged = createOnAction<CounterChangeEvent>(COUNTER_ACTION.CHANGED);
72
+
73
+ // ── 4. Types ─────────────────────────────────────────────────────────────────
74
+ type CounterProps = { value: number };
75
+ type CounterChangeEvent = { value: number };
76
+ type CounterEvents = { onChange: CounterChangeEvent };
77
+ type CounterMapperProps = CounterProps & CounterEvents;
78
+
79
+ // ── 5. Mapper — assembles child cards ────────────────────────────────────────
80
+ function CounterMapper(
81
+ _: string,
82
+ props: PiMapProps<CounterMapperProps, ReduxState, object>,
83
+ registerCard: RegisterCardF,
84
+ ): PiCardDef {
85
+ // Register a child card under a stable name (optional but shown for reference)
86
+ const plusButton = registerCard("plus", Button({
87
+ label: "+",
88
+ opts: {size: "lg"},
89
+ // Re-map raw click → COUNTER_ACTION.CHANGED { value + 1 }
90
+ onClickedMapper: (_, {resolve}) => ({
91
+ type: COUNTER_ACTION.CHANGED,
92
+ value: resolve(props.value) + 1, // resolve() unwraps lazy state selectors
93
+ }),
94
+ }));
95
+
96
+ return Stack({
97
+ direction: "row", alignItems: "center", spacing: 4,
98
+ className: "p-16 justify-center",
99
+ content: [
100
+ Button({
101
+ label: "−",
102
+ opts: {size: "lg"},
103
+ onClickedMapper: (_, {resolve}) => ({
104
+ type: COUNTER_ACTION.CHANGED,
105
+ value: resolve(props.value) - 1,
106
+ }),
107
+ }),
108
+ Typography({
109
+ text: (_, {resolve}) => `Count: ${resolve(props.value)}`, // reactive text
110
+ level: "h2",
111
+ className: "min-w-[120px] text-center",
112
+ }),
113
+ plusButton,
114
+ ],
115
+ });
116
+ }
117
+
118
+ // ── 6. Register ───────────────────────────────────────────────────────────────
119
+ registerMetaCard({
120
+ type: COUNTER_CARD,
121
+ mapper: CounterMapper,
122
+ events: COUNTER_ACTION,
123
+ } satisfies PiRegisterMetaCard);
124
+ ```
125
+
126
+ **Usage at the app level** (`example/src/app.pihanga.ts`):
127
+
128
+ ```ts
129
+ import {Counter} from "./counter.card";
130
+ import type {AppState} from "./app.state";
131
+
132
+ registerCard("page", Counter<AppState>({
133
+ value: (s) => s.count, // state selector — auto-reactive
134
+ }));
135
+ ```
136
+
137
+ **Handling the emitted event** (`example/src/app.reducer.ts`):
138
+
139
+ ```ts
140
+ import {onCounterChanged} from "./counter.card";
141
+
142
+ register((r) => {
143
+ onCounterChanged<AppState>(r, (state, {value}) => {
144
+ state.count = value; // raw button clicks never surface here — encapsulated
145
+ });
146
+ });
147
+ ```
148
+
149
+ ### Key API reference for meta cards
150
+
151
+ | Symbol | Source | Purpose |
152
+ |---|---|---|
153
+ | `registerMetaCard(opts)` | `@pihanga2/core` | Registers a mapper as a named card type |
154
+ | `PiRegisterMetaCard` | `@pihanga2/core` | Type for the `opts` object (use `satisfies`) |
155
+ | `MetaCardMapperF` | `@pihanga2/core` | Type of the mapper function |
156
+ | `RegisterCardF` | `@pihanga2/core` | Type of the `registerCard` arg inside the mapper |
157
+ | `PiMapProps<Props, State, Ctx>` | `@pihanga2/core` | Prop type inside the mapper (each prop may be a state selector) |
158
+ | `resolve(prop)` | `StateMapperContext` | Unwraps a lazy prop value inside a child card's prop function |
159
+ | `onClickedMapper` / `on*Mapper` | any card | Event mapper suffix — re-maps a child card's action to a different action type |
160
+
161
+ ### Patterns to know
162
+
163
+ **Lazy prop propagation with `resolve`**
164
+
165
+ When a meta card receives `value: (s) => s.count` (a state selector), child
166
+ cards can't use that selector directly — it has to be unwrapped at the time the
167
+ child's own prop function runs:
168
+
169
+ ```ts
170
+ // ❌ Doesn't work — selector is passed as-is; child sees a function, not a number
171
+ Typography({ text: props.value })
172
+
173
+ // ✅ Correct — resolve() unwraps the selector inside the child's prop function
174
+ Typography({ text: (_, {resolve}) => `Count: ${resolve(props.value)}` })
175
+ ```
176
+
177
+ **Event remapping with `onXxxMapper`**
178
+
179
+ Every card that emits an event supports an `on<EventName>Mapper` prop. The
180
+ mapper intercepts the raw event and returns a different action (or `null` to
181
+ suppress):
182
+
183
+ ```ts
184
+ Button({
185
+ label: "Save",
186
+ // Re-map generic click → domain-specific action
187
+ onClickedMapper: (_ev, _ctx) => ({ type: MY_ACTION.SAVED }),
188
+ })
189
+ ```
190
+
191
+ **Registering a stable child card name**
192
+
193
+ Pass the result of `registerCard(name, def)` as a child in the `content` array.
194
+ This is optional but gives the child a stable registry identity:
195
+
196
+ ```ts
197
+ const saveBtn = registerCard("save-btn", Button({label: "Save", …}));
198
+ return Stack({ content: [saveBtn] });
199
+ ```
200
+
201
+ **Accessing metacard context from a deeply nested child**
202
+
203
+ Use `metaCtxtProps` in a child card's state mapper when you need the context
204
+ props that were passed to the meta card's top-level card:
205
+
206
+ ```ts
207
+ // Inside a child card declaration within the mapper:
208
+ properties: (s, { metaCtxtProps }) => metaCtxtProps.elementData.properties,
209
+ ```
210
+
211
+ ---
212
+
213
+ ## ⚠️ Hard constraints before you write a single line (primitive cards)
214
+
11
215
 
12
216
  1. **`src/components/` is read-only.** It is managed exclusively by the shadcn
13
217
  CLI (`npx shadcn@latest add <name>`). Never create or edit files there.
@@ -383,7 +587,8 @@ Rules:
383
587
 
384
588
  | Need | Card to study |
385
589
  |---|---|
386
- | Minimal card structure | `src/cards/emptyCard.tsx` |
590
+ | **Meta card (composition)** | **`example/src/counter.card.ts`** — fully annotated: Stack + Button × 2 + Typography, event remapping, `resolve()`, stable child names |
591
+ | Minimal primitive card structure | `src/cards/emptyCard.tsx` |
387
592
  | Button with variants, icons, tooltip | `src/cards/button/` |
388
593
  | Dropdown / context menu | `src/cards/dropDownMenu/` |
389
594
  | Form input pattern | `src/cards/textField/`, `src/cards/checkbox/` |
package/AGENT.md CHANGED
@@ -30,12 +30,67 @@ https://github.com/ivcap-works/pihanga-shadcn
30
30
 
31
31
  | If your task is … | Read this file |
32
32
  |---|---|
33
+ | **Getting started** — brand-new project, shadcn/ui + pihanga setup, file layout | [`AGENTS.getting-started.md`](./AGENTS.getting-started.md) |
33
34
  | **Using** existing cards — install, wire, compose, navigate | [`AGENT.using-cards.md`](./AGENT.using-cards.md) |
34
- | **Building** new cardscreate a new card type for this repo | [`AGENT.building-cards.md`](./AGENT.building-cards.md) |
35
+ | **Building complex UI**assembling a reusable widget from existing cards | [`AGENT.building-cards.md`](./AGENT.building-cards.md) — § *Meta cards* |
36
+ | **Building** a new primitive card from scratch — new React component for this repo | [`AGENT.building-cards.md`](./AGENT.building-cards.md) |
35
37
 
36
- If you are **unsure**, start with `AGENT.using-cards.md`; it covers the common
37
- app-building case. Switch to `AGENT.building-cards.md` only when you need to
38
- add a new card *type* to `src/cards/` itself.
38
+ If you are **starting from scratch**, read `AGENTS.getting-started.md` first it
39
+ covers Vite setup, shadcn/ui init, pihanga-core installation, and the initial file
40
+ layout. Then continue with `AGENT.using-cards.md` for wiring and composing cards.
41
+
42
+ If you are adding cards to an **existing project**, go straight to
43
+ `AGENT.using-cards.md`.
44
+
45
+ ### ⭐ Need a complex, reusable UI widget? Use a meta card.
46
+
47
+ Before writing a new React component, ask: *can I build this by composing
48
+ existing cards?* If yes — and that is almost always the answer — use a
49
+ **meta card** instead.
50
+
51
+ A meta card is a TypeScript-only module (no JSX, no React imports) that
52
+ assembles other cards inside a mapper function and registers itself with
53
+ `registerMetaCard`. The result looks and feels exactly like a primitive card to
54
+ consumers: a typed factory function, typed event helpers, and full `memo()`/state
55
+ reactivity.
56
+
57
+ ```ts
58
+ // example/src/counter.card.ts (abbreviated)
59
+ import {registerMetaCard, createCardDeclaration, registerActions} from "@pihanga2/core";
60
+ import {Stack, Button, Typography} from "@pihanga2/shadcn";
61
+
62
+ const COUNTER_CARD = "meta/counter";
63
+ export const Counter = createCardDeclaration<CounterProps, CounterEvents>(COUNTER_CARD);
64
+ export const COUNTER_ACTION = registerActions(COUNTER_CARD, ["changed"]);
65
+
66
+ registerMetaCard({
67
+ type: COUNTER_CARD,
68
+ mapper(_name, props, registerCard) {
69
+ return Stack({ content: [
70
+ Button({ label: "−", onClickedMapper: (_, {resolve}) => ({type: COUNTER_ACTION.CHANGED, value: resolve(props.value) - 1}) }),
71
+ Typography({ text: (_, {resolve}) => `Count: ${resolve(props.value)}` }),
72
+ Button({ label: "+", onClickedMapper: (_, {resolve}) => ({type: COUNTER_ACTION.CHANGED, value: resolve(props.value) + 1}) }),
73
+ ]});
74
+ },
75
+ events: COUNTER_ACTION,
76
+ });
77
+ ```
78
+
79
+ **Why meta cards over new React components?**
80
+
81
+ | Concern | Meta card | New React component |
82
+ |---|---|---|
83
+ | Code complexity | Low — pure TypeScript config | High — JSX, hooks, event wiring |
84
+ | Maintenance | Inherits upstream fixes automatically | Must be kept in sync manually |
85
+ | Reusability | Drops into any Pihanga app | Tightly coupled to one project |
86
+ | Testability | Plain function, mockable | Requires DOM + Redux setup |
87
+ | Extendibility | Swap out any inner card without touching consumers | Requires internal refactoring |
88
+
89
+ See the full meta card guide in [`AGENT.building-cards.md`](./AGENT.building-cards.md#meta-cards--composing-new-widgets-from-existing-cards)
90
+ and the complete working example at [`example/src/counter.card.ts`](./example/src/counter.card.ts).
91
+
92
+ Switch to `AGENT.building-cards.md` — § *Building a primitive card from scratch* — only
93
+ when no combination of existing cards can achieve the required UI.
39
94
 
40
95
  ---
41
96
 
@@ -48,3 +103,10 @@ add a new card *type* to `src/cards/` itself.
48
103
  must ensure only one of them calls it.
49
104
  - **`children` is a reserved React prop** — never use it in a card's `…Props`
50
105
  type; use `content`, `contentCard`, or `items` instead.
106
+ - **npm channel: add `lucide-react` to `optimizeDeps.include`** — excluding
107
+ `@pihanga2/shadcn` from Vite's pre-bundler (required to prevent duplicate
108
+ registries) also causes `lucide-react` to be served as raw ESM, triggering
109
+ ~1 000 individual icon file requests in the dev browser. Adblockers block
110
+ `fingerprint.js` as a tracking script. Always include `"lucide-react"` in
111
+ `optimizeDeps.include` (see `AGENTS.getting-started.md` Step 5). Production
112
+ builds are unaffected — Rollup tree-shakes unused icons normally.