@pihanga2/shadcn 0.2.1 → 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.
- package/AGENT.building-cards.md +208 -3
- package/AGENT.md +58 -3
- package/AGENT.using-cards.md +142 -4
- package/README.md +4 -1
- package/cards/badge/badge.component.js +1 -0
- package/cards/badge/badge.component.js.map +1 -1
- package/cards/box/box.component.js +1 -1
- package/cards/box/box.component.js.map +1 -1
- package/cards/box/box.types.d.ts +0 -8
- package/cards/box/box.types.js.map +1 -1
- package/cards/button/button.types.d.ts +1 -0
- package/cards/button/button.types.js.map +1 -1
- package/cards/{chartGraph/chartGraph.component.d.ts → chart/chart.component.d.ts} +1 -1
- package/cards/chart/chart.types.d.ts +223 -0
- package/cards/chart/index.d.ts +1 -0
- package/cards/checkbox/checkbox.component.js +1 -0
- package/cards/checkbox/checkbox.component.js.map +1 -1
- package/cards/checkbox/checkbox.types.d.ts +4 -0
- package/cards/checkbox/checkbox.types.js +2 -2
- package/cards/checkbox/checkbox.types.js.map +1 -1
- package/cards/checkbox/index.js +5 -5
- package/cards/conditional/conditional.types.d.ts +20 -5
- package/cards/conditional/conditional.types.js.map +1 -1
- package/cards/core-index.d.ts +3 -0
- package/cards/core-index.js +36 -29
- package/cards/dataTable/dataTable.types.js.map +1 -1
- package/cards/dataTable/dataTableRowDetail.component.js +1 -0
- package/cards/dataTable/dataTableRowDetail.component.js.map +1 -1
- package/cards/field/field.types.js.map +1 -1
- package/cards/fileDrop/fileDrop.component.js +90 -0
- package/cards/fileDrop/fileDrop.component.js.map +1 -0
- package/cards/fileDrop/fileDrop.types.js +11 -0
- package/cards/fileDrop/fileDrop.types.js.map +1 -0
- package/cards/fileDrop/index.js +13 -0
- package/cards/fileDrop/index.js.map +1 -0
- package/cards/flexGrid/flexGrid.types.js.map +1 -1
- package/cards/form/form.types.d.ts +4 -0
- package/cards/form/form.types.js +2 -2
- package/cards/form/form.types.js.map +1 -1
- package/cards/form/index.js +5 -5
- package/cards/graphin/graphin.types.d.ts +37 -4
- package/cards/input/index.js +5 -5
- package/cards/input/input.component.js +1 -0
- package/cards/input/input.component.js.map +1 -1
- package/cards/input/input.types.d.ts +10 -2
- package/cards/input/input.types.js +2 -2
- package/cards/input/input.types.js.map +1 -1
- package/cards/list/list.types.js +1 -0
- package/cards/list/list.types.js.map +1 -1
- package/cards/pageWithNavbar/pageWithNavbar.component.d.ts +5 -7
- package/cards/pageWithNavbar/pageWithNavbar.component.js +1 -1
- package/cards/pageWithNavbar/pageWithNavbar.component.js.map +1 -1
- package/cards/pasteTarget/pasteTarget.component.js.map +1 -1
- package/cards/select/index.js +5 -5
- package/cards/select/select.component.js +1 -0
- package/cards/select/select.component.js.map +1 -1
- package/cards/select/select.types.d.ts +12 -0
- package/cards/select/select.types.js +2 -2
- package/cards/select/select.types.js.map +1 -1
- package/cards/slider/index.js +13 -0
- package/cards/slider/index.js.map +1 -0
- package/cards/slider/slider.component.js +64 -0
- package/cards/slider/slider.component.js.map +1 -0
- package/cards/slider/slider.types.d.ts +12 -4
- package/cards/slider/slider.types.js +7 -0
- package/cards/slider/slider.types.js.map +1 -0
- package/cards/sliderValue/index.js +12 -0
- package/cards/sliderValue/index.js.map +1 -0
- package/cards/sliderValue/sliderValue.component.js +33 -0
- package/cards/sliderValue/sliderValue.component.js.map +1 -0
- package/cards/sliderValue/sliderValue.types.js +7 -0
- package/cards/sliderValue/sliderValue.types.js.map +1 -0
- package/cards/stack/stack.component.js +1 -1
- package/cards/stack/stack.component.js.map +1 -1
- package/cards/stack/stack.types.js.map +1 -1
- package/cards/switch/index.js +5 -5
- package/cards/switch/switch.component.js +1 -0
- package/cards/switch/switch.component.js.map +1 -1
- package/cards/switch/switch.types.d.ts +4 -0
- package/cards/switch/switch.types.js +2 -2
- package/cards/switch/switch.types.js.map +1 -1
- package/cards/textField/index.js +5 -5
- package/cards/textField/textField.component.js +1 -0
- package/cards/textField/textField.component.js.map +1 -1
- package/cards/textField/textField.types.d.ts +4 -0
- package/cards/textField/textField.types.js +2 -2
- package/cards/textField/textField.types.js.map +1 -1
- package/cards/toast/index.js +7 -7
- package/cards/toast/toast.component.js.map +1 -1
- package/cards/toast/toast.types.d.ts +4 -0
- package/cards/toast/toast.types.js +2 -2
- package/cards/toast/toast.types.js.map +1 -1
- package/cards/toggleGroup/index.js +5 -5
- package/cards/toggleGroup/toggleGroup.component.js +1 -0
- package/cards/toggleGroup/toggleGroup.component.js.map +1 -1
- package/cards/toggleGroup/toggleGroup.types.d.ts +4 -0
- package/cards/toggleGroup/toggleGroup.types.js +2 -2
- package/cards/toggleGroup/toggleGroup.types.js.map +1 -1
- package/cards/typography/typography.types.js +1 -0
- package/cards/typography/typography.types.js.map +1 -1
- package/components/ui/slider.js +36 -0
- package/components/ui/slider.js.map +1 -0
- package/components/ui/toggle-group.js +1 -0
- package/components/ui/toggle-group.js.map +1 -1
- package/package.json +19 -2
- package/pihanga-shadcn.css +1 -1
- package/cards/chartGraph/chartGraph.types.d.ts +0 -102
- package/cards/chartGraph/index.d.ts +0 -1
package/AGENT.building-cards.md
CHANGED
|
@@ -1,13 +1,217 @@
|
|
|
1
1
|
# AGENT.building-cards.md — creating new pihanga-shadcn cards
|
|
2
2
|
|
|
3
|
-
> **Scope:**
|
|
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
|
-
##
|
|
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
|
-
|
|
|
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
|
@@ -32,7 +32,8 @@ https://github.com/ivcap-works/pihanga-shadcn
|
|
|
32
32
|
|---|---|
|
|
33
33
|
| **Getting started** — brand-new project, shadcn/ui + pihanga setup, file layout | [`AGENTS.getting-started.md`](./AGENTS.getting-started.md) |
|
|
34
34
|
| **Using** existing cards — install, wire, compose, navigate | [`AGENT.using-cards.md`](./AGENT.using-cards.md) |
|
|
35
|
-
| **Building
|
|
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) |
|
|
36
37
|
|
|
37
38
|
If you are **starting from scratch**, read `AGENTS.getting-started.md` first — it
|
|
38
39
|
covers Vite setup, shadcn/ui init, pihanga-core installation, and the initial file
|
|
@@ -41,8 +42,55 @@ layout. Then continue with `AGENT.using-cards.md` for wiring and composing card
|
|
|
41
42
|
If you are adding cards to an **existing project**, go straight to
|
|
42
43
|
`AGENT.using-cards.md`.
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
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.
|
|
46
94
|
|
|
47
95
|
---
|
|
48
96
|
|
|
@@ -55,3 +103,10 @@ to `src/cards/` itself.
|
|
|
55
103
|
must ensure only one of them calls it.
|
|
56
104
|
- **`children` is a reserved React prop** — never use it in a card's `…Props`
|
|
57
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.
|
package/AGENT.using-cards.md
CHANGED
|
@@ -30,9 +30,16 @@
|
|
|
30
30
|
> shadcn/ui and Radix UI updates, it won't appear in the playground
|
|
31
31
|
> automatically, and it won't benefit from upstream bug fixes.
|
|
32
32
|
>
|
|
33
|
-
>
|
|
34
|
-
>
|
|
35
|
-
>
|
|
33
|
+
> **⭐ If no single card covers your need but existing cards could be combined:**
|
|
34
|
+
> Build a **meta card** — a TypeScript-only mapper that assembles existing cards
|
|
35
|
+
> into a reusable, self-contained widget. Meta cards require no JSX, no React
|
|
36
|
+
> imports, and no DOM knowledge. They are easier to maintain, extend, and test
|
|
37
|
+
> than a bespoke React component. See
|
|
38
|
+
> [Meta cards — building complex UIs by composition](#meta-cards--building-complex-uis-by-composition)
|
|
39
|
+
> below, or the full guide in
|
|
40
|
+
> [`AGENT.building-cards.md`](./AGENT.building-cards.md#meta-cards--composing-new-widgets-from-existing-cards).
|
|
41
|
+
>
|
|
42
|
+
> **If no suitable card or card combination exists:**
|
|
36
43
|
> - Unless the card would expose confidential business logic, please
|
|
37
44
|
> **open an issue** at
|
|
38
45
|
> `https://github.com/ivcap-works/pihanga-shadcn/issues` describing what you
|
|
@@ -424,6 +431,137 @@ registerCard("myApp/save", Button({
|
|
|
424
431
|
|
|
425
432
|
---
|
|
426
433
|
|
|
434
|
+
## Meta cards — building complex UIs by composition
|
|
435
|
+
|
|
436
|
+
> **⭐ This is the recommended approach whenever you need a reusable UI widget
|
|
437
|
+
> that combines several existing cards.** Prefer a meta card over writing a new
|
|
438
|
+
> React component; meta cards are easier to maintain, extend, test, and port.
|
|
439
|
+
|
|
440
|
+
A **meta card** is a TypeScript-only module that assembles existing primitive
|
|
441
|
+
cards (or other meta cards) inside a mapper function. No JSX, no React hooks,
|
|
442
|
+
no DOM knowledge is required.
|
|
443
|
+
|
|
444
|
+
### Decision guide
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
Do I need a complex, multi-widget UI?
|
|
448
|
+
│
|
|
449
|
+
├─► Can I build it from existing cards? ──► YES ──► Write a META CARD ⭐
|
|
450
|
+
│ (registerMetaCard)
|
|
451
|
+
└─► NO — needs a DOM API / third-party React hook
|
|
452
|
+
└─► Only then: write a PRIMITIVE CARD
|
|
453
|
+
(registerCardComponent + React component)
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Minimal meta card skeleton
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
// src/myWidget.card.ts
|
|
460
|
+
import {
|
|
461
|
+
createCardDeclaration, createOnAction, registerActions, registerMetaCard,
|
|
462
|
+
} from "@pihanga2/core";
|
|
463
|
+
import type {PiCardDef, PiMapProps, PiRegisterMetaCard, ReduxState, RegisterCardF} from "@pihanga2/core";
|
|
464
|
+
import {Stack, Button, Typography} from "@pihanga2/shadcn"; // use whatever cards you need
|
|
465
|
+
|
|
466
|
+
// ── 1. Identity (use "meta/<name>" convention for app-local meta cards) ─────
|
|
467
|
+
const MY_WIDGET_CARD = "meta/my-widget";
|
|
468
|
+
|
|
469
|
+
// ── 2. Public factory ────────────────────────────────────────────────────────
|
|
470
|
+
export const MyWidget = createCardDeclaration<MyWidgetProps, MyWidgetEvents>(MY_WIDGET_CARD);
|
|
471
|
+
|
|
472
|
+
// ── 3. Actions ───────────────────────────────────────────────────────────────
|
|
473
|
+
export const MY_WIDGET_ACTION = registerActions(MY_WIDGET_CARD, ["submitted"]);
|
|
474
|
+
export const onMyWidgetSubmitted = createOnAction<MyWidgetSubmitEvent>(MY_WIDGET_ACTION.SUBMITTED);
|
|
475
|
+
|
|
476
|
+
// ── 4. Types ─────────────────────────────────────────────────────────────────
|
|
477
|
+
type MyWidgetProps = { label: string; value: string };
|
|
478
|
+
type MyWidgetSubmitEvent = { value: string };
|
|
479
|
+
type MyWidgetEvents = { onSubmit: MyWidgetSubmitEvent };
|
|
480
|
+
type MapperProps = MyWidgetProps & MyWidgetEvents;
|
|
481
|
+
|
|
482
|
+
// ── 5. Mapper ────────────────────────────────────────────────────────────────
|
|
483
|
+
function MyWidgetMapper(
|
|
484
|
+
_: string,
|
|
485
|
+
props: PiMapProps<MapperProps, ReduxState, object>,
|
|
486
|
+
_registerCard: RegisterCardF,
|
|
487
|
+
): PiCardDef {
|
|
488
|
+
return Stack({
|
|
489
|
+
direction: "row",
|
|
490
|
+
content: [
|
|
491
|
+
Typography({ text: (_, {resolve}) => resolve(props.label) }),
|
|
492
|
+
Button({
|
|
493
|
+
label: "Submit",
|
|
494
|
+
onClickedMapper: (_, {resolve}) => ({
|
|
495
|
+
type: MY_WIDGET_ACTION.SUBMITTED,
|
|
496
|
+
value: resolve(props.value),
|
|
497
|
+
}),
|
|
498
|
+
}),
|
|
499
|
+
],
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ── 6. Register ───────────────────────────────────────────────────────────────
|
|
504
|
+
registerMetaCard({
|
|
505
|
+
type: MY_WIDGET_CARD,
|
|
506
|
+
mapper: MyWidgetMapper,
|
|
507
|
+
events: MY_WIDGET_ACTION,
|
|
508
|
+
} satisfies PiRegisterMetaCard);
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Using the meta card in your app:**
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
// src/app.pihanga.ts
|
|
515
|
+
import {MyWidget, onMyWidgetSubmitted} from "./myWidget.card";
|
|
516
|
+
import type {AppState} from "./app.state";
|
|
517
|
+
|
|
518
|
+
register((r) => {
|
|
519
|
+
onMyWidgetSubmitted<AppState>(r, (state, {value}) => {
|
|
520
|
+
state.submittedValue = value;
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
registerCard("app/myWidget", MyWidget<AppState>({
|
|
525
|
+
label: "Enter name",
|
|
526
|
+
value: (s) => s.currentValue, // reactive — updates when state changes
|
|
527
|
+
}));
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Key patterns
|
|
531
|
+
|
|
532
|
+
**`resolve(props.foo)` — unwrap lazy state selectors in child props**
|
|
533
|
+
|
|
534
|
+
A meta card prop may be a plain value *or* a state selector `(s) => s.foo`.
|
|
535
|
+
Inside child card prop functions, always use `resolve()` to unwrap it:
|
|
536
|
+
|
|
537
|
+
```ts
|
|
538
|
+
// ✅ Correct
|
|
539
|
+
Typography({ text: (_, {resolve}) => `Hello ${resolve(props.name)}` })
|
|
540
|
+
|
|
541
|
+
// ❌ Wrong — child sees a function reference, not the resolved string
|
|
542
|
+
Typography({ text: props.name })
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**`onXxxMapper` — remap child events into domain actions**
|
|
546
|
+
|
|
547
|
+
Every event-emitting card supports an `on<Event>Mapper` prop that intercepts
|
|
548
|
+
the raw event and emits a different action (or `null` to suppress):
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
Button({
|
|
552
|
+
label: "Delete",
|
|
553
|
+
onClickedMapper: (_, {resolve}) => ({
|
|
554
|
+
type: MY_WIDGET_ACTION.SUBMITTED,
|
|
555
|
+
value: resolve(props.value),
|
|
556
|
+
}),
|
|
557
|
+
})
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Full reference:** See [`AGENT.building-cards.md`](./AGENT.building-cards.md#meta-cards--composing-new-widgets-from-existing-cards)
|
|
561
|
+
and the complete annotated example at [`example/src/counter.card.ts`](./example/src/counter.card.ts).
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
427
565
|
## Version pinning
|
|
428
566
|
|
|
429
567
|
Use a git tag in the URL to pin to a specific release:
|
|
@@ -992,7 +1130,7 @@ project that was bootstrapped without the reference `index.css`:
|
|
|
992
1130
|
Both fixes are now included in the reference `src/index.css` template in
|
|
993
1131
|
*Prerequisites step 3*.
|
|
994
1132
|
|
|
995
|
-
### 2026-06 agent evaluation — ViteJS app from scratch
|
|
1133
|
+
### 2026-06 agent evaluation — ViteJS app from scratch
|
|
996
1134
|
|
|
997
1135
|
An agent built a new ViteJS + React + TypeScript app from scratch using
|
|
998
1136
|
`yarn create vite` and then manually copied pihanga-shadcn cards. The
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
-
## Included cards (
|
|
76
|
+
## Included cards (33)
|
|
77
77
|
|
|
78
78
|
- `badge`
|
|
79
79
|
- `box`
|
|
@@ -84,6 +84,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
84
84
|
- `dialog`
|
|
85
85
|
- `dropDownMenu`
|
|
86
86
|
- `field`
|
|
87
|
+
- `fileDrop`
|
|
87
88
|
- `flexGrid`
|
|
88
89
|
- `form`
|
|
89
90
|
- `framework`
|
|
@@ -97,6 +98,8 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
97
98
|
- `pageWithNavbar`
|
|
98
99
|
- `pasteTarget`
|
|
99
100
|
- `select`
|
|
101
|
+
- `slider`
|
|
102
|
+
- `sliderValue`
|
|
100
103
|
- `stack`
|
|
101
104
|
- `stepper`
|
|
102
105
|
- `switch`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.component.js","names":[],"sources":["../../../src/cards/badge/badge.component.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Badge} from \"@/components/ui/badge\";\nimport {cn} from \"@/lib/utils\";\nimport type {BadgeCardProps} from \"./badge.types\";\n\n/**\n * `shad/badge` card component.\n *\n * Renders a shadcn `<Badge>` element with a `label` and an optional `variant`.\n * The variant vocabulary is intentionally identical to the `BadgeColumn.variants`\n * map used by the `shad/data-table` card, so both can be driven from the same\n * source-of-truth value:\n *\n * \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n */\nexport const BadgeComponent = (\n props: PiCardProps<BadgeCardProps>,\n): React.ReactNode => {\n const {label, variant = \"secondary\", className, cardName} = props;\n\n return (\n <Badge\n variant={variant}\n className={cn(className)}\n data-pihanga={cardName}\n >\n {label}\n </Badge>\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"badge.component.js","names":[],"sources":["../../../src/cards/badge/badge.component.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Badge} from \"@/components/ui/badge\";\nimport {cn} from \"@/lib/utils\";\nimport type {BadgeCardProps} from \"./badge.types\";\n\n/**\n * `shad/badge` card component.\n *\n * Renders a shadcn `<Badge>` element with a `label` and an optional `variant`.\n * The variant vocabulary is intentionally identical to the `BadgeColumn.variants`\n * map used by the `shad/data-table` card, so both can be driven from the same\n * source-of-truth value:\n *\n * \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n */\nexport const BadgeComponent = (\n props: PiCardProps<BadgeCardProps>,\n): React.ReactNode => {\n const {label, variant = \"secondary\", className, cardName} = props;\n\n return (\n <Badge\n variant={variant}\n className={cn(className)}\n data-pihanga={cardName}\n >\n {label}\n </Badge>\n );\n};\n"],"mappings":";;;;;;AAgBA,IAAa,KACX,MACoB;CACpB,IAAM,EAAC,UAAO,aAAU,aAAa,cAAW,gBAAY;CAE5D,OACE,kBAAC,GAAD;EACW;EACT,WAAW,EAAG,CAAS;EACvB,gBAAc;YAEb;CACI,CAAA;AAEX"}
|
|
@@ -3,7 +3,7 @@ import "react";
|
|
|
3
3
|
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
4
4
|
//#region src/cards/box/box.component.tsx
|
|
5
5
|
var r = (r) => {
|
|
6
|
-
let { content: i, className: a, style: o, cardName: s } = r, c = { ...o
|
|
6
|
+
let { content: i, className: a, style: o, cardName: s } = r, c = { ...o };
|
|
7
7
|
r.height && (c.height = `${r.height}px`), r.width && (c.width = `${r.width}px`), r.marginTop && (c.marginTop = `${r.marginTop}px`), r.marginBottom && (c.marginBottom = `${r.marginBottom}px`), r.marginLeft && (c.marginLeft = `${r.marginLeft}px`), r.marginRight && (c.marginRight = `${r.marginRight}px`), r.paddingTop && (c.paddingTop = `${r.paddingTop}px`), r.paddingBottom && (c.paddingBottom = `${r.paddingBottom}px`), r.paddingLeft && (c.paddingLeft = `${r.paddingLeft}px`), r.paddingRight && (c.paddingRight = `${r.paddingRight}px`);
|
|
8
8
|
function l() {
|
|
9
9
|
return i ? i.map((n, r) => /* @__PURE__ */ t(e, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.component.js","names":[],"sources":["../../../src/cards/box/box.component.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport type {BoxProps} from \"./box.types\";\n\nexport const BoxComponent = (props: PiCardProps<BoxProps>): React.ReactNode => {\n const {content, className, style, cardName} = props;\n\n const sy: Record<string, string | number> = {\n ...(style as
|
|
1
|
+
{"version":3,"file":"box.component.js","names":[],"sources":["../../../src/cards/box/box.component.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport type {BoxProps} from \"./box.types\";\n\nexport const BoxComponent = (props: PiCardProps<BoxProps>): React.ReactNode => {\n const {content, className, style, cardName} = props;\n\n const sy: Record<string, string | number> = {\n ...(style as Record<string, string | number> | undefined),\n };\n if (props.height) sy.height = `${props.height}px`;\n if (props.width) sy.width = `${props.width}px`;\n\n if (props.marginTop) sy.marginTop = `${props.marginTop}px`;\n if (props.marginBottom) sy.marginBottom = `${props.marginBottom}px`;\n if (props.marginLeft) sy.marginLeft = `${props.marginLeft}px`;\n if (props.marginRight) sy.marginRight = `${props.marginRight}px`;\n\n if (props.paddingTop) sy.paddingTop = `${props.paddingTop}px`;\n if (props.paddingBottom) sy.paddingBottom = `${props.paddingBottom}px`;\n if (props.paddingLeft) sy.paddingLeft = `${props.paddingLeft}px`;\n if (props.paddingRight) sy.paddingRight = `${props.paddingRight}px`;\n\n function renderContent() {\n if (!content) return null;\n return content.map((cn, i) => (\n <Card cardName={cn} parentCard={cardName} key={i} />\n ));\n }\n\n return (\n <div className={className} style={sy} data-pihanga={cardName}>\n {renderContent()}\n {props.children}\n </div>\n );\n};\n"],"mappings":";;;;AAKA,IAAa,KAAgB,MAAkD;CAC7E,IAAM,EAAC,YAAS,cAAW,UAAO,gBAAY,GAExC,IAAsC,EAC1C,GAAI,EACN;CAYA,AAXI,EAAM,WAAQ,EAAG,SAAS,GAAG,EAAM,OAAO,MAC1C,EAAM,UAAO,EAAG,QAAQ,GAAG,EAAM,MAAM,MAEvC,EAAM,cAAW,EAAG,YAAY,GAAG,EAAM,UAAU,MACnD,EAAM,iBAAc,EAAG,eAAe,GAAG,EAAM,aAAa,MAC5D,EAAM,eAAY,EAAG,aAAa,GAAG,EAAM,WAAW,MACtD,EAAM,gBAAa,EAAG,cAAc,GAAG,EAAM,YAAY,MAEzD,EAAM,eAAY,EAAG,aAAa,GAAG,EAAM,WAAW,MACtD,EAAM,kBAAe,EAAG,gBAAgB,GAAG,EAAM,cAAc,MAC/D,EAAM,gBAAa,EAAG,cAAc,GAAG,EAAM,YAAY,MACzD,EAAM,iBAAc,EAAG,eAAe,GAAG,EAAM,aAAa;CAEhE,SAAS,IAAgB;EAEvB,OADK,IACE,EAAQ,KAAK,GAAI,MACtB,kBAAC,GAAD;GAAM,UAAU;GAAI,YAAY;EAAmB,GAAJ,CAAI,CACpD,IAHoB;CAIvB;CAEA,OACE,kBAAC,OAAD;EAAgB;EAAW,OAAO;EAAI,gBAAc;YAApD,CACG,EAAc,GACd,EAAM,QACJ;;AAET"}
|
package/cards/box/box.types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PiCardRef } from '@pihanga2/core';
|
|
2
|
-
import { AlignItemsT } from '../types';
|
|
3
2
|
export declare const BOX_CARD = "shad/box";
|
|
4
3
|
/**
|
|
5
4
|
* Responsive display grid breakpoints.
|
|
@@ -37,13 +36,6 @@ export type BoxProps<S = object> = {
|
|
|
37
36
|
paddingBottom?: number;
|
|
38
37
|
paddingLeft?: number;
|
|
39
38
|
paddingRight?: number;
|
|
40
|
-
alignItems?: AlignItemsT;
|
|
41
|
-
/** Tailwind gap spacing units */
|
|
42
|
-
gap?: number;
|
|
43
|
-
/** Uniform padding in pixels */
|
|
44
|
-
padding?: number;
|
|
45
|
-
/** Responsive display grid breakpoints */
|
|
46
|
-
display?: DisplayGridT;
|
|
47
39
|
/** Card style object (use the `shad` key for Tailwind-compatible overrides) */
|
|
48
40
|
style?: S;
|
|
49
41
|
/** Additional Tailwind utility classes */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.types.js","names":[],"sources":["../../../src/cards/box/box.types.ts"],"sourcesContent":["import {type PiCardRef, createCardDeclaration} from \"@pihanga2/core\";\
|
|
1
|
+
{"version":3,"file":"box.types.js","names":[],"sources":["../../../src/cards/box/box.types.ts"],"sourcesContent":["import {type PiCardRef, createCardDeclaration} from \"@pihanga2/core\";\n\nexport const BOX_CARD = \"shad/box\";\n\n/**\n * Responsive display grid breakpoints.\n *\n * Each key maps to a CSS display value applied at that viewport width:\n * - `xs`: < 640 px (default / mobile-first)\n * - `sm`: ≥ 640 px\n * - `md`: ≥ 768 px\n * - `lg`: ≥ 1024 px\n * - `xl`: ≥ 1280 px\n * - `xxl`: ≥ 1536 px\n */\nexport type DisplayGridT = {\n xs?: string;\n sm?: string;\n md?: string;\n lg?: string;\n xl?: string;\n xxl?: string;\n};\n\nexport type BoxProps<S = object> = {\n /** Child card refs rendered inside the box */\n content?: PiCardRef[];\n\n /** Override the root element type */\n component?: React.ElementType;\n\n /** Fixed height in pixels */\n height?: number;\n /** Fixed width in pixels */\n width?: number;\n\n marginTop?: number;\n marginBottom?: number;\n marginLeft?: number;\n marginRight?: number;\n\n paddingTop?: number;\n paddingBottom?: number;\n paddingLeft?: number;\n paddingRight?: number;\n\n /** Card style object (use the `shad` key for Tailwind-compatible overrides) */\n style?: S;\n /** Additional Tailwind utility classes */\n className?: string;\n};\n\n/**\n * Factory function for declaring a `shad/box` card instance.\n *\n * ```ts\n * import {registerCard} from \"@pihanga2/core\";\n * import {Box} from \"@/cards/box\";\n *\n * registerCard(\"myApp/spacer\", Box({height: 24}));\n *\n * registerCard(\"myApp/contentRegion\", Box({\n * content: [\"myApp/card1\", \"myApp/card2\"],\n * paddingTop: 16,\n * className: \"rounded-lg border bg-card\",\n * }));\n * ```\n */\nexport const Box = createCardDeclaration<BoxProps>(BOX_CARD);\n"],"mappings":";;AAEA,IAAa,IAAW,YAkEX,IAAM,EAAgC,CAAQ"}
|
|
@@ -7,6 +7,7 @@ export declare const PI_BUTTON_ACTION: {
|
|
|
7
7
|
export declare const onButtonClicked: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
8
8
|
cardID: string;
|
|
9
9
|
} & PiButtonClickedEvent>) => void;
|
|
10
|
+
/** @deprecated Use `onButtonClicked` instead. */
|
|
10
11
|
export declare const onPiButtonClicked: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
11
12
|
cardID: string;
|
|
12
13
|
} & PiButtonClickedEvent>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.types.js","names":[],"sources":["../../../src/cards/button/button.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_BUTTON_CARD = \"pi/button\";\n\nexport const Button = createCardDeclaration<PiButtonProps, PiButtonEvents>(\n PI_BUTTON_CARD,\n);\n\nexport const PI_BUTTON_ACTION = registerActions(PI_BUTTON_CARD, [\"clicked\"]);\n\nexport const onButtonClicked = createOnAction<PiButtonClickedEvent>(\n PI_BUTTON_ACTION.CLICKED,\n);\n\n
|
|
1
|
+
{"version":3,"file":"button.types.js","names":[],"sources":["../../../src/cards/button/button.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_BUTTON_CARD = \"pi/button\";\n\nexport const Button = createCardDeclaration<PiButtonProps, PiButtonEvents>(\n PI_BUTTON_CARD,\n);\n\nexport const PI_BUTTON_ACTION = registerActions(PI_BUTTON_CARD, [\"clicked\"]);\n\nexport const onButtonClicked = createOnAction<PiButtonClickedEvent>(\n PI_BUTTON_ACTION.CLICKED,\n);\n\n/** @deprecated Use `onButtonClicked` instead. */\nexport const onPiButtonClicked = onButtonClicked;\n\n/**\n * Declarative \"opts\" subset based on `src/registry/ui/button.tsx`.\n *\n * Notes:\n * - Any icon is referenced by name and resolved via `src/pihanga/icons.ts`.\n * - `children` are provided via `label` (simple) or `content` (card ref).\n */\nexport type PiButtonOpts = {\n /**\n * Visual style variant of the button.\n * Determines the button's appearance (colors, borders, etc.).\n */\n variant?:\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | \"ghost2\"\n | \"ghost3\"\n | \"ghostActive\"\n | \"brand\"\n | \"nav\"\n | \"navAction\"\n | \"menuAction\"\n | \"blockAction\"\n | \"blockActionSecondary\"\n | \"primaryOutline\"\n | \"radio\"\n | \"none\";\n\n /**\n * Size of the button.\n * Controls padding, font size, and overall dimensions.\n */\n size?:\n | \"default\"\n | \"xs\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"icon\"\n | \"iconSm\"\n | \"navAction\"\n | \"menuAction\"\n | \"blockAction\"\n | \"none\";\n\n /** Truncate button content with ellipsis if it overflows. */\n truncate?: boolean;\n\n /** If true, button is rendered as a menu trigger button. */\n isMenu?: boolean;\n\n /** Custom CSS class to apply to the loading spinner/indicator. */\n loadingClassName?: string;\n\n /** Icon to display before the button content. Set 'size' to 'icon' if ONLY show icon */\n beforeIcon?: string;\n\n /** Icon to display after the button content */\n afterIcon?: string;\n};\n\nexport type PiButtonProps = {\n id?: string; // optional 'id' to be added to events\n\n /**\n * Optional explicit aria-label.\n *\n * Useful for icon-only / letter-only buttons where the visible content\n * shouldn't be used as the accessible name.\n */\n ariaLabel?: string;\n\n /**\n * Declarative options; maps closely to `@/registry/ui/button`.\n */\n opts?: PiButtonOpts;\n\n /** Simple label; used as children and aria-label fallback. */\n label?: string;\n\n /**\n * Icon name to use as the main button content (instead of text label).\n * Resolved via `src/pihanga/icons.ts`.\n * Takes precedence over `label` and `contentCard`.\n */\n iconLabel?: string;\n\n /**\n * If set, renders this card as the content instead of `label`.\n * (Useful for rich content.)\n */\n contentCard?: PiCardRef;\n\n /** When provided, button will render as an anchor. */\n href?: string;\n target?: string;\n\n tooltip?: string;\n\n /**\n * If set, renders this card as the tooltip content instead of `tooltip`.\n * (Useful for rich tooltip content.)\n */\n tooltipCard?: PiCardRef;\n\n /**\n * Controls tooltip visibility mode.\n * - 'auto' (default): Normal hover behavior\n * - 'open': Force tooltip to be visible\n * - 'closed': Force tooltip to be hidden\n */\n tooltipMode?: \"auto\" | \"open\" | \"closed\";\n\n /**\n * Controls tooltip placement/position.\n * - 'top': Tooltip appears above the button (default)\n * - 'right': Tooltip appears to the right of the button\n * - 'bottom': Tooltip appears below the button\n * - 'left': Tooltip appears to the left of the button\n */\n tooltipPlacement?: \"top\" | \"right\" | \"bottom\" | \"left\";\n\n /** Indicates whether the button is in an active/selected state. */\n active?: boolean;\n\n /** Indicates whether the button has focus state styling applied. */\n focused?: boolean;\n\n /** If true, disables the button and prevents interaction. */\n disabled?: boolean;\n\n /** If true, shows loading state and typically disables interaction. */\n loading?: boolean;\n\n /** If true, indicates an async operation is pending (similar to loading but distinct). */\n isPending?: boolean;\n\n className?: string;\n};\n\nexport type PiButtonClickedEvent = {\n id?: string;\n};\n\nexport type PiButtonEvents = {\n onClicked: PiButtonClickedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAiB,aAEjB,IAAS,EACpB,CACF,GAEa,IAAmB,EAAgB,GAAgB,CAAC,SAAS,CAAC,GAE9D,IAAkB,EAC7B,EAAiB,OACnB,GAGa,IAAoB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { PiCardProps } from '@pihanga2/core';
|
|
3
|
-
import { PiChartGraphProps } from './
|
|
3
|
+
import { PiChartGraphProps } from './chart.types';
|
|
4
4
|
export declare const ChartGraphComponent: (props: PiCardProps<PiChartGraphProps>) => React.ReactNode;
|