@pihanga2/shadcn 0.2.3 → 0.2.5
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/README.md +3 -1
- package/cards/chart/chart.types.d.ts +63 -0
- package/cards/graphin/graphin.types.d.ts +106 -0
- package/cards/graphin/legend.component.d.ts +6 -0
- package/cards/jsonViewer/index.js +13 -0
- package/cards/jsonViewer/index.js.map +1 -0
- package/cards/jsonViewer/jsonViewer.component.js +89 -0
- package/cards/jsonViewer/jsonViewer.component.js.map +1 -0
- package/cards/jsonViewer/jsonViewer.types.js +7 -0
- package/cards/jsonViewer/jsonViewer.types.js.map +1 -0
- package/cards/resizable/index.js +12 -0
- package/cards/resizable/index.js.map +1 -0
- package/cards/resizable/resizable.component.js +42 -0
- package/cards/resizable/resizable.component.js.map +1 -0
- package/cards/resizable/resizable.types.js +10 -0
- package/cards/resizable/resizable.types.js.map +1 -0
- package/components/ui/resizable.js +34 -0
- package/components/ui/resizable.js.map +1 -0
- package/package.json +13 -1
- package/pihanga-shadcn.css +1 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
## Included cards (
|
|
65
|
+
## Included cards (35)
|
|
66
66
|
|
|
67
67
|
- `badge`
|
|
68
68
|
- `box`
|
|
@@ -78,6 +78,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
78
78
|
- `form`
|
|
79
79
|
- `framework`
|
|
80
80
|
- `input`
|
|
81
|
+
- `jsonViewer`
|
|
81
82
|
- `list`
|
|
82
83
|
- `loadingOverlay`
|
|
83
84
|
- `loadingSkeleton`
|
|
@@ -86,6 +87,7 @@ import "@pihanga2/shadcn/cards/dataTable";
|
|
|
86
87
|
- `navbarSearch`
|
|
87
88
|
- `pageWithNavbar`
|
|
88
89
|
- `pasteTarget`
|
|
90
|
+
- `resizable`
|
|
89
91
|
- `select`
|
|
90
92
|
- `slider`
|
|
91
93
|
- `sliderValue`
|
|
@@ -47,6 +47,55 @@ export type PiChartSeries = {
|
|
|
47
47
|
*/
|
|
48
48
|
fillOpacity?: number;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* A single recharts `<ReferenceArea>` to overlay on the chart.
|
|
52
|
+
* Highlights a rectangular region between two x or y coordinates.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* // Highlight a y-range (horizontal band)
|
|
57
|
+
* { y1: 100, y2: 200, fill: "var(--chart-3)", fillOpacity: 0.2, label: "Target zone" }
|
|
58
|
+
*
|
|
59
|
+
* // Highlight an x-range (vertical band)
|
|
60
|
+
* { x1: "Feb", x2: "Apr", fill: "var(--chart-2)", fillOpacity: 0.15 }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export type PiChartReferenceArea = {
|
|
64
|
+
/** Start value on the **x-axis**. Omit for a y-only band. */
|
|
65
|
+
x1?: string | number;
|
|
66
|
+
/** End value on the **x-axis**. Omit for a y-only band. */
|
|
67
|
+
x2?: string | number;
|
|
68
|
+
/** Start value on the **y-axis**. Omit for an x-only band. */
|
|
69
|
+
y1?: number;
|
|
70
|
+
/** End value on the **y-axis**. Omit for an x-only band. */
|
|
71
|
+
y2?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Label rendered inside the area. Pass a plain string or a
|
|
74
|
+
* `{ value, position, fill, fontSize }` object for full control.
|
|
75
|
+
*/
|
|
76
|
+
label?: string | {
|
|
77
|
+
value: string;
|
|
78
|
+
position?: string;
|
|
79
|
+
fill?: string;
|
|
80
|
+
fontSize?: number;
|
|
81
|
+
};
|
|
82
|
+
/** CSS fill colour. Defaults to `"var(--chart-1)"`. */
|
|
83
|
+
fill?: string;
|
|
84
|
+
/** Fill opacity, 0–1. Defaults to `0.2`. */
|
|
85
|
+
fillOpacity?: number;
|
|
86
|
+
/** CSS colour for the area border stroke. */
|
|
87
|
+
stroke?: string;
|
|
88
|
+
/** SVG `stroke-dasharray` for the area border. */
|
|
89
|
+
strokeDasharray?: string;
|
|
90
|
+
/** Area border width in pixels. */
|
|
91
|
+
strokeWidth?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Behaviour when the area extends beyond the chart boundary.
|
|
94
|
+
* `"discard"` | `"hidden"` | `"visible"` | `"extendDomain"`.
|
|
95
|
+
* Defaults to `"discard"`.
|
|
96
|
+
*/
|
|
97
|
+
ifOverflow?: "discard" | "hidden" | "visible" | "extendDomain";
|
|
98
|
+
};
|
|
50
99
|
/**
|
|
51
100
|
* A single recharts `<ReferenceLine>` to overlay on the chart.
|
|
52
101
|
* Specify either `x` (vertical line) or `y` (horizontal line).
|
|
@@ -218,6 +267,20 @@ export type PiChartGraphProps = {
|
|
|
218
267
|
* ```
|
|
219
268
|
*/
|
|
220
269
|
referenceLines?: PiChartReferenceLine[];
|
|
270
|
+
/**
|
|
271
|
+
* One or more reference areas to overlay on the chart.
|
|
272
|
+
* Each entry maps directly to a recharts `<ReferenceArea>` and highlights
|
|
273
|
+
* a rectangular region (x-band, y-band, or both).
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* referenceAreas: [
|
|
278
|
+
* { y1: 100, y2: 200, fill: "var(--chart-3)", fillOpacity: 0.15, label: "Target" },
|
|
279
|
+
* { x1: "Feb", x2: "Apr", fill: "var(--chart-4)", fillOpacity: 0.1 },
|
|
280
|
+
* ]
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
referenceAreas?: PiChartReferenceArea[];
|
|
221
284
|
/** Extra Tailwind / CSS classes forwarded to the outermost element. */
|
|
222
285
|
className?: string;
|
|
223
286
|
};
|
|
@@ -208,12 +208,87 @@ export type GraphinProps = {
|
|
|
208
208
|
* @default "style"
|
|
209
209
|
*/
|
|
210
210
|
nodeStyleKey?: string;
|
|
211
|
+
/**
|
|
212
|
+
* Legend overlay rendered as an absolutely-positioned panel over the graph.
|
|
213
|
+
*
|
|
214
|
+
* Two content modes:
|
|
215
|
+
* - **`items`** — built-in swatch list (colour + shape + label).
|
|
216
|
+
* - **`cardName`** — any registered Pihanga card used as a fully custom legend.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```ts
|
|
220
|
+
* legend: {
|
|
221
|
+
* position: "bottom-left",
|
|
222
|
+
* items: [
|
|
223
|
+
* { label: "Gateway", fill: "#e67e22", shape: "star" },
|
|
224
|
+
* { label: "Service", fill: "#2980b9", shape: "circle" },
|
|
225
|
+
* { label: "Database", fill: "#8e44ad", shape: "rect" },
|
|
226
|
+
* ],
|
|
227
|
+
* }
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
legend?: GraphinLegend;
|
|
211
231
|
style?: {
|
|
212
232
|
root?: React.CSSProperties;
|
|
213
233
|
item?: React.CSSProperties;
|
|
214
234
|
};
|
|
215
235
|
className?: string;
|
|
216
236
|
};
|
|
237
|
+
/**
|
|
238
|
+
* A single item in the built-in legend.
|
|
239
|
+
*/
|
|
240
|
+
export type GraphinLegendItem = {
|
|
241
|
+
/** Text label displayed beside the shape swatch. */
|
|
242
|
+
label: string;
|
|
243
|
+
/** Fill colour of the swatch. Defaults to `"#4793AF"`. */
|
|
244
|
+
fill?: string;
|
|
245
|
+
/** Stroke / border colour of the swatch. */
|
|
246
|
+
stroke?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Shape of the swatch marker. Mirrors the values accepted by `nodeStyles`.
|
|
249
|
+
* Use `"line"` for edge/connector legend entries.
|
|
250
|
+
* @default "circle"
|
|
251
|
+
*/
|
|
252
|
+
shape?: "circle" | "rect" | "diamond" | "star" | "triangle" | "hexagon" | "ellipse" | "line";
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Legend overlay configuration.
|
|
256
|
+
*
|
|
257
|
+
* Two mutually-exclusive content modes:
|
|
258
|
+
* - **`items`** — serialisable list of `{ label, fill, stroke, shape }` entries
|
|
259
|
+
* rendered as a built-in swatch list.
|
|
260
|
+
* - **`cardName`** — name of any registered Pihanga card whose component is
|
|
261
|
+
* rendered as the legend body. Takes priority over `items` when both are set.
|
|
262
|
+
* Use this for fully custom legends (images, interactive filters, etc.).
|
|
263
|
+
*/
|
|
264
|
+
export type GraphinLegend = {
|
|
265
|
+
/**
|
|
266
|
+
* Built-in legend items — each rendered as a small SVG shape swatch + label.
|
|
267
|
+
* Ignored when `cardName` is also provided.
|
|
268
|
+
*/
|
|
269
|
+
items?: GraphinLegendItem[];
|
|
270
|
+
/**
|
|
271
|
+
* Registered pihanga card name to use as the legend body.
|
|
272
|
+
* The card is rendered inside the legend wrapper div; it receives no special
|
|
273
|
+
* context props — source its data from Redux state as usual.
|
|
274
|
+
*/
|
|
275
|
+
cardName?: string;
|
|
276
|
+
/**
|
|
277
|
+
* Corner of the graph container where the legend is anchored.
|
|
278
|
+
* @default "bottom-left"
|
|
279
|
+
*/
|
|
280
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
281
|
+
/**
|
|
282
|
+
* Extra CSS class name(s) applied to the legend wrapper `<div>`.
|
|
283
|
+
* @example "rounded-xl shadow-lg"
|
|
284
|
+
*/
|
|
285
|
+
className?: string;
|
|
286
|
+
/**
|
|
287
|
+
* Inline style overrides for the legend wrapper `<div>`.
|
|
288
|
+
* Merged on top of the built-in positioning styles.
|
|
289
|
+
*/
|
|
290
|
+
style?: React.CSSProperties;
|
|
291
|
+
};
|
|
217
292
|
export type GraphinTooltip = {
|
|
218
293
|
/** Registered card name whose component is rendered inside the floating panel. */
|
|
219
294
|
node?: string;
|
|
@@ -331,6 +406,7 @@ export declare const GRAPHIN_OP_ACTION: {
|
|
|
331
406
|
CLEAR_NODE_STYLES: string;
|
|
332
407
|
ZOOM_TO_NODE: string;
|
|
333
408
|
UPDATE_NODE_STYLE_MAP: string;
|
|
409
|
+
FIT_VIEW: string;
|
|
334
410
|
};
|
|
335
411
|
/**
|
|
336
412
|
* Payload for `GRAPHIN_OP_ACTION.SET_NODE_STYLES`.
|
|
@@ -470,6 +546,36 @@ export type GraphinUpdateNodeStyleMapAction = {
|
|
|
470
546
|
* ```
|
|
471
547
|
*/
|
|
472
548
|
export declare const dispatchGraphinUpdateNodeStyleMap: (dispatch: DispatchF, payload: GraphinUpdateNodeStyleMapAction) => string;
|
|
549
|
+
/**
|
|
550
|
+
* Payload for `GRAPHIN_OP_ACTION.FIT_VIEW`.
|
|
551
|
+
*
|
|
552
|
+
* Fits (and optionally re-centres) the entire graph within the visible
|
|
553
|
+
* container — the same effect as the initial `autoFit: 'view'`.
|
|
554
|
+
*/
|
|
555
|
+
export type GraphinFitViewAction = {
|
|
556
|
+
cardName: string;
|
|
557
|
+
/**
|
|
558
|
+
* - `"view"` — zoom + pan so all nodes fit inside the viewport (default).
|
|
559
|
+
* - `"center"` — pan to centre the graph without changing the zoom level.
|
|
560
|
+
*/
|
|
561
|
+
mode?: "view" | "center";
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* Fit the entire graph into the available canvas area.
|
|
565
|
+
*
|
|
566
|
+
* - `mode: "view"` (default) — zoom + pan so every node is visible.
|
|
567
|
+
* - `mode: "center"` — pan to the graph centroid without changing zoom.
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```ts
|
|
571
|
+
* // Zoom-to-fit after the user has panned/zoomed away
|
|
572
|
+
* dispatchGraphinFitView(dispatch, { cardName: "myGraph" });
|
|
573
|
+
*
|
|
574
|
+
* // Re-centre without changing zoom
|
|
575
|
+
* dispatchGraphinFitView(dispatch, { cardName: "myGraph", mode: "center" });
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
export declare const dispatchGraphinFitView: (dispatch: DispatchF, payload: GraphinFitViewAction) => string;
|
|
473
579
|
export type { ReduxAction };
|
|
474
580
|
export type GraphinEvents = {
|
|
475
581
|
/** Fired when the pointer enters a node */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JSON_VIEWER_ACTION as e, JSON_VIEWER_CARD as t, JsonViewer as n, onJsonViewerClicked as r, onJsonViewerCopied as i } from "./jsonViewer.types.js";
|
|
2
|
+
import { JsonViewerComponent as a } from "./jsonViewer.component.js";
|
|
3
|
+
import { actionTypesToEvents as o, registerCardComponent as s } from "@pihanga2/core";
|
|
4
|
+
//#region src/cards/jsonViewer/index.ts
|
|
5
|
+
s({
|
|
6
|
+
name: t,
|
|
7
|
+
component: a,
|
|
8
|
+
events: o(e)
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { e as JSON_VIEWER_ACTION, t as JSON_VIEWER_CARD, n as JsonViewer, r as onJsonViewerClicked, i as onJsonViewerCopied };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/jsonViewer/index.ts"],"sourcesContent":["import {registerCardComponent, actionTypesToEvents} from \"@pihanga2/core\";\n\nimport {JSON_VIEWER_CARD, JSON_VIEWER_ACTION} from \"./jsonViewer.types\";\nimport {JsonViewerComponent} from \"./jsonViewer.component\";\n\nexport * from \"./jsonViewer.types\";\n\nregisterCardComponent({\n name: JSON_VIEWER_CARD,\n component: JsonViewerComponent,\n events: actionTypesToEvents(JSON_VIEWER_ACTION),\n});\n"],"mappings":";;;;AAOA,EAAsB;CACpB,MAAM;CACN,WAAW;CACX,QAAQ,EAAoB,CAAkB;AAChD,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getIconElement as e } from "../icons.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
/* empty css */
|
|
4
|
+
import { useCallback as t, useLayoutEffect as n, useRef as r, useState as i } from "react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
import { Check as s, Copy as c } from "lucide-react";
|
|
7
|
+
import { JsonView as l, allExpanded as u, defaultStyles as d } from "react-json-view-lite";
|
|
8
|
+
//#region src/cards/jsonViewer/jsonViewer.component.tsx
|
|
9
|
+
function f(e) {
|
|
10
|
+
if (e === !1) return u;
|
|
11
|
+
if (e === !0) return () => !1;
|
|
12
|
+
let t = e ?? 1;
|
|
13
|
+
return (e) => e < t;
|
|
14
|
+
}
|
|
15
|
+
var p = {
|
|
16
|
+
...d,
|
|
17
|
+
container: "jv-container",
|
|
18
|
+
basicChildStyle: "jv-basic-child",
|
|
19
|
+
label: "jv-label",
|
|
20
|
+
clickableLabel: "jv-clickable-label",
|
|
21
|
+
nullValue: "jv-null",
|
|
22
|
+
undefinedValue: "jv-undefined",
|
|
23
|
+
numberValue: "jv-number",
|
|
24
|
+
stringValue: "jv-string",
|
|
25
|
+
booleanValue: "jv-boolean",
|
|
26
|
+
otherValue: "jv-other",
|
|
27
|
+
punctuation: "jv-punctuation",
|
|
28
|
+
expandIcon: "jv-expand-icon",
|
|
29
|
+
collapseIcon: "jv-collapse-icon",
|
|
30
|
+
collapsedContent: "jv-collapsed-content",
|
|
31
|
+
childFieldsContainer: "jv-child-fields-container"
|
|
32
|
+
};
|
|
33
|
+
function m(t) {
|
|
34
|
+
if (t) {
|
|
35
|
+
let n = e(t);
|
|
36
|
+
if (n) return n;
|
|
37
|
+
}
|
|
38
|
+
return c;
|
|
39
|
+
}
|
|
40
|
+
async function h(e) {
|
|
41
|
+
if (typeof ClipboardItem < "u") {
|
|
42
|
+
let t = new Blob([e], { type: "text/plain" });
|
|
43
|
+
await navigator.clipboard.write([new ClipboardItem({ "text/plain": t })]);
|
|
44
|
+
} else await navigator.clipboard.writeText(e);
|
|
45
|
+
}
|
|
46
|
+
var g = (e) => {
|
|
47
|
+
let { source: c, collapsed: u = 1, modifyFn: d, style: g, className: _, cardName: v, _cls: y, copyToClipboard: b, copyIcon: x, onCopied: S } = e, C = r(null), [w, T] = i(!1);
|
|
48
|
+
n(() => {
|
|
49
|
+
d && d(c, C.current);
|
|
50
|
+
}, [d, c]);
|
|
51
|
+
let E = f(u), D = t(async () => {
|
|
52
|
+
let e = JSON.stringify(c, null, 2), t = !1;
|
|
53
|
+
try {
|
|
54
|
+
await h(e), t = !0, T(!0), setTimeout(() => T(!1), 2e3);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.warn("[JsonViewer] clipboard write failed:", e);
|
|
57
|
+
}
|
|
58
|
+
S?.({ success: t });
|
|
59
|
+
}, [c, S]), O = m(x), k = s;
|
|
60
|
+
return /* @__PURE__ */ o("div", {
|
|
61
|
+
className: [y("root", _), "jv-wrapper"].join(" "),
|
|
62
|
+
ref: C,
|
|
63
|
+
"data-pihanga": v,
|
|
64
|
+
style: g,
|
|
65
|
+
children: [b && /* @__PURE__ */ a("button", {
|
|
66
|
+
type: "button",
|
|
67
|
+
"aria-label": w ? "Copied!" : "Copy JSON to clipboard",
|
|
68
|
+
title: w ? "Copied!" : "Copy JSON to clipboard",
|
|
69
|
+
className: `jv-copy-btn${w ? " jv-copy-btn--done" : ""}`,
|
|
70
|
+
onClick: D,
|
|
71
|
+
children: w ? /* @__PURE__ */ a(k, {
|
|
72
|
+
size: 13,
|
|
73
|
+
strokeWidth: 2.5
|
|
74
|
+
}) : /* @__PURE__ */ a(O, {
|
|
75
|
+
size: 13,
|
|
76
|
+
strokeWidth: 2
|
|
77
|
+
})
|
|
78
|
+
}), /* @__PURE__ */ a(l, {
|
|
79
|
+
data: c,
|
|
80
|
+
shouldExpandNode: E,
|
|
81
|
+
style: p,
|
|
82
|
+
clickToExpandNode: !0
|
|
83
|
+
})]
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
export { g as JsonViewerComponent };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=jsonViewer.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonViewer.component.js","names":[],"sources":["../../../src/cards/jsonViewer/jsonViewer.component.tsx"],"sourcesContent":["import React, {useLayoutEffect, useRef, useState, useCallback} from \"react\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport {JsonView, allExpanded, defaultStyles} from \"react-json-view-lite\";\nimport {Copy, Check} from \"lucide-react\";\nimport \"react-json-view-lite/dist/index.css\";\nimport \"./jsonViewer.css\";\n\nimport {getIconElement} from \"@/cards/icons\";\nimport type {JsonViewerProps, JsonViewerEvents} from \"./jsonViewer.types\";\n\n/**\n * Converts the `collapsed` prop (from the react-json-view API) into a\n * `shouldExpandNode` predicate for react-json-view-lite.\n *\n * - `false` → expand everything\n * - `true` → collapse everything\n * - number N → expand nodes at depth < N (root is depth 0)\n */\nfunction toShouldExpandNode(\n collapsed: boolean | number | undefined,\n): (level: number) => boolean {\n if (collapsed === false) return allExpanded;\n if (collapsed === true) return () => false;\n // default: collapsed = 1 → only root level expanded\n const depth = collapsed ?? 1;\n return (level: number) => level < depth;\n}\n\n/**\n * Compact, code-editor style for react-json-view-lite.\n *\n * The expand/collapse icon is positioned absolutely inside a fixed left gutter\n * (via padding-left on `.jv-basic-child`), so the opening `{` and closing `}`\n * always start at the same column — fixing the original misalignment where the\n * icon pushed the open-brace further right than the close-brace.\n */\nconst compactStyles = {\n // spread defaultStyles first to satisfy required fields (ariaLables, stringifyStringValues, …)\n ...defaultStyles,\n container: \"jv-container\",\n basicChildStyle: \"jv-basic-child\",\n label: \"jv-label\",\n clickableLabel: \"jv-clickable-label\",\n nullValue: \"jv-null\",\n undefinedValue: \"jv-undefined\",\n numberValue: \"jv-number\",\n stringValue: \"jv-string\",\n booleanValue: \"jv-boolean\",\n otherValue: \"jv-other\",\n punctuation: \"jv-punctuation\",\n expandIcon: \"jv-expand-icon\",\n collapseIcon: \"jv-collapse-icon\",\n collapsedContent: \"jv-collapsed-content\",\n childFieldsContainer: \"jv-child-fields-container\",\n};\n\n/**\n * Resolve the icon element to use for the copy button.\n *\n * Resolution order:\n * 1. `iconName` looked up in the global icon registry\n * 2. Lucide `Copy` as the built-in fallback\n */\nfunction resolveCopyIcon(iconName?: string): React.ElementType {\n if (iconName) {\n const registered = getIconElement(iconName);\n if (registered) return registered;\n }\n return Copy;\n}\n\n/** Write `text` to the clipboard, preferring `ClipboardItem` for explicit MIME. */\nasync function writeToClipboard(text: string): Promise<void> {\n if (typeof ClipboardItem !== \"undefined\") {\n const blob = new Blob([text], {type: \"text/plain\"});\n await navigator.clipboard.write([new ClipboardItem({\"text/plain\": blob})]);\n } else {\n // Fallback for browsers without ClipboardItem support\n await navigator.clipboard.writeText(text);\n }\n}\n\nexport const JsonViewerComponent = (\n props: PiCardProps<JsonViewerProps, JsonViewerEvents>,\n): React.ReactNode => {\n const {\n source,\n collapsed = 1,\n modifyFn,\n style,\n className,\n cardName,\n _cls,\n copyToClipboard,\n copyIcon,\n onCopied,\n } = props;\n\n const elRef = useRef<HTMLDivElement>(null);\n const [copied, setCopied] = useState(false);\n\n useLayoutEffect(() => {\n if (modifyFn) {\n modifyFn(source, elRef.current);\n }\n }, [modifyFn, source]);\n\n const shouldExpandNode = toShouldExpandNode(collapsed);\n\n const handleCopy = useCallback(async () => {\n const text = JSON.stringify(source, null, 2);\n let success = false;\n try {\n await writeToClipboard(text);\n success = true;\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.warn(\"[JsonViewer] clipboard write failed:\", err);\n }\n onCopied?.({success});\n }, [source, onCopied]);\n\n const CopyIconEl = resolveCopyIcon(copyIcon);\n const ConfirmIconEl = Check;\n\n return (\n <div\n className={[_cls(\"root\", className), \"jv-wrapper\"].join(\" \")}\n ref={elRef}\n data-pihanga={cardName}\n style={style as React.CSSProperties | undefined}\n >\n {copyToClipboard && (\n <button\n type=\"button\"\n aria-label={copied ? \"Copied!\" : \"Copy JSON to clipboard\"}\n title={copied ? \"Copied!\" : \"Copy JSON to clipboard\"}\n className={`jv-copy-btn${copied ? \" jv-copy-btn--done\" : \"\"}`}\n onClick={handleCopy}\n >\n {copied ? (\n <ConfirmIconEl size={13} strokeWidth={2.5} />\n ) : (\n <CopyIconEl size={13} strokeWidth={2} />\n )}\n </button>\n )}\n <JsonView\n data={source as object | unknown[]}\n shouldExpandNode={shouldExpandNode}\n style={compactStyles}\n clickToExpandNode\n />\n </div>\n );\n};\n"],"mappings":";;;;;;;;AAkBA,SAAS,EACP,GAC4B;CAC5B,IAAI,MAAc,IAAO,OAAO;CAChC,IAAI,MAAc,IAAM,aAAa;CAErC,IAAM,IAAQ,KAAa;CAC3B,QAAQ,MAAkB,IAAQ;AACpC;AAUA,IAAM,IAAgB;CAEpB,GAAG;CACH,WAAW;CACX,iBAAiB;CACjB,OAAO;CACP,gBAAgB;CAChB,WAAW;CACX,gBAAgB;CAChB,aAAa;CACb,aAAa;CACb,cAAc;CACd,YAAY;CACZ,aAAa;CACb,YAAY;CACZ,cAAc;CACd,kBAAkB;CAClB,sBAAsB;AACxB;AASA,SAAS,EAAgB,GAAsC;CAC7D,IAAI,GAAU;EACZ,IAAM,IAAa,EAAe,CAAQ;EAC1C,IAAI,GAAY,OAAO;CACzB;CACA,OAAO;AACT;AAGA,eAAe,EAAiB,GAA6B;CAC3D,IAAI,OAAO,gBAAkB,KAAa;EACxC,IAAM,IAAO,IAAI,KAAK,CAAC,CAAI,GAAG,EAAC,MAAM,aAAY,CAAC;EAClD,MAAM,UAAU,UAAU,MAAM,CAAC,IAAI,cAAc,EAAC,cAAc,EAAI,CAAC,CAAC,CAAC;CAC3E,OAEE,MAAM,UAAU,UAAU,UAAU,CAAI;AAE5C;AAEA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,WACA,eAAY,GACZ,aACA,UACA,cACA,aACA,SACA,oBACA,aACA,gBACE,GAEE,IAAQ,EAAuB,IAAI,GACnC,CAAC,GAAQ,KAAa,EAAS,EAAK;CAE1C,QAAsB;EACpB,AAAI,KACF,EAAS,GAAQ,EAAM,OAAO;CAElC,GAAG,CAAC,GAAU,CAAM,CAAC;CAErB,IAAM,IAAmB,EAAmB,CAAS,GAE/C,IAAa,EAAY,YAAY;EACzC,IAAM,IAAO,KAAK,UAAU,GAAQ,MAAM,CAAC,GACvC,IAAU;EACd,IAAI;GAIF,AAHA,MAAM,EAAiB,CAAI,GAC3B,IAAU,IACV,EAAU,EAAI,GACd,iBAAiB,EAAU,EAAK,GAAG,GAAI;EACzC,SAAS,GAAK;GACZ,QAAQ,KAAK,wCAAwC,CAAG;EAC1D;EACA,IAAW,EAAC,WAAO,CAAC;CACtB,GAAG,CAAC,GAAQ,CAAQ,CAAC,GAEf,IAAa,EAAgB,CAAQ,GACrC,IAAgB;CAEtB,OACE,kBAAC,OAAD;EACE,WAAW,CAAC,EAAK,QAAQ,CAAS,GAAG,YAAY,EAAE,KAAK,GAAG;EAC3D,KAAK;EACL,gBAAc;EACP;YAJT,CAMG,KACC,kBAAC,UAAD;GACE,MAAK;GACL,cAAY,IAAS,YAAY;GACjC,OAAO,IAAS,YAAY;GAC5B,WAAW,cAAc,IAAS,uBAAuB;GACzD,SAAS;aAER,IACC,kBAAC,GAAD;IAAe,MAAM;IAAI,aAAa;GAAM,CAAA,IAE5C,kBAAC,GAAD;IAAY,MAAM;IAAI,aAAa;GAAI,CAAA;EAEnC,CAAA,GAEV,kBAAC,GAAD;GACE,MAAM;GACY;GAClB,OAAO;GACP,mBAAA;EACD,CAAA,CACE;;AAET"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
|
|
2
|
+
//#region src/cards/jsonViewer/jsonViewer.types.ts
|
|
3
|
+
var r = "json-viewer", i = e(r), a = n(r, ["clicked", "copied"]), o = t(a.CLICKED), s = t(a.COPIED);
|
|
4
|
+
//#endregion
|
|
5
|
+
export { a as JSON_VIEWER_ACTION, r as JSON_VIEWER_CARD, i as JsonViewer, o as onJsonViewerClicked, s as onJsonViewerCopied };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=jsonViewer.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonViewer.types.js","names":[],"sources":["../../../src/cards/jsonViewer/jsonViewer.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const JSON_VIEWER_CARD = \"json-viewer\";\n\nexport const JsonViewer = createCardDeclaration<\n JsonViewerProps,\n JsonViewerEvents\n>(JSON_VIEWER_CARD);\n\nexport const JSON_VIEWER_ACTION = registerActions(JSON_VIEWER_CARD, [\n \"clicked\",\n \"copied\",\n]);\n\nexport const onJsonViewerClicked = createOnAction<ClickedEvent>(\n JSON_VIEWER_ACTION.CLICKED,\n);\n\nexport const onJsonViewerCopied = createOnAction<CopiedEvent>(\n JSON_VIEWER_ACTION.COPIED,\n);\n\nexport type JsonViewerProps<S = object> = {\n /** The JSON value to display. */\n source: unknown;\n /**\n * Theme name. Maps loosely to a visual style.\n * Use \"monokai\", \"solarized\", \"ocean\", etc. (visual only — actual theming\n * is done via className / global CSS since react-json-view-lite uses CSS\n * variables rather than named themes).\n */\n theme?: string;\n iconStyle?: \"circle\" | \"triangle\" | \"square\";\n /**\n * When `true`, all nodes are collapsed.\n * When `false`, all nodes are expanded.\n * When a number N, nodes at depth >= N are collapsed (root is depth 0).\n * Default: 1 (expand root only).\n */\n collapsed?: boolean | number;\n /** When `true`, a click-to-expand interaction is enabled on node labels. */\n enableClipboard?: boolean;\n /** When `true`, removes quotes from object keys in display. */\n removeQuotesOnKeys?: boolean;\n /** When `true`, objects and arrays are labelled with their size. */\n displayObjectSize?: boolean;\n /** When `true`, data-type labels prefix values. */\n displayDataTypes?: boolean;\n /**\n * Called after the JSON is rendered, allowing post-render DOM modifications.\n * Receives the raw `source` value and the container element.\n */\n modifyFn?: ModifyFn;\n\n /**\n * When `true`, shows a copy-to-clipboard icon button in the top-right\n * corner of the viewer. Clicking it copies the pretty-printed JSON to the\n * clipboard using `ClipboardItem` with `text/plain` MIME type.\n */\n copyToClipboard?: boolean;\n\n /**\n * Icon name resolved via the global icon registry (`getIconElement`).\n * If omitted or the name is not registered, falls back to the Lucide\n * `Copy` icon automatically.\n *\n * @example\n * // Register your own icon first:\n * registerIcon(\"my-copy\", MyCustomCopyIcon);\n * // Then pass the name to the card:\n * JsonViewer({ copyToClipboard: true, copyIcon: \"my-copy\", ... })\n */\n copyIcon?: string;\n\n /** Inline style for the react-json-view-lite container. */\n style?: S;\n /** Additional CSS class for the outer wrapper. */\n className?: string;\n};\n\nexport type ModifyFn = (source: unknown, el: HTMLElement | null) => void;\n\nexport type ClickedEvent = Record<string, never>;\n\nexport type CopiedEvent = {\n /** `true` when the clipboard write succeeded, `false` on error. */\n success: boolean;\n};\n\nexport type JsonViewerEvents = {\n onClicked: ClickedEvent;\n onCopied: CopiedEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAmB,eAEnB,IAAa,EAGxB,CAAgB,GAEL,IAAqB,EAAgB,GAAkB,CAClE,WACA,QACF,CAAC,GAEY,IAAsB,EACjC,EAAmB,OACrB,GAEa,IAAqB,EAChC,EAAmB,MACrB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DefResizableHandle as e, RESIZABLE_CARD as t, Resizable as n, SdResizable as r } from "./resizable.types.js";
|
|
2
|
+
import { ResizableComponent as i } from "./resizable.component.js";
|
|
3
|
+
import { registerCardComponent as a } from "@pihanga2/core";
|
|
4
|
+
//#region src/cards/resizable/index.ts
|
|
5
|
+
a({
|
|
6
|
+
name: t,
|
|
7
|
+
component: i
|
|
8
|
+
});
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as DefResizableHandle, t as RESIZABLE_CARD, n as Resizable, r as SdResizable };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/resizable/index.ts"],"sourcesContent":["import {registerCardComponent} from \"@pihanga2/core\";\n\nimport {ResizableComponent} from \"./resizable.component\";\nimport {RESIZABLE_CARD} from \"./resizable.types\";\n\nexport * from \"./resizable.types\";\n\nregisterCardComponent({\n name: RESIZABLE_CARD,\n component: ResizableComponent,\n});\n"],"mappings":";;;;AAOA,EAAsB;CACpB,MAAM;CACN,WAAW;AACb,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ResizableHandle as e, ResizablePanel as t, ResizablePanelGroup as n } from "../../components/ui/resizable.js";
|
|
2
|
+
import { DefResizableHandle as r } from "./resizable.types.js";
|
|
3
|
+
import { Fragment as i } from "react";
|
|
4
|
+
import { Card as a } from "@pihanga2/core";
|
|
5
|
+
import { clsx as o } from "clsx";
|
|
6
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
7
|
+
//#region src/cards/resizable/resizable.component.tsx
|
|
8
|
+
var l = (l) => {
|
|
9
|
+
let { content: u, handles: d, direction: f, className: p, style: m, cardName: h } = l, g = m ?? {}, _ = Array.isArray(d) ? d : Array(u.length - 1).fill(d ?? r);
|
|
10
|
+
function v(e, n) {
|
|
11
|
+
let r = e.name ?? `panel${n}`, l = {
|
|
12
|
+
defaultSize: e.defaultSize ?? 50,
|
|
13
|
+
collapsible: e.collapsible
|
|
14
|
+
};
|
|
15
|
+
return e.maxSize !== void 0 && (l.maxSize = e.maxSize), e.minSize !== void 0 && (l.minSize = e.minSize), /* @__PURE__ */ c(i, { children: [y(n), /* @__PURE__ */ s(t, {
|
|
16
|
+
...l,
|
|
17
|
+
className: o(g.panel, p && `${p}-${r}`),
|
|
18
|
+
children: /* @__PURE__ */ s(a, {
|
|
19
|
+
cardName: e.content,
|
|
20
|
+
parentCard: h
|
|
21
|
+
})
|
|
22
|
+
})] }, n);
|
|
23
|
+
}
|
|
24
|
+
function y(t) {
|
|
25
|
+
if (t === 0) return null;
|
|
26
|
+
let n = _[t - 1];
|
|
27
|
+
return /* @__PURE__ */ s(e, {
|
|
28
|
+
...n,
|
|
29
|
+
className: o(g.handle)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return /* @__PURE__ */ s(n, {
|
|
33
|
+
direction: f ?? "horizontal",
|
|
34
|
+
className: o(p, g.root),
|
|
35
|
+
"data-pihanga": h,
|
|
36
|
+
children: u.map(v)
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { l as ResizableComponent };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=resizable.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable.component.js","names":[],"sources":["../../../src/cards/resizable/resizable.component.tsx"],"sourcesContent":["import React, {Fragment} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {clsx} from \"clsx\";\nimport {\n ResizableHandle,\n ResizablePanel,\n ResizablePanelGroup,\n} from \"@/components/ui/resizable\";\nimport {\n DefResizableHandle,\n type PiResizableHandle,\n type PiResizablePanel,\n type ResizableProps,\n} from \"./resizable.types\";\n\nexport const ResizableComponent = (\n props: PiCardProps<ResizableProps>,\n): React.ReactNode => {\n const {content, handles, direction, className, style, cardName} = props;\n\n const sy =\n (style as {root?: string; panel?: string; handle?: string} | undefined) ??\n {};\n\n const _handles: PiResizableHandle[] = Array.isArray(handles)\n ? handles\n : Array(content.length - 1).fill(handles ?? DefResizableHandle);\n\n function renderPanel(el: PiResizablePanel, idx: number) {\n const name = el.name ?? `panel${idx}`;\n const panelProps: {\n defaultSize: number;\n collapsible?: boolean;\n maxSize?: number;\n minSize?: number;\n } = {\n defaultSize: el.defaultSize ?? 50,\n collapsible: el.collapsible,\n };\n if (el.maxSize !== undefined) panelProps.maxSize = el.maxSize;\n if (el.minSize !== undefined) panelProps.minSize = el.minSize;\n\n return (\n <Fragment key={idx}>\n {renderHandle(idx)}\n <ResizablePanel\n {...panelProps}\n className={clsx(sy.panel, className && `${className}-${name}`)}\n >\n <Card cardName={el.content} parentCard={cardName} />\n </ResizablePanel>\n </Fragment>\n );\n }\n\n function renderHandle(idx: number) {\n if (idx === 0) return null;\n const h = _handles[idx - 1];\n return <ResizableHandle {...h} className={clsx(sy.handle)} />;\n }\n\n return (\n <ResizablePanelGroup\n direction={direction ?? \"horizontal\"}\n className={clsx(className, sy.root)}\n data-pihanga={cardName}\n >\n {content.map(renderPanel)}\n </ResizablePanelGroup>\n );\n};\n"],"mappings":";;;;;;;AAeA,IAAa,KACX,MACoB;CACpB,IAAM,EAAC,YAAS,YAAS,cAAW,cAAW,UAAO,gBAAY,GAE5D,IACH,KACD,CAAC,GAEG,IAAgC,MAAM,QAAQ,CAAO,IACvD,IACA,MAAM,EAAQ,SAAS,CAAC,EAAE,KAAK,KAAW,CAAkB;CAEhE,SAAS,EAAY,GAAsB,GAAa;EACtD,IAAM,IAAO,EAAG,QAAQ,QAAQ,KAC1B,IAKF;GACF,aAAa,EAAG,eAAe;GAC/B,aAAa,EAAG;EAClB;EAIA,OAHI,EAAG,YAAY,KAAA,MAAW,EAAW,UAAU,EAAG,UAClD,EAAG,YAAY,KAAA,MAAW,EAAW,UAAU,EAAG,UAGpD,kBAAC,GAAD,EAAA,UAAA,CACG,EAAa,CAAG,GACjB,kBAAC,GAAD;GACE,GAAI;GACJ,WAAW,EAAK,EAAG,OAAO,KAAa,GAAG,EAAU,GAAG,GAAM;aAE7D,kBAAC,GAAD;IAAM,UAAU,EAAG;IAAS,YAAY;GAAW,CAAA;EACrC,CAAA,CACR,EAAA,GARK,CAQL;CAEd;CAEA,SAAS,EAAa,GAAa;EACjC,IAAI,MAAQ,GAAG,OAAO;EACtB,IAAM,IAAI,EAAS,IAAM;EACzB,OAAO,kBAAC,GAAD;GAAiB,GAAI;GAAG,WAAW,EAAK,EAAG,MAAM;EAAI,CAAA;CAC9D;CAEA,OACE,kBAAC,GAAD;EACE,WAAW,KAAa;EACxB,WAAW,EAAK,GAAW,EAAG,IAAI;EAClC,gBAAc;YAEb,EAAQ,IAAI,CAAW;CACL,CAAA;AAEzB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createCardDeclaration as e } from "@pihanga2/core";
|
|
2
|
+
//#region src/cards/resizable/resizable.types.ts
|
|
3
|
+
var t = "shad/resizable", n = e(t), r = n, i = {
|
|
4
|
+
withHandle: !0,
|
|
5
|
+
disabled: !1
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { i as DefResizableHandle, t as RESIZABLE_CARD, r as Resizable, n as SdResizable };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=resizable.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable.types.js","names":[],"sources":["../../../src/cards/resizable/resizable.types.ts"],"sourcesContent":["import {type PiCardRef, createCardDeclaration} from \"@pihanga2/core\";\n\nexport const RESIZABLE_CARD = \"shad/resizable\";\n\nexport const SdResizable =\n createCardDeclaration<ResizableProps>(RESIZABLE_CARD);\n\n/** Convenience alias — same as {@link SdResizable}. */\nexport const Resizable = SdResizable;\n\nexport type ResizableProps<S = unknown> = {\n /** Ordered list of panels to render inside the resizable group. */\n content: PiResizablePanel[];\n /** Layout direction of the panel group. Defaults to `\"horizontal\"`. */\n direction?: \"horizontal\" | \"vertical\";\n /**\n * Handle configuration(s) between panels.\n *\n * - A single `PiResizableHandle` is used for every handle in the group.\n * - An array must have exactly `content.length - 1` entries.\n *\n * Defaults to {@link DefResizableHandle} (visible drag handle).\n */\n handles?: PiResizableHandle | PiResizableHandle[];\n className?: string;\n /**\n * Optional Shadcn-style style overrides.\n *\n * When provided as `{ shad?: { root?: string; panel?: string; handle?: string } }`,\n * the nested class names are applied to the corresponding elements in\n * addition to any `className` already set.\n */\n style?: S;\n};\n\nexport type PiResizablePanel = {\n /** Used for CSS class suffixes and stable ordering. Defaults to `\"panel{idx}\"`. */\n name?: string;\n /** The card to render inside this panel. */\n content: PiCardRef;\n /** Initial size as a percentage of the group. Defaults to `50`. */\n defaultSize?: number;\n minSize?: number;\n maxSize?: number;\n /** When `true`, the panel can be collapsed to zero size. */\n collapsible?: boolean;\n};\n\nexport type PiResizableHandle = {\n /** When `true`, renders a visible drag-handle grip indicator. */\n withHandle?: boolean;\n /** When `true`, the handle is decorative only (not draggable). */\n disabled?: boolean;\n};\n\n/** Default handle configuration: visible grip, draggable. */\nexport const DefResizableHandle: PiResizableHandle = {\n withHandle: true,\n disabled: false,\n};\n"],"mappings":";;AAEA,IAAa,IAAiB,kBAEjB,IACX,EAAsC,CAAc,GAGzC,IAAY,GAgDZ,IAAwC;CACnD,YAAY;CACZ,UAAU;AACZ"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { cn as e } from "../lib/utils.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
4
|
+
import { GripVerticalIcon as n } from "lucide-react";
|
|
5
|
+
import * as r from "react-resizable-panels";
|
|
6
|
+
//#region src/components/ui/resizable.tsx
|
|
7
|
+
function i({ className: n, ...i }) {
|
|
8
|
+
return /* @__PURE__ */ t(r.PanelGroup, {
|
|
9
|
+
"data-slot": "resizable-panel-group",
|
|
10
|
+
className: e("flex h-full w-full data-[panel-group-direction=vertical]:flex-col", n),
|
|
11
|
+
...i
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function a({ ...e }) {
|
|
15
|
+
return /* @__PURE__ */ t(r.Panel, {
|
|
16
|
+
"data-slot": "resizable-panel",
|
|
17
|
+
...e
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function o({ withHandle: i, className: a, ...o }) {
|
|
21
|
+
return /* @__PURE__ */ t(r.PanelResizeHandle, {
|
|
22
|
+
"data-slot": "resizable-handle",
|
|
23
|
+
className: e("bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90", a),
|
|
24
|
+
...o,
|
|
25
|
+
children: i && /* @__PURE__ */ t("div", {
|
|
26
|
+
className: "bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border",
|
|
27
|
+
children: /* @__PURE__ */ t(n, { className: "size-2.5" })
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { o as ResizableHandle, a as ResizablePanel, i as ResizablePanelGroup };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=resizable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable.js","names":[],"sources":["../../../src/components/ui/resizable.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { GripVerticalIcon } from \"lucide-react\"\nimport * as ResizablePrimitive from \"react-resizable-panels\"\n\nimport { cn } from \"@/components/lib/utils\"\n\nfunction ResizablePanelGroup({\n className,\n ...props\n}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {\n return (\n <ResizablePrimitive.PanelGroup\n data-slot=\"resizable-panel-group\"\n className={cn(\n \"flex h-full w-full data-[panel-group-direction=vertical]:flex-col\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ResizablePanel({\n ...props\n}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {\n return <ResizablePrimitive.Panel data-slot=\"resizable-panel\" {...props} />\n}\n\nfunction ResizableHandle({\n withHandle,\n className,\n ...props\n}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {\n withHandle?: boolean\n}) {\n return (\n <ResizablePrimitive.PanelResizeHandle\n data-slot=\"resizable-handle\"\n className={cn(\n \"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90\",\n className\n )}\n {...props}\n >\n {withHandle && (\n <div className=\"bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border\">\n <GripVerticalIcon className=\"size-2.5\" />\n </div>\n )}\n </ResizablePrimitive.PanelResizeHandle>\n )\n}\n\nexport { ResizablePanelGroup, ResizablePanel, ResizableHandle }\n"],"mappings":";;;;;;AAMA,SAAS,EAAoB,EAC3B,cACA,GAAG,KAC0D;CAC7D,OACE,kBAAC,EAAmB,YAApB;EACE,aAAU;EACV,WAAW,EACT,qEACA,CACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,EAAe,EACtB,GAAG,KACqD;CACxD,OAAO,kBAAC,EAAmB,OAApB;EAA0B,aAAU;EAAkB,GAAI;CAAQ,CAAA;AAC3E;AAEA,SAAS,EAAgB,EACvB,eACA,cACA,GAAG,KAGF;CACD,OACE,kBAAC,EAAmB,mBAApB;EACE,aAAU;EACV,WAAW,EACT,6oBACA,CACF;EACA,GAAI;YAEH,KACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAkB,WAAU,WAAY,CAAA;EACrC,CAAA;CAE6B,CAAA;AAE1C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pihanga2/shadcn",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Pihanga core card components built on shadcn/ui and Radix UI — the npm distribution of pihanga-shadcn.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -60,6 +60,10 @@
|
|
|
60
60
|
"import": "./cards/input/index.js",
|
|
61
61
|
"types": "./cards/input/index.d.ts"
|
|
62
62
|
},
|
|
63
|
+
"./cards/jsonViewer": {
|
|
64
|
+
"import": "./cards/jsonViewer/index.js",
|
|
65
|
+
"types": "./cards/jsonViewer/index.d.ts"
|
|
66
|
+
},
|
|
63
67
|
"./cards/list": {
|
|
64
68
|
"import": "./cards/list/index.js",
|
|
65
69
|
"types": "./cards/list/index.d.ts"
|
|
@@ -92,6 +96,10 @@
|
|
|
92
96
|
"import": "./cards/pasteTarget/index.js",
|
|
93
97
|
"types": "./cards/pasteTarget/index.d.ts"
|
|
94
98
|
},
|
|
99
|
+
"./cards/resizable": {
|
|
100
|
+
"import": "./cards/resizable/index.js",
|
|
101
|
+
"types": "./cards/resizable/index.d.ts"
|
|
102
|
+
},
|
|
95
103
|
"./cards/select": {
|
|
96
104
|
"import": "./cards/select/index.js",
|
|
97
105
|
"types": "./cards/select/index.d.ts"
|
|
@@ -160,6 +168,7 @@
|
|
|
160
168
|
"./cards/form/index.js",
|
|
161
169
|
"./cards/framework/index.js",
|
|
162
170
|
"./cards/input/index.js",
|
|
171
|
+
"./cards/jsonViewer/index.js",
|
|
163
172
|
"./cards/list/index.js",
|
|
164
173
|
"./cards/loadingOverlay/index.js",
|
|
165
174
|
"./cards/loadingSkeleton/index.js",
|
|
@@ -168,6 +177,7 @@
|
|
|
168
177
|
"./cards/navbarSearch/index.js",
|
|
169
178
|
"./cards/pageWithNavbar/index.js",
|
|
170
179
|
"./cards/pasteTarget/index.js",
|
|
180
|
+
"./cards/resizable/index.js",
|
|
171
181
|
"./cards/select/index.js",
|
|
172
182
|
"./cards/slider/index.js",
|
|
173
183
|
"./cards/sliderValue/index.js",
|
|
@@ -216,6 +226,8 @@
|
|
|
216
226
|
"lucide-react": "^0.513.0",
|
|
217
227
|
"radix-ui": "^1.4.3",
|
|
218
228
|
"react-drag-drop-files": "^3.0.1",
|
|
229
|
+
"react-json-view-lite": "^2.5.0",
|
|
230
|
+
"react-resizable-panels": "^2.0.0",
|
|
219
231
|
"sonner": "^2.0.7",
|
|
220
232
|
"tailwind-merge": "^3.3.0"
|
|
221
233
|
}
|
package/pihanga-shadcn.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.pi-file-drop-root label{border:2px dashed #999;flex-grow:1;justify-content:space-between;max-width:100%;display:flex}.pi-file-drop-root .dropzone-msg{cursor:pointer;text-align:center;flex-grow:4;margin:3em 0}.pi-file-drop-root .pi-progress{max-width:100%;padding:1.25em}.pi-file-drop-root .pi-progress-label{font-size:1rem;font-weight:600;line-height:1.5rem}.pi-file-drop -root.pi-progress-container{background-color:#dadfe5;border-radius:4px;height:1rem;display:flex;position:relative;overflow:hidden}.pi-file-drop-root .pi-progress-bar{color:#fff;background-color:#0054a6;flex-direction:column;justify-content:center;display:flex;overflow:hidden}.pi-file-drop-root .pi-progress .label{font-size:1em;display:block}.overlay-wrapper{flex-direction:column;width:100%;min-width:0;display:flex;position:relative}.overlay-wrapper.is-loading.fill-parent{min-height:12rem}.overlay-content{flex-direction:column;flex-grow:1;width:100%;min-width:0;display:flex;position:relative;overflow-y:hidden}.overlay-wrapper.fill-parent .overlay-content{min-height:inherit}.loading-overlay{opacity:0;pointer-events:none;z-index:2;background:oklch(100% 0 0/.7);justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;inset:0}.overlay-wrapper.viewport-centered .loading-overlay{z-index:50;position:fixed;inset:0}.dark .loading-overlay{background:oklch(14.5% 0 0/.5)}.overlay-wrapper.is-loading .loading-overlay{opacity:1;pointer-events:auto}.loading-overlay__content{color:var(--muted-foreground);align-items:center;gap:.5rem;font-size:.875rem;display:flex}.window{justify-content:center;width:100%;height:100%;display:flex}.page{flex-direction:column;flex:1;width:100%;height:100%;display:flex}.header{border-bottom:1px solid var(--border);background-color:var(--background);flex-shrink:0;align-items:center;gap:1rem;padding-left:1rem;padding-right:1rem;display:flex}@media (width>=768px){.header{padding-left:1.5rem;padding-right:1.5rem}}.title-section{align-items:center;gap:.5rem;display:inline-flex}.title-section .title-icon{flex-shrink:0;width:1.5rem;height:1.5rem}.title-section .title-text{color:var(--foreground);font-size:1.125rem;font-weight:600;line-height:1.75rem}.nav-md{display:none}@media (width>=768px){.nav-md{flex-direction:row;align-items:center;gap:1.25rem;display:flex}}@media (width>=1024px){.nav-md{gap:1.5rem}}.nav-md .nav-link{color:var(--muted-foreground);padding-left:0;padding-right:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.nav-md .nav-link:hover{color:var(--foreground)}.nav-sm{gap:1.5rem;padding:1rem;font-size:1.125rem;font-weight:500;line-height:1.75rem;display:grid}.nav-sm .nav-link{color:var(--muted-foreground);justify-content:flex-start;padding-left:0;padding-right:0;font-size:1.125rem;line-height:1.75rem}.nav-sm .nav-link:hover{color:var(--foreground)}.header-actions{align-items:center;gap:.5rem;margin-left:auto;display:flex}@media (width>=1024px){.header-actions{gap:1rem}}.main{background-color:var(--background);flex:1;min-height:0;padding:.5rem 1rem;overflow-y:auto}@media (width>=768px){.main{padding:1rem 2rem}}.footer{background-color:var(--background);color:var(--muted-foreground);flex-shrink:0;padding:.5rem 1rem}@media (width>=768px){.footer{padding:1rem 2rem}}.pihanga-paste-target{border:2px dashed #999}.pihanga-paste-target .paste-target-msg-title{font-size:1.2em}.pihanga-paste-target textarea:focus{outline:none}.pihanga-paste-target-focused{border-width:3px;border-color:#007bff}.pihanga-paste-target-focused .paste-target-focus-reminder{display:none}.pi-stepper{--pi-stepper-indicator-size-sm:1.5rem;--pi-stepper-indicator-size-md:2rem;--pi-stepper-indicator-size-lg:2.5rem;--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-md);--pi-stepper-connector-thickness:2px;--pi-stepper-connector-min-height:1.5rem;--pi-stepper-label-max-width:6rem}.pi-stepper--sm{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-sm)}.pi-stepper--md{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-md)}.pi-stepper--lg{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-lg)}.pi-stepper__indicator{transition:background-color .2s,border-color .2s,color .2s,outline-color .2s}.pi-stepper__indicator--clickable{cursor:pointer}.pi-stepper__indicator--clickable:hover{opacity:.8}.pi-stepper__indicator--static{cursor:default}.pi-stepper__connector{transition:background-color .3s}.pi-stepper__connector--vertical{min-height:var(--pi-stepper-connector-min-height)}.pi-stepper__label-title{transition:color .2s}
|
|
1
|
+
.pi-file-drop-root label{border:2px dashed #999;flex-grow:1;justify-content:space-between;max-width:100%;display:flex}.pi-file-drop-root .dropzone-msg{cursor:pointer;text-align:center;flex-grow:4;margin:3em 0}.pi-file-drop-root .pi-progress{max-width:100%;padding:1.25em}.pi-file-drop-root .pi-progress-label{font-size:1rem;font-weight:600;line-height:1.5rem}.pi-file-drop -root.pi-progress-container{background-color:#dadfe5;border-radius:4px;height:1rem;display:flex;position:relative;overflow:hidden}.pi-file-drop-root .pi-progress-bar{color:#fff;background-color:#0054a6;flex-direction:column;justify-content:center;display:flex;overflow:hidden}.pi-file-drop-root .pi-progress .label{font-size:1em;display:block}._GzYRV{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;line-height:1.2}._3eOF8{margin-right:5px;font-weight:700}._3eOF8+._3eOF8{margin-left:-5px}._1MFti{cursor:pointer}._f10Tu{-webkit-user-select:none;user-select:none;margin-right:5px;font-size:1.2em}._1UmXx:after{content:"▸"}._1LId0:after{content:"▾"}._1pNG9{margin-right:5px}._1pNG9:after{content:"...";font-size:.8em}._2IvMF{background:#eee}._2bkNM{margin:0;padding:0 10px}._1BXBN{margin:0;padding:0}._1MGIk{color:#000;margin-right:5px;font-weight:600}._3uHL6{color:#000}._2T6PJ,._1Gho6{color:#df113a}._vGjyY{color:#2a3f3c}._1bQdo{color:#0b75f5}._3zQKs{color:#469038}._1xvuR{color:#43413d}._oLqym,._2AXVT,._2KJWg{color:#000}._11RoI{background:#002b36}._17H2C,._3QHg2,._3fDAz{color:#fdf6e3}._2bSDX{color:#fdf6e3;margin-right:5px;font-weight:bolder}._gsbQL{color:#fdf6e3}._LaAZe,._GTKgm{color:#81b5ac}._Chy1W{color:#cb4b16}._2bveF{color:#d33682}._2vRm-{color:#ae81ff}._1prJR{color:#268bd2}.jv-wrapper{position:relative}.jv-copy-btn{z-index:10;width:22px;height:22px;color:hsl(var(--muted-foreground,0 0% 55%));cursor:pointer;opacity:.35;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;padding:0;transition:opacity .15s,color .15s,background .15s,border-color .15s;display:inline-flex;position:absolute;top:4px;right:4px}.jv-wrapper:hover .jv-copy-btn,.jv-wrapper:focus-within .jv-copy-btn{opacity:1}.jv-copy-btn:hover{color:hsl(var(--foreground));background:hsl(var(--muted,0 0% 96%));border-color:hsl(var(--border,0 0% 88%))}.jv-copy-btn--done{opacity:1;color:#2e7d32}.dark .jv-copy-btn--done{color:#81c784}.jv-container{white-space:pre-wrap;word-wrap:break-word;color:var(--foreground);background:0 0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12.5px;font-weight:400;line-height:1.55}.jv-basic-child{margin:0;padding:0 0 0 1.1em;display:block;position:relative}.jv-label{color:var(--foreground);margin-right:0;font-weight:400}.jv-clickable-label{color:var(--foreground);cursor:pointer;margin-right:0;font-weight:400}.jv-clickable-label:hover{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.jv-punctuation{color:var(--foreground);font-weight:400}.jv-expand-icon,.jv-collapse-icon{width:1em;color:hsl(var(--muted-foreground,0 0% 55%));-webkit-user-select:none;user-select:none;cursor:pointer;justify-content:center;align-items:center;font-size:.65em;line-height:1;display:inline-flex;position:absolute;top:.775em;left:0;transform:translateY(-50%)}.jv-expand-icon:after{content:"▶"}.jv-collapse-icon:after{content:"▼"}.jv-collapsed-content{cursor:pointer;color:hsl(var(--muted-foreground,0 0% 55%))}.jv-collapsed-content:after{content:" …"}.jv-child-fields-container{border-left:1px solid hsl(var(--border,0 0% 88%));margin:0 0 0 .3em;padding:0 0 0 1.25em}.jv-null,.jv-undefined{color:#c0392b}.jv-string{color:#2e7d32}.jv-number{color:#1565c0}.jv-boolean{color:#00796b}.jv-other{color:var(--foreground)}.dark .jv-string{color:#81c784}.dark .jv-number{color:#64b5f6}.dark .jv-boolean{color:#4db6ac}.dark .jv-null,.dark .jv-undefined{color:#ef9a9a}.dark .jv-child-fields-container{border-left-color:hsl(var(--border,0 0% 25%))}.overlay-wrapper{flex-direction:column;width:100%;min-width:0;display:flex;position:relative}.overlay-wrapper.is-loading.fill-parent{min-height:12rem}.overlay-content{flex-direction:column;flex-grow:1;width:100%;min-width:0;display:flex;position:relative;overflow-y:hidden}.overlay-wrapper.fill-parent .overlay-content{min-height:inherit}.loading-overlay{opacity:0;pointer-events:none;z-index:2;background:oklch(100% 0 0/.7);justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;inset:0}.overlay-wrapper.viewport-centered .loading-overlay{z-index:50;position:fixed;inset:0}.dark .loading-overlay{background:oklch(14.5% 0 0/.5)}.overlay-wrapper.is-loading .loading-overlay{opacity:1;pointer-events:auto}.loading-overlay__content{color:var(--muted-foreground);align-items:center;gap:.5rem;font-size:.875rem;display:flex}.window{justify-content:center;width:100%;height:100%;display:flex}.page{flex-direction:column;flex:1;width:100%;height:100%;display:flex}.header{border-bottom:1px solid var(--border);background-color:var(--background);flex-shrink:0;align-items:center;gap:1rem;padding-left:1rem;padding-right:1rem;display:flex}@media (width>=768px){.header{padding-left:1.5rem;padding-right:1.5rem}}.title-section{align-items:center;gap:.5rem;display:inline-flex}.title-section .title-icon{flex-shrink:0;width:1.5rem;height:1.5rem}.title-section .title-text{color:var(--foreground);font-size:1.125rem;font-weight:600;line-height:1.75rem}.nav-md{display:none}@media (width>=768px){.nav-md{flex-direction:row;align-items:center;gap:1.25rem;display:flex}}@media (width>=1024px){.nav-md{gap:1.5rem}}.nav-md .nav-link{color:var(--muted-foreground);padding-left:0;padding-right:0;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.nav-md .nav-link:hover{color:var(--foreground)}.nav-sm{gap:1.5rem;padding:1rem;font-size:1.125rem;font-weight:500;line-height:1.75rem;display:grid}.nav-sm .nav-link{color:var(--muted-foreground);justify-content:flex-start;padding-left:0;padding-right:0;font-size:1.125rem;line-height:1.75rem}.nav-sm .nav-link:hover{color:var(--foreground)}.header-actions{align-items:center;gap:.5rem;margin-left:auto;display:flex}@media (width>=1024px){.header-actions{gap:1rem}}.main{background-color:var(--background);flex:1;min-height:0;padding:.5rem 1rem;overflow-y:auto}@media (width>=768px){.main{padding:1rem 2rem}}.footer{background-color:var(--background);color:var(--muted-foreground);flex-shrink:0;padding:.5rem 1rem}@media (width>=768px){.footer{padding:1rem 2rem}}.pihanga-paste-target{border:2px dashed #999}.pihanga-paste-target .paste-target-msg-title{font-size:1.2em}.pihanga-paste-target textarea:focus{outline:none}.pihanga-paste-target-focused{border-width:3px;border-color:#007bff}.pihanga-paste-target-focused .paste-target-focus-reminder{display:none}.pi-stepper{--pi-stepper-indicator-size-sm:1.5rem;--pi-stepper-indicator-size-md:2rem;--pi-stepper-indicator-size-lg:2.5rem;--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-md);--pi-stepper-connector-thickness:2px;--pi-stepper-connector-min-height:1.5rem;--pi-stepper-label-max-width:6rem}.pi-stepper--sm{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-sm)}.pi-stepper--md{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-md)}.pi-stepper--lg{--pi-stepper-indicator-size:var(--pi-stepper-indicator-size-lg)}.pi-stepper__indicator{transition:background-color .2s,border-color .2s,color .2s,outline-color .2s}.pi-stepper__indicator--clickable{cursor:pointer}.pi-stepper__indicator--clickable:hover{opacity:.8}.pi-stepper__indicator--static{cursor:default}.pi-stepper__connector{transition:background-color .3s}.pi-stepper__connector--vertical{min-height:var(--pi-stepper-connector-min-height)}.pi-stepper__label-title{transition:color .2s}
|
|
2
2
|
/*$vite$:1*/
|