@pihanga2/shadcn 0.1.0 → 0.2.1

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 (99) hide show
  1. package/AGENT.building-cards.md +413 -0
  2. package/AGENT.md +57 -0
  3. package/AGENT.using-cards.md +1051 -0
  4. package/cards/box/box.component.js +2 -2
  5. package/cards/box/box.component.js.map +1 -1
  6. package/cards/box/box.types.d.ts +1 -4
  7. package/cards/box/box.types.js.map +1 -1
  8. package/cards/button/button.types.d.ts +3 -0
  9. package/cards/button/button.types.js +2 -2
  10. package/cards/button/button.types.js.map +1 -1
  11. package/cards/button/index.js +5 -5
  12. package/cards/chartGraph/chartGraph.component.d.ts +4 -0
  13. package/cards/chartGraph/chartGraph.types.d.ts +102 -0
  14. package/cards/chartGraph/index.d.ts +1 -0
  15. package/cards/conditional/conditional.component.d.ts +14 -3
  16. package/cards/conditional/conditional.component.js +17 -9
  17. package/cards/conditional/conditional.component.js.map +1 -1
  18. package/cards/conditional/conditional.types.d.ts +100 -8
  19. package/cards/conditional/conditional.types.js.map +1 -1
  20. package/cards/core-index.js +31 -31
  21. package/cards/fileDrop/fileDrop.component.d.ts +5 -0
  22. package/cards/fileDrop/fileDrop.types.d.ts +43 -0
  23. package/cards/fileDrop/index.d.ts +2 -0
  24. package/cards/graphin/contextMenu.component.d.ts +28 -0
  25. package/cards/graphin/eventDispatcher.component.d.ts +8 -0
  26. package/cards/graphin/graphin.component.d.ts +2 -2
  27. package/cards/graphin/graphin.playground-cards.d.ts +1 -0
  28. package/cards/graphin/graphin.types.d.ts +444 -4
  29. package/cards/graphin/graphinOpHandler.component.d.ts +8 -0
  30. package/cards/graphin/index.d.ts +1 -0
  31. package/cards/graphin/tooltip.component.d.ts +13 -1
  32. package/cards/icons.js +7 -3
  33. package/cards/icons.js.map +1 -1
  34. package/cards/index.d.ts +1 -0
  35. package/cards/slider/index.d.ts +1 -0
  36. package/cards/slider/slider.component.d.ts +4 -0
  37. package/cards/slider/slider.types.d.ts +102 -0
  38. package/cards/sliderValue/index.d.ts +1 -0
  39. package/cards/sliderValue/sliderValue.component.d.ts +15 -0
  40. package/cards/sliderValue/sliderValue.types.d.ts +21 -0
  41. package/cards/stack/stack.types.d.ts +19 -22
  42. package/cards/stack/stack.types.js +1 -1
  43. package/cards/stack/stack.types.js.map +1 -1
  44. package/cards/tabs/index.js +4 -4
  45. package/cards/tabs/tabs.component.js +51 -25
  46. package/cards/tabs/tabs.component.js.map +1 -1
  47. package/cards/tabs/tabs.types.d.ts +12 -0
  48. package/cards/tabs/tabs.types.js +2 -2
  49. package/cards/tabs/tabs.types.js.map +1 -1
  50. package/cards/types.d.ts +4 -0
  51. package/cards/types.js.map +1 -1
  52. package/components/hooks/use-breakpoint.d.ts +49 -0
  53. package/components/hooks/use-breakpoint.js +73 -0
  54. package/components/hooks/use-breakpoint.js.map +1 -0
  55. package/package.json +9 -1
  56. package/pihanga-shadcn.css +1 -1
  57. package/mountain-snow.svg +0 -4
  58. package/r/badge.json +0 -35
  59. package/r/box.json +0 -26
  60. package/r/button.json +0 -39
  61. package/r/checkbox.json +0 -31
  62. package/r/conditional.json +0 -30
  63. package/r/dataTable.json +0 -43
  64. package/r/dialog.json +0 -36
  65. package/r/dropDownMenu.json +0 -47
  66. package/r/field.json +0 -35
  67. package/r/flexGrid.json +0 -32
  68. package/r/form.json +0 -37
  69. package/r/framework.json +0 -28
  70. package/r/graphin.json +0 -42
  71. package/r/input.json +0 -35
  72. package/r/jsonViewer.json +0 -40
  73. package/r/list.json +0 -39
  74. package/r/loadingOverlay.json +0 -39
  75. package/r/loadingSkeleton.json +0 -30
  76. package/r/markdownViewer.json +0 -51
  77. package/r/menu.json +0 -34
  78. package/r/modeToggle.json +0 -36
  79. package/r/navbarSearch.json +0 -31
  80. package/r/pageWithNavbar.json +0 -46
  81. package/r/pasteTarget.json +0 -41
  82. package/r/pihanga-base.json +0 -11
  83. package/r/pihanga-cards-icons.json +0 -16
  84. package/r/pihanga-cards-types.json +0 -16
  85. package/r/pihanga-hook-use-is-touch-device.json +0 -15
  86. package/r/pihanga-lib-utils.json +0 -18
  87. package/r/pihanga-theme-provider.json +0 -25
  88. package/r/pihanga-ui-extras.json +0 -68
  89. package/r/registry.json +0 -171
  90. package/r/resizable.json +0 -35
  91. package/r/select.json +0 -35
  92. package/r/stack.json +0 -27
  93. package/r/stepper.json +0 -39
  94. package/r/switch.json +0 -36
  95. package/r/tabs.json +0 -33
  96. package/r/textField.json +0 -32
  97. package/r/toast.json +0 -32
  98. package/r/toggleGroup.json +0 -35
  99. package/r/typography.json +0 -31
@@ -0,0 +1,413 @@
1
+ # AGENT.building-cards.md — creating new pihanga-shadcn cards
2
+
3
+ > **Scope:** adding a new card *type* to `src/cards/` in this repository.
4
+ > Read [`AGENT.md`](./AGENT.md) first for orientation and universal rules.
5
+ > If you only need to *use* existing cards in an app, switch to
6
+ > [`AGENT.using-cards.md`](./AGENT.using-cards.md).
7
+
8
+ ---
9
+
10
+ ## ⚠️ Hard constraints before you write a single line
11
+
12
+ 1. **`src/components/` is read-only.** It is managed exclusively by the shadcn
13
+ CLI (`npx shadcn@latest add <name>`). Never create or edit files there.
14
+ All custom code belongs in `src/cards/<cardName>/`.
15
+
16
+ 2. **Never use `children` as a prop name.** It conflicts with React's type
17
+ system. Use `content`, `contentCard` (a `PiCardRef`), or `items` instead.
18
+
19
+ 3. **`@pihanga2/core` and React are always available** — never list them in
20
+ `dependencies.json`.
21
+
22
+ 4. **Every card folder must have a `dependencies.json`** — even if the card has
23
+ no external dependencies (use `{}`-valued sections as an explicit "none").
24
+
25
+ 5. **Form-aware input cards must use `useFormContext()`** — see
26
+ [Form-aware inputs](#form-aware-input-cards) below.
27
+
28
+ ---
29
+
30
+ ## Standard card folder layout
31
+
32
+ ```
33
+ src/cards/<cardName>/
34
+ index.ts # registration entry-point + re-exports
35
+ <cardName>.types.ts # card id, Props, Events, action/event wiring
36
+ <cardName>.component.tsx # React implementation (PiCardProps)
37
+ <cardName>.example.ts # playground definition (STRONGLY recommended)
38
+ dependencies.json # required — npm deps beyond core/react
39
+ <cardName>.css # optional local styles
40
+ <cardName>.test.tsx # optional unit tests
41
+ ```
42
+
43
+ The minimum viable card is: `index.ts` + `*.types.ts` + `*.component.tsx` +
44
+ `dependencies.json`.
45
+
46
+ ---
47
+
48
+ ## Step-by-step checklist for a new card
49
+
50
+ ### 1 — Choose a card name (string id)
51
+
52
+ Pick a stable string constant. Convention: `"shad/<cardName>"` for cards in
53
+ this repo (lower-case, slash-separated).
54
+
55
+ ```ts
56
+ // src/cards/myCard/myCard.types.ts
57
+ export const MY_CARD = "shad/my-card";
58
+ ```
59
+
60
+ This string must match the `name` passed to `registerCardComponent` and the
61
+ `cardId` in the example file.
62
+
63
+ ---
64
+
65
+ ### 2 — Define Props and Events (`*.types.ts`)
66
+
67
+ A typical types file has five parts:
68
+
69
+ #### a) Card declaration factory
70
+
71
+ ```ts
72
+ import {createCardDeclaration} from "@pihanga2/core";
73
+
74
+ export const MyCard = createCardDeclaration<MyCardProps, MyCardEvents>(MY_CARD);
75
+ ```
76
+
77
+ This creates the factory callers use: `MyCard({ label: "Hi" })`.
78
+
79
+ #### b) Actions the card can emit
80
+
81
+ ```ts
82
+ import {registerActions} from "@pihanga2/core";
83
+
84
+ export const MY_CARD_ACTION = registerActions(MY_CARD, ["clicked", "changed"]);
85
+ ```
86
+
87
+ #### c) Event payload types
88
+
89
+ ```ts
90
+ export type MyCardClickedEvent = { id?: string };
91
+ export type MyCardChangedEvent = { id?: string; value: string };
92
+ ```
93
+
94
+ #### d) Typed `onXxx` consumer helpers
95
+
96
+ ```ts
97
+ import {createOnAction} from "@pihanga2/core";
98
+
99
+ export const onMyCardClicked = createOnAction<MyCardClickedEvent>(
100
+ MY_CARD_ACTION.CLICKED,
101
+ );
102
+ export const onMyCardChanged = createOnAction<MyCardChangedEvent>(
103
+ MY_CARD_ACTION.CHANGED,
104
+ );
105
+ ```
106
+
107
+ #### e) Props and Events types
108
+
109
+ ```ts
110
+ export type MyCardProps = {
111
+ id?: string;
112
+ label: string;
113
+ disabled?: boolean;
114
+ // ⚠️ Do NOT use `children` — use `content` or `contentCard` instead
115
+ };
116
+
117
+ export type MyCardEvents = {
118
+ onClicked: MyCardClickedEvent;
119
+ onChanged: MyCardChangedEvent;
120
+ };
121
+ ```
122
+
123
+ ---
124
+
125
+ ### 3 — Implement the React component (`*.component.tsx`)
126
+
127
+ ```tsx
128
+ import React from "react";
129
+ import {PiCardProps} from "@pihanga2/core";
130
+ import type {MyCardProps, MyCardEvents} from "./myCard.types";
131
+
132
+ export const MyCardComponent = (
133
+ props: PiCardProps<MyCardProps, MyCardEvents>,
134
+ ) => {
135
+ const {cardName, id, label, disabled, onClicked} = props;
136
+
137
+ return (
138
+ <div data-pihanga={cardName}> {/* always set data-pihanga for debugging */}
139
+ <button
140
+ disabled={disabled}
141
+ onClick={() => onClicked({id})}
142
+ >
143
+ {label}
144
+ </button>
145
+ </div>
146
+ );
147
+ };
148
+ ```
149
+
150
+ Key rules:
151
+ - Always set `data-pihanga={cardName}` on the outermost DOM element.
152
+ - Render nested cards with `<Card cardName={ref} parentCard={cardName} />`
153
+ (import `Card` from `@pihanga2/core`).
154
+ - Call event callbacks (`onClicked`, etc.) directly — Pihanga dispatches the
155
+ corresponding Redux action automatically.
156
+
157
+ #### Rendering a nested card
158
+
159
+ ```tsx
160
+ import {Card} from "@pihanga2/core";
161
+ import type {PiCardRef} from "@pihanga2/core";
162
+
163
+ // In props: contentCard?: PiCardRef
164
+ <Card cardName={contentCard} parentCard={cardName} />
165
+ ```
166
+
167
+ ---
168
+
169
+ ### 4 — Register the card (`index.ts`)
170
+
171
+ ```ts
172
+ import {registerCardComponent, actionTypesToEvents} from "@pihanga2/core";
173
+ import {MyCardComponent} from "./myCard.component";
174
+ import {MY_CARD, MY_CARD_ACTION} from "./myCard.types";
175
+
176
+ export * from "./myCard.types";
177
+
178
+ registerCardComponent({
179
+ name: MY_CARD,
180
+ component: MyCardComponent,
181
+ events: actionTypesToEvents(MY_CARD_ACTION),
182
+ });
183
+ ```
184
+
185
+ For a card with no events, omit the `events` field:
186
+
187
+ ```ts
188
+ registerCardComponent({name: MY_CARD, component: MyCardComponent});
189
+ ```
190
+
191
+ For cards needing global initialisation (plugin registries, etc.), use
192
+ `register(...)` instead:
193
+
194
+ ```ts
195
+ import {register} from "@pihanga2/core";
196
+
197
+ register((r) => {
198
+ registerCardComponent({name: MY_CARD, component: MyCardComponent, events: ...});
199
+ // additional global setup…
200
+ });
201
+ ```
202
+
203
+ ---
204
+
205
+ ### 5 — Create `dependencies.json` (required)
206
+
207
+ List every npm package **directly imported** by any `.ts`/`.tsx` file in the
208
+ card folder. Exclude `@pihanga2/core`, `react`, `react-dom`, and
209
+ path-aliased local files.
210
+
211
+ ```json
212
+ {
213
+ "dependencies": {
214
+ "some-package": "^1.2.3"
215
+ },
216
+ "devDependencies": {}
217
+ }
218
+ ```
219
+
220
+ For a card with no external deps:
221
+
222
+ ```json
223
+ {
224
+ "dependencies": {},
225
+ "devDependencies": {}
226
+ }
227
+ ```
228
+
229
+ **Auto-generate / update all cards at once:**
230
+
231
+ ```sh
232
+ yarn gen-card-deps # update all cards
233
+ yarn gen-card-deps --dry-run # preview without writing
234
+ yarn gen-card-deps --card myCard # single card only
235
+ ```
236
+
237
+ > If the script reports `UNKNOWN - add to root package.json`, the package is
238
+ > missing from the root `package.json` — add it there first, then re-run.
239
+
240
+ ---
241
+
242
+ ### 6 — Create the playground example (`*.example.ts`) — strongly recommended
243
+
244
+ A card without an `*.example.ts` **does not appear in the playground**.
245
+
246
+ ```ts
247
+ import {definePlayground} from "@/playground/definePlayground";
248
+ import {MyCard, onMyCardClicked, type MyCardProps} from "./index";
249
+
250
+ export default definePlayground<MyCardProps>({
251
+ // ── Required ─────────────────────────────────────────────────────────
252
+ cardId: "shad/my-card",
253
+ title: "My Card",
254
+ introduction: `
255
+ One-sentence summary of what this card does.
256
+
257
+ When to use it, what it supports, any important constraints or gotchas.
258
+ `.trim(),
259
+
260
+ // ── Live preview ─────────────────────────────────────────────────────
261
+ preview: (props) => MyCard(props),
262
+ defaultProps: { label: "Hello" }, // must be JSON-serialisable (no memo)
263
+
264
+ // ── Usage scenarios (tabs) ───────────────────────────────────────────
265
+ facets: [
266
+ {
267
+ id: "basic",
268
+ title: "Basic",
269
+ description: "The most common usage — plain label with default styling.",
270
+ props: { label: "Hello" },
271
+ },
272
+ {
273
+ id: "disabled",
274
+ title: "Disabled",
275
+ description: "Use when the action is not currently available.",
276
+ props: { label: "Unavailable", disabled: true },
277
+ },
278
+ ],
279
+
280
+ // ── Interactive prop editor ───────────────────────────────────────────
281
+ controls: [
282
+ { prop: "label", type: "text", label: "Label", placeholder: "Card text…" },
283
+ { prop: "disabled", type: "boolean", label: "Disabled" },
284
+ ],
285
+
286
+ // ── Event logging (omit if no events) ────────────────────────────────
287
+ registerEvents: (r, logEvent) => {
288
+ onMyCardClicked(r, (state, ev) => {
289
+ logEvent(state, "onMyCardClicked", { id: ev.id });
290
+ });
291
+ },
292
+
293
+ // ── Copy-paste usage snippet ──────────────────────────────────────────
294
+ note: `
295
+ \`\`\`ts
296
+ import {registerCard, register} from "@pihanga2/core";
297
+ import {MyCard, onMyCardClicked} from "@/cards/myCard";
298
+
299
+ register((r) => {
300
+ onMyCardClicked(r, (state, {id}) => {
301
+ if (id === "save") state.isSaving = true;
302
+ });
303
+ });
304
+
305
+ registerCard("myApp/widget", MyCard({
306
+ id: "save",
307
+ label: "Save",
308
+ }));
309
+ \`\`\`
310
+ `.trim(),
311
+ });
312
+ ```
313
+
314
+ #### `definePlayground` field reference
315
+
316
+ | Field | Required | Purpose |
317
+ |---|---|---|
318
+ | `cardId` | ✅ | Must match the card's `CARD_ID` constant |
319
+ | `title` | ✅ | Human-readable name shown in the sidebar |
320
+ | `introduction` | ✅ | Markdown prose — what it is, when to use it, gotchas |
321
+ | `preview` | ✅ | Factory for the live preview: `(props) => MyCard(props)` |
322
+ | `defaultProps` | ✅ | JSON-serialisable initial prop values (no `memo()`) |
323
+ | `facets` | Recommended | Named usage scenarios (3–6 tabs) |
324
+ | `controls` | Recommended | Interactive prop editor entries |
325
+ | `note` | Recommended | Copy-paste `registerCard(...)` snippets |
326
+ | `registerEvents` | If card emits events | Log every `onXxx` handler via `logEvent` |
327
+
328
+ Control types: `"text"`, `"boolean"`, `"token"` (enum picker), `"number"`.
329
+ Use dot notation for nested props: `"opts.variant"`.
330
+
331
+ ---
332
+
333
+ ## Form-aware input cards
334
+
335
+ Any card that acts as a form input (text field, select, checkbox, etc.) **must**
336
+ work both inside and outside a `pi/form` card.
337
+
338
+ ```tsx
339
+ import {useFormContext} from "@/cards/form/form.context";
340
+
341
+ export const MyInputComponent = (props: PiCardProps<MyInputProps, MyInputEvents>) => {
342
+ const {name, value: propValue, onChanged} = props;
343
+
344
+ const form = useFormContext(); // always safe to call
345
+ const useFormData = form.isInForm && Boolean(name);
346
+
347
+ const value = useFormData
348
+ ? ((form.formData[name!] as string | undefined) ?? "")
349
+ : propValue;
350
+
351
+ function handleChange(newValue: string) {
352
+ if (useFormData) {
353
+ form.handleChange(name!, newValue); // update form state
354
+ } else {
355
+ onChanged({name, value: newValue}); // dispatch Pihanga event
356
+ }
357
+ }
358
+
359
+ return <input value={value} onChange={e => handleChange(e.target.value)} />;
360
+ };
361
+ ```
362
+
363
+ `useFormContext()` fields:
364
+
365
+ | Field | Type | Purpose |
366
+ |---|---|---|
367
+ | `isInForm` | `boolean` | `true` when inside a `pi/form` card |
368
+ | `formData` | `Record<string, unknown>` | Current field values keyed by `name` |
369
+ | `errors` | `Record<string, string>` | Validation errors keyed by `name` |
370
+ | `handleChange` | `(field, value) => void` | Push a new field value |
371
+ | `setError` | `(field, error \| null) => void` | Set or clear a validation error |
372
+
373
+ Rules:
374
+ - **Always call `useFormContext()`** unconditionally — it is a no-op outside a form.
375
+ - **Gate on `isInForm && Boolean(name)`** before using form data.
376
+ - **Always include `name?: string` in the Props type** to allow form participation.
377
+ - See `src/cards/textField/textField.component.tsx` and
378
+ `src/cards/checkbox/checkbox.component.tsx` for complete working examples.
379
+
380
+ ---
381
+
382
+ ## Where to look for real examples
383
+
384
+ | Need | Card to study |
385
+ |---|---|
386
+ | Minimal card structure | `src/cards/emptyCard.tsx` |
387
+ | Button with variants, icons, tooltip | `src/cards/button/` |
388
+ | Dropdown / context menu | `src/cards/dropDownMenu/` |
389
+ | Form input pattern | `src/cards/textField/`, `src/cards/checkbox/` |
390
+ | Layout card composing children | `src/cards/flexGrid/`, `src/cards/stack/` |
391
+ | Card with local CSS | `src/cards/pasteTarget/` |
392
+ | Card with Radix `asChild` | `src/cards/dropDownMenu/` |
393
+
394
+ ---
395
+
396
+ ## After building the card — publish checklist
397
+
398
+ - [ ] `dependencies.json` exists and is accurate (`yarn gen-card-deps --card <name>`)
399
+ - [ ] `*.example.ts` created with `introduction`, `preview`, `defaultProps`, at
400
+ least two `facets`, and `registerEvents` if the card emits actions
401
+ - [ ] Card appears in the playground (run `yarn dev` and navigate to the explorer)
402
+ - [ ] `make gen-registry` runs without errors and emits a valid `public/r/<cardName>.json`
403
+ - [ ] No files added to `src/components/`
404
+
405
+ ---
406
+
407
+ ## Related files
408
+
409
+ - `src/playground/playground.pihanga.ts` — how cards are wired into the playground app
410
+ - `src/app.pihanga.ts` — top-level frame/page setup (reference for `registerFramework`)
411
+ - `src/cards/BUILDING_CARDS_HOWTO.md` — human-oriented narrative version of this guide
412
+ - `scripts/gen-card-dependencies.mjs` — dependency scanner
413
+ - `scripts/gen-registry.mjs` — registry builder
package/AGENT.md ADDED
@@ -0,0 +1,57 @@
1
+ # AGENT.md — pihanga-shadcn (entry point)
2
+
3
+ AI coding assistants (Cline, Cursor, Windsurf, Claude, etc.) should read this
4
+ file **first** at the start of every task in a project that involves
5
+ `pihanga-shadcn`, then follow the pointer below that matches the task.
6
+
7
+ ---
8
+
9
+ ## What is pihanga-shadcn?
10
+
11
+ `pihanga-shadcn` is a library of **Pihanga card components** built on top of
12
+ [shadcn/ui](https://ui.shadcn.com) and [Radix UI](https://radix-ui.com). Cards
13
+ are distributed as a **shadcn-style copy-on-install registry** — consumers run
14
+ `npx shadcn@latest add <url>` and the CLI copies TypeScript source files
15
+ directly into the project.
16
+
17
+ Registry base URL:
18
+ ```
19
+ https://ivcap-works.github.io/pihanga-shadcn/r
20
+ ```
21
+
22
+ Source repository:
23
+ ```
24
+ https://github.com/ivcap-works/pihanga-shadcn
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Which guide do you need?
30
+
31
+ | If your task is … | Read this file |
32
+ |---|---|
33
+ | **Getting started** — brand-new project, shadcn/ui + pihanga setup, file layout | [`AGENTS.getting-started.md`](./AGENTS.getting-started.md) |
34
+ | **Using** existing cards — install, wire, compose, navigate | [`AGENT.using-cards.md`](./AGENT.using-cards.md) |
35
+ | **Building** new cards — create a new card type for this repo | [`AGENT.building-cards.md`](./AGENT.building-cards.md) |
36
+
37
+ If you are **starting from scratch**, read `AGENTS.getting-started.md` first — it
38
+ covers Vite setup, shadcn/ui init, pihanga-core installation, and the initial file
39
+ layout. Then continue with `AGENT.using-cards.md` for wiring and composing cards.
40
+
41
+ If you are adding cards to an **existing project**, go straight to
42
+ `AGENT.using-cards.md`.
43
+
44
+ Switch to `AGENT.building-cards.md` only when you need to add a new card *type*
45
+ to `src/cards/` itself.
46
+
47
+ ---
48
+
49
+ ## Universal rules (apply to both guides)
50
+
51
+ - **`src/components/` is read-only** — it is managed by the shadcn CLI. Never
52
+ add custom code there. All custom code lives in `src/cards/<cardName>/`.
53
+ - **`@pihanga2/core`** is always available; do not list it in `dependencies.json`.
54
+ - **One `registerFramework()` per app boot** — composing multiple init functions
55
+ must ensure only one of them calls it.
56
+ - **`children` is a reserved React prop** — never use it in a card's `…Props`
57
+ type; use `content`, `contentCard`, or `items` instead.