@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.
- package/AGENT.building-cards.md +413 -0
- package/AGENT.md +57 -0
- package/AGENT.using-cards.md +1051 -0
- package/cards/box/box.component.js +2 -2
- package/cards/box/box.component.js.map +1 -1
- package/cards/box/box.types.d.ts +1 -4
- package/cards/box/box.types.js.map +1 -1
- package/cards/button/button.types.d.ts +3 -0
- package/cards/button/button.types.js +2 -2
- package/cards/button/button.types.js.map +1 -1
- package/cards/button/index.js +5 -5
- package/cards/chartGraph/chartGraph.component.d.ts +4 -0
- package/cards/chartGraph/chartGraph.types.d.ts +102 -0
- package/cards/chartGraph/index.d.ts +1 -0
- package/cards/conditional/conditional.component.d.ts +14 -3
- package/cards/conditional/conditional.component.js +17 -9
- package/cards/conditional/conditional.component.js.map +1 -1
- package/cards/conditional/conditional.types.d.ts +100 -8
- package/cards/conditional/conditional.types.js.map +1 -1
- package/cards/core-index.js +31 -31
- package/cards/fileDrop/fileDrop.component.d.ts +5 -0
- package/cards/fileDrop/fileDrop.types.d.ts +43 -0
- package/cards/fileDrop/index.d.ts +2 -0
- package/cards/graphin/contextMenu.component.d.ts +28 -0
- package/cards/graphin/eventDispatcher.component.d.ts +8 -0
- package/cards/graphin/graphin.component.d.ts +2 -2
- package/cards/graphin/graphin.playground-cards.d.ts +1 -0
- package/cards/graphin/graphin.types.d.ts +444 -4
- package/cards/graphin/graphinOpHandler.component.d.ts +8 -0
- package/cards/graphin/index.d.ts +1 -0
- package/cards/graphin/tooltip.component.d.ts +13 -1
- package/cards/icons.js +7 -3
- package/cards/icons.js.map +1 -1
- package/cards/index.d.ts +1 -0
- package/cards/slider/index.d.ts +1 -0
- package/cards/slider/slider.component.d.ts +4 -0
- package/cards/slider/slider.types.d.ts +102 -0
- package/cards/sliderValue/index.d.ts +1 -0
- package/cards/sliderValue/sliderValue.component.d.ts +15 -0
- package/cards/sliderValue/sliderValue.types.d.ts +21 -0
- package/cards/stack/stack.types.d.ts +19 -22
- package/cards/stack/stack.types.js +1 -1
- package/cards/stack/stack.types.js.map +1 -1
- package/cards/tabs/index.js +4 -4
- package/cards/tabs/tabs.component.js +51 -25
- package/cards/tabs/tabs.component.js.map +1 -1
- package/cards/tabs/tabs.types.d.ts +12 -0
- package/cards/tabs/tabs.types.js +2 -2
- package/cards/tabs/tabs.types.js.map +1 -1
- package/cards/types.d.ts +4 -0
- package/cards/types.js.map +1 -1
- package/components/hooks/use-breakpoint.d.ts +49 -0
- package/components/hooks/use-breakpoint.js +73 -0
- package/components/hooks/use-breakpoint.js.map +1 -0
- package/package.json +9 -1
- package/pihanga-shadcn.css +1 -1
- package/mountain-snow.svg +0 -4
- package/r/badge.json +0 -35
- package/r/box.json +0 -26
- package/r/button.json +0 -39
- package/r/checkbox.json +0 -31
- package/r/conditional.json +0 -30
- package/r/dataTable.json +0 -43
- package/r/dialog.json +0 -36
- package/r/dropDownMenu.json +0 -47
- package/r/field.json +0 -35
- package/r/flexGrid.json +0 -32
- package/r/form.json +0 -37
- package/r/framework.json +0 -28
- package/r/graphin.json +0 -42
- package/r/input.json +0 -35
- package/r/jsonViewer.json +0 -40
- package/r/list.json +0 -39
- package/r/loadingOverlay.json +0 -39
- package/r/loadingSkeleton.json +0 -30
- package/r/markdownViewer.json +0 -51
- package/r/menu.json +0 -34
- package/r/modeToggle.json +0 -36
- package/r/navbarSearch.json +0 -31
- package/r/pageWithNavbar.json +0 -46
- package/r/pasteTarget.json +0 -41
- package/r/pihanga-base.json +0 -11
- package/r/pihanga-cards-icons.json +0 -16
- package/r/pihanga-cards-types.json +0 -16
- package/r/pihanga-hook-use-is-touch-device.json +0 -15
- package/r/pihanga-lib-utils.json +0 -18
- package/r/pihanga-theme-provider.json +0 -25
- package/r/pihanga-ui-extras.json +0 -68
- package/r/registry.json +0 -171
- package/r/resizable.json +0 -35
- package/r/select.json +0 -35
- package/r/stack.json +0 -27
- package/r/stepper.json +0 -39
- package/r/switch.json +0 -36
- package/r/tabs.json +0 -33
- package/r/textField.json +0 -32
- package/r/toast.json +0 -32
- package/r/toggleGroup.json +0 -35
- 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.
|