@namroodinc/liquid-editorial 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/UPGRADING.md +25 -0
  3. package/dist/components/AssetCard/index.cjs +2 -0
  4. package/dist/components/AssetCard/index.cjs.map +1 -0
  5. package/dist/components/AssetCard/index.d.cts +41 -0
  6. package/dist/components/AssetCard/index.d.cts.map +1 -0
  7. package/dist/components/AssetCard/index.d.ts +41 -0
  8. package/dist/components/AssetCard/index.d.ts.map +1 -0
  9. package/dist/components/AssetCard/index.js +159 -0
  10. package/dist/components/AssetCard/index.js.map +1 -0
  11. package/dist/components/AssetCard/style.css +204 -0
  12. package/dist/components/AssetGrid/index.cjs +1 -1
  13. package/dist/components/AssetGrid/index.cjs.map +1 -1
  14. package/dist/components/AssetGrid/index.d.cts +5 -30
  15. package/dist/components/AssetGrid/index.d.cts.map +1 -1
  16. package/dist/components/AssetGrid/index.d.ts +5 -30
  17. package/dist/components/AssetGrid/index.d.ts.map +1 -1
  18. package/dist/components/AssetGrid/index.js +20 -107
  19. package/dist/components/AssetGrid/index.js.map +1 -1
  20. package/dist/components/AssetGrid/style.css +0 -161
  21. package/dist/components/Button/index.cjs +1 -1
  22. package/dist/components/Button/index.cjs.map +1 -1
  23. package/dist/components/Button/index.js +1 -1
  24. package/dist/components/Button/index.js.map +1 -1
  25. package/dist/components/Button/style.css +0 -16
  26. package/dist/components/Icon/style.css +18 -0
  27. package/dist/components/ImageCropper/index.cjs +1 -1
  28. package/dist/components/ImageCropper/index.cjs.map +1 -1
  29. package/dist/components/ImageCropper/index.d.cts +16 -1
  30. package/dist/components/ImageCropper/index.d.cts.map +1 -1
  31. package/dist/components/ImageCropper/index.d.ts +16 -1
  32. package/dist/components/ImageCropper/index.d.ts.map +1 -1
  33. package/dist/components/ImageCropper/index.js +143 -107
  34. package/dist/components/ImageCropper/index.js.map +1 -1
  35. package/dist/index.cjs +1 -1
  36. package/dist/index.d.cts +3 -1
  37. package/dist/index.d.cts.map +1 -1
  38. package/dist/index.d.ts +3 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +34 -33
  41. package/dist/styles.css +1 -0
  42. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { type HTMLAttributes, type ReactNode } from "react";
2
- import type { IconName } from "../Icon/icons.cjs";
2
+ import { type AssetCardSkeletonProps } from "../AssetCard/index.cjs";
3
3
  export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
4
4
  /**
5
5
  * Renders placeholder tiles instead of children. The grid keeps its
@@ -8,6 +8,8 @@ export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
8
8
  isLoading?: boolean;
9
9
  /** How many placeholder tiles to show while loading. */
10
10
  loadingCount?: number;
11
+ /** Slots each placeholder reserves; match the cards that replace them. */
12
+ loadingCard?: Omit<AssetCardSkeletonProps, "index">;
11
13
  children?: ReactNode;
12
14
  }
13
15
  /**
@@ -15,35 +17,8 @@ export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
15
17
  * a `TableFrame` when it is the page's collection, bare inside a drawer.
16
18
  */
17
19
  export declare const AssetGrid: import("react").ForwardRefExoticComponent<AssetGridProps & import("react").RefAttributes<HTMLDivElement>>;
18
- export interface AssetTileSkeletonProps {
19
- /** Position in the grid — drives the stagger, nothing else. */
20
- index?: number;
21
- }
22
- /** One loading tile: thumbnail block, name line, meta line. */
23
- export declare function AssetTileSkeleton({ index }: AssetTileSkeletonProps): import("react").JSX.Element;
24
- export interface AssetTileProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect" | "title"> {
25
- /** File name, shown in full — asset names are how people find things again. */
26
- name: string;
27
- /** Thumbnail source. Omit for a non-image asset and pass an `icon` instead. */
28
- src?: string;
29
- /** Glyph shown when there is no thumbnail, or while one fails to load. */
30
- icon?: IconName;
31
- /** Second line: dimensions, size, date — whatever the library sorts by. */
32
- meta?: ReactNode;
33
- /** Paints the soft fill and the solid rail. Never changes the tile's size. */
34
- isSelected?: boolean;
35
- /** The asset is still uploading or processing: dimmed, with a spinner. */
36
- isPending?: boolean;
37
- /** Makes the whole tile a selection target. */
38
- onSelect?: () => void;
39
- /** Badges or a menu, pinned to the top of the thumbnail. */
40
- actions?: ReactNode;
41
- }
42
- /**
43
- * One asset in the library. Selection paints a fill and a rail exactly like a
44
- * table row, so a grid and a list read as the same collection in two shapes.
45
- */
46
- export declare const AssetTile: import("react").ForwardRefExoticComponent<AssetTileProps & import("react").RefAttributes<HTMLDivElement>>;
20
+ export { AssetCard as AssetTile, AssetCardSkeleton as AssetTileSkeleton } from "../AssetCard/index.cjs";
21
+ export type { AssetCardProps as AssetTileProps, AssetCardSkeletonProps as AssetTileSkeletonProps } from "../AssetCard/index.cjs";
47
22
  export interface AssetGridEmptyProps {
48
23
  title: string;
49
24
  description?: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/AssetGrid/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,aAAa,CAAC;AAMrB,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAmBpB,CAAC;AAEH,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,EAAE,KAAS,EAAE,EAAE,sBAAsB,+BAWtE;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAClE,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAyEpB,CAAC;AAEH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,mBAAmB,+BASjF"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/AssetGrid/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,aAAa,CAAC;AAMrB,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;IACpD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAmBpB,CAAC;AAGH,OAAO,EAAE,SAAS,IAAI,SAAS,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC9F,YAAY,EAAE,cAAc,IAAI,cAAc,EAAE,sBAAsB,IAAI,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEvH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,mBAAmB,+BASjF"}
@@ -1,5 +1,5 @@
1
1
  import { type HTMLAttributes, type ReactNode } from "react";
2
- import type { IconName } from "../Icon/icons.js";
2
+ import { type AssetCardSkeletonProps } from "../AssetCard/index.js";
3
3
  export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
4
4
  /**
5
5
  * Renders placeholder tiles instead of children. The grid keeps its
@@ -8,6 +8,8 @@ export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
8
8
  isLoading?: boolean;
9
9
  /** How many placeholder tiles to show while loading. */
10
10
  loadingCount?: number;
11
+ /** Slots each placeholder reserves; match the cards that replace them. */
12
+ loadingCard?: Omit<AssetCardSkeletonProps, "index">;
11
13
  children?: ReactNode;
12
14
  }
13
15
  /**
@@ -15,35 +17,8 @@ export interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {
15
17
  * a `TableFrame` when it is the page's collection, bare inside a drawer.
16
18
  */
17
19
  export declare const AssetGrid: import("react").ForwardRefExoticComponent<AssetGridProps & import("react").RefAttributes<HTMLDivElement>>;
18
- export interface AssetTileSkeletonProps {
19
- /** Position in the grid — drives the stagger, nothing else. */
20
- index?: number;
21
- }
22
- /** One loading tile: thumbnail block, name line, meta line. */
23
- export declare function AssetTileSkeleton({ index }: AssetTileSkeletonProps): import("react").JSX.Element;
24
- export interface AssetTileProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect" | "title"> {
25
- /** File name, shown in full — asset names are how people find things again. */
26
- name: string;
27
- /** Thumbnail source. Omit for a non-image asset and pass an `icon` instead. */
28
- src?: string;
29
- /** Glyph shown when there is no thumbnail, or while one fails to load. */
30
- icon?: IconName;
31
- /** Second line: dimensions, size, date — whatever the library sorts by. */
32
- meta?: ReactNode;
33
- /** Paints the soft fill and the solid rail. Never changes the tile's size. */
34
- isSelected?: boolean;
35
- /** The asset is still uploading or processing: dimmed, with a spinner. */
36
- isPending?: boolean;
37
- /** Makes the whole tile a selection target. */
38
- onSelect?: () => void;
39
- /** Badges or a menu, pinned to the top of the thumbnail. */
40
- actions?: ReactNode;
41
- }
42
- /**
43
- * One asset in the library. Selection paints a fill and a rail exactly like a
44
- * table row, so a grid and a list read as the same collection in two shapes.
45
- */
46
- export declare const AssetTile: import("react").ForwardRefExoticComponent<AssetTileProps & import("react").RefAttributes<HTMLDivElement>>;
20
+ export { AssetCard as AssetTile, AssetCardSkeleton as AssetTileSkeleton } from "../AssetCard/index.js";
21
+ export type { AssetCardProps as AssetTileProps, AssetCardSkeletonProps as AssetTileSkeletonProps } from "../AssetCard/index.js";
47
22
  export interface AssetGridEmptyProps {
48
23
  title: string;
49
24
  description?: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AssetGrid/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,aAAa,CAAC;AAMrB,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAmBpB,CAAC;AAEH,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,EAAE,KAAS,EAAE,EAAE,sBAAsB,+BAWtE;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAClE,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAyEpB,CAAC;AAEH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,mBAAmB,+BASjF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AssetGrid/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,aAAa,CAAC;AAMrB,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;IACpD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,2GAmBpB,CAAC;AAGH,OAAO,EAAE,SAAS,IAAI,SAAS,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC9F,YAAY,EAAE,cAAc,IAAI,cAAc,EAAE,sBAAsB,IAAI,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEvH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,mBAAmB,+BASjF"}
@@ -1,127 +1,40 @@
1
1
  import { t as e } from "../../chunks/Icon-CGY1FNXT.js";
2
- import { Button as t } from "../Button/index.js";
3
- import { Grid as n } from "../Grid/index.js";
4
- import { Skeleton as r } from "../Skeleton/index.js";
5
- import { forwardRef as i, useState as a } from "react";
6
- import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
2
+ import { Grid as t } from "../Grid/index.js";
3
+ import { AssetCard as n, AssetCardSkeleton as r } from "../AssetCard/index.js";
4
+ import { forwardRef as i } from "react";
5
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
6
  //#region src/components/AssetGrid/index.tsx
8
- function l(...e) {
7
+ function s(...e) {
9
8
  return e.filter(Boolean).join(" ");
10
9
  }
11
- var u = i(function({ isLoading: e = !1, loadingCount: t = 8, className: r, children: i, ...a }, o) {
12
- return /* @__PURE__ */ s(n, {
13
- ref: o,
10
+ var c = i(function({ isLoading: e = !1, loadingCount: n = 8, loadingCard: i, className: o, children: c, ...l }, u) {
11
+ return /* @__PURE__ */ a(t, {
12
+ ref: u,
14
13
  columns: "auto",
15
14
  gap: "md",
16
15
  minColumnWidth: "var(--asset-tile-min-width)",
17
- className: l("ds-asset-grid", r),
16
+ className: s("ds-asset-grid", o),
18
17
  "aria-busy": e || void 0,
19
- ...a,
20
- children: e ? Array.from({ length: t }, (e, t) => /* @__PURE__ */ s(d, { index: t }, t)) : i
18
+ ...l,
19
+ children: e ? Array.from({ length: n }, (e, t) => /* @__PURE__ */ a(r, {
20
+ ...i,
21
+ index: t
22
+ }, t)) : c
21
23
  });
22
24
  });
23
- function d({ index: e = 0 }) {
24
- let t = { animationDelay: `calc(var(--skeleton-stagger) * ${e})` };
25
- return /* @__PURE__ */ c("div", {
26
- className: "ds-asset-tile",
27
- "data-loading": "true",
28
- "aria-hidden": "true",
29
- children: [/* @__PURE__ */ s(r, {
30
- shape: "block",
31
- className: "ds-asset-tile__thumb",
32
- style: t
33
- }), /* @__PURE__ */ c("div", {
34
- className: "ds-asset-tile__text",
35
- children: [/* @__PURE__ */ s(r, {
36
- shape: "text",
37
- width: "80%",
38
- style: t
39
- }), /* @__PURE__ */ s(r, {
40
- shape: "text",
41
- width: "45%",
42
- style: t
43
- })]
44
- })]
45
- });
46
- }
47
- var f = i(function({ name: n, src: i, icon: u = "image", meta: d, isSelected: f = !1, isPending: p = !1, onSelect: m, actions: h, className: g, ..._ }, v) {
48
- let [y, b] = a(!1), [x, S] = a(!1), C = !!i && !y;
49
- return /* @__PURE__ */ c("div", {
50
- ref: v,
51
- className: l("ds-asset-tile", g),
52
- "data-selected": f || void 0,
53
- "data-pending": p || void 0,
54
- ..._,
55
- children: [/* @__PURE__ */ c("div", {
56
- className: "ds-asset-tile__thumb",
57
- children: [
58
- C ? /* @__PURE__ */ c(o, { children: [!x && /* @__PURE__ */ s(r, {
59
- shape: "block",
60
- className: "ds-asset-tile__thumb-skeleton"
61
- }), /* @__PURE__ */ s("img", {
62
- src: i,
63
- alt: "",
64
- loading: "lazy",
65
- "data-loaded": x || void 0,
66
- onLoad: () => S(!0),
67
- onError: () => b(!0)
68
- })] }) : /* @__PURE__ */ s("span", {
69
- className: "ds-asset-tile__placeholder",
70
- children: /* @__PURE__ */ s(e, {
71
- name: y ? "alert-triangle" : u,
72
- size: 24
73
- })
74
- }),
75
- p && /* @__PURE__ */ c("span", {
76
- className: "ds-asset-tile__pending",
77
- role: "status",
78
- children: [/* @__PURE__ */ s(e, {
79
- name: "spinner",
80
- size: 20,
81
- className: "ds-asset-tile__spinner"
82
- }), /* @__PURE__ */ s("span", {
83
- className: "ds-asset-tile__pending-label",
84
- children: "Uploading"
85
- })]
86
- }),
87
- h && /* @__PURE__ */ s("div", {
88
- className: "ds-asset-tile__actions",
89
- children: h
90
- }),
91
- m && /* @__PURE__ */ s(t, {
92
- variant: "simple",
93
- className: "ds-asset-tile__hit",
94
- "aria-pressed": f,
95
- "aria-label": n,
96
- isDisabled: p,
97
- onClick: m
98
- })
99
- ]
100
- }), /* @__PURE__ */ c("div", {
101
- className: "ds-asset-tile__text",
102
- children: [/* @__PURE__ */ s("span", {
103
- className: "ds-asset-tile__name",
104
- children: n
105
- }), d && /* @__PURE__ */ s("span", {
106
- className: "ds-asset-tile__meta",
107
- children: d
108
- })]
109
- })]
110
- });
111
- });
112
- function p({ title: t, description: n, action: r }) {
113
- return /* @__PURE__ */ c("div", {
25
+ function l({ title: t, description: n, action: r }) {
26
+ return /* @__PURE__ */ o("div", {
114
27
  className: "ds-asset-empty",
115
28
  children: [
116
- /* @__PURE__ */ s(e, {
29
+ /* @__PURE__ */ a(e, {
117
30
  name: "image",
118
31
  size: 24
119
32
  }),
120
- /* @__PURE__ */ s("span", {
33
+ /* @__PURE__ */ a("span", {
121
34
  className: "ds-asset-empty__title",
122
35
  children: t
123
36
  }),
124
- n && /* @__PURE__ */ s("span", {
37
+ n && /* @__PURE__ */ a("span", {
125
38
  className: "ds-asset-empty__description",
126
39
  children: n
127
40
  }),
@@ -130,6 +43,6 @@ function p({ title: t, description: n, action: r }) {
130
43
  });
131
44
  }
132
45
  //#endregion
133
- export { u as AssetGrid, p as AssetGridEmpty, f as AssetTile, d as AssetTileSkeleton };
46
+ export { c as AssetGrid, l as AssetGridEmpty, n as AssetTile, r as AssetTileSkeleton };
134
47
 
135
48
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_Fragment","Icon","Fragment$1","cx","parts","AssetGrid","forwardRef","rest","Grid","className","isLoading","loadingCount","AssetTileSkeleton","i","children","delay","index","Skeleton","AssetTile","hasFailed","setHasFailed","useState","isLoaded","setIsLoaded","showImage","src","isSelected","isPending","icon","actions","onSelect","Button","name","meta","AssetGridEmpty","title","description","action"],"sources":["../../../src/components/AssetGrid/index.tsx"],"sourcesContent":["import {\n forwardRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Grid } from \"../Grid\";\nimport { Button } from \"../Button\";\nimport { Icon } from \"../Icon\";\nimport type { IconName } from \"../Icon/icons\";\nimport { Skeleton } from \"../Skeleton\";\nimport \"./style.css\";\n\nfunction cx(...parts: (string | undefined | false)[]) {\n return parts.filter(Boolean).join(\" \");\n}\n\nexport interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Renders placeholder tiles instead of children. The grid keeps its\n * geometry, so nothing moves when the real assets arrive.\n */\n isLoading?: boolean;\n /** How many placeholder tiles to show while loading. */\n loadingCount?: number;\n children?: ReactNode;\n}\n\n/**\n * The media library surface: an auto-filling grid of asset tiles. Sits inside\n * a `TableFrame` when it is the page's collection, bare inside a drawer.\n */\nexport const AssetGrid = forwardRef<HTMLDivElement, AssetGridProps>(function AssetGrid(\n { isLoading = false, loadingCount = 8, className, children, ...rest },\n ref,\n) {\n return (\n <Grid\n ref={ref}\n columns=\"auto\"\n gap=\"md\"\n minColumnWidth=\"var(--asset-tile-min-width)\"\n className={cx(\"ds-asset-grid\", className)}\n aria-busy={isLoading || undefined}\n {...rest}\n >\n {isLoading\n ? Array.from({ length: loadingCount }, (_, i) => <AssetTileSkeleton key={i} index={i} />)\n : children}\n </Grid>\n );\n});\n\nexport interface AssetTileSkeletonProps {\n /** Position in the grid — drives the stagger, nothing else. */\n index?: number;\n}\n\n/** One loading tile: thumbnail block, name line, meta line. */\nexport function AssetTileSkeleton({ index = 0 }: AssetTileSkeletonProps) {\n const delay = { animationDelay: `calc(var(--skeleton-stagger) * ${index})` };\n return (\n <div className=\"ds-asset-tile\" data-loading=\"true\" aria-hidden=\"true\">\n <Skeleton shape=\"block\" className=\"ds-asset-tile__thumb\" style={delay} />\n <div className=\"ds-asset-tile__text\">\n <Skeleton shape=\"text\" width=\"80%\" style={delay} />\n <Skeleton shape=\"text\" width=\"45%\" style={delay} />\n </div>\n </div>\n );\n}\n\nexport interface AssetTileProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\" | \"title\"> {\n /** File name, shown in full — asset names are how people find things again. */\n name: string;\n /** Thumbnail source. Omit for a non-image asset and pass an `icon` instead. */\n src?: string;\n /** Glyph shown when there is no thumbnail, or while one fails to load. */\n icon?: IconName;\n /** Second line: dimensions, size, date — whatever the library sorts by. */\n meta?: ReactNode;\n /** Paints the soft fill and the solid rail. Never changes the tile's size. */\n isSelected?: boolean;\n /** The asset is still uploading or processing: dimmed, with a spinner. */\n isPending?: boolean;\n /** Makes the whole tile a selection target. */\n onSelect?: () => void;\n /** Badges or a menu, pinned to the top of the thumbnail. */\n actions?: ReactNode;\n}\n\n/**\n * One asset in the library. Selection paints a fill and a rail exactly like a\n * table row, so a grid and a list read as the same collection in two shapes.\n */\nexport const AssetTile = forwardRef<HTMLDivElement, AssetTileProps>(function AssetTile(\n {\n name,\n src,\n icon = \"image\",\n meta,\n isSelected = false,\n isPending = false,\n onSelect,\n actions,\n className,\n ...rest\n },\n ref,\n) {\n const [hasFailed, setHasFailed] = useState(false);\n const [isLoaded, setIsLoaded] = useState(false);\n const showImage = Boolean(src) && !hasFailed;\n\n return (\n <div\n ref={ref}\n className={cx(\"ds-asset-tile\", className)}\n data-selected={isSelected || undefined}\n data-pending={isPending || undefined}\n {...rest}\n >\n <div className=\"ds-asset-tile__thumb\">\n {showImage ? (\n <>\n {!isLoaded && <Skeleton shape=\"block\" className=\"ds-asset-tile__thumb-skeleton\" />}\n <img\n src={src}\n alt=\"\"\n loading=\"lazy\"\n data-loaded={isLoaded || undefined}\n onLoad={() => setIsLoaded(true)}\n onError={() => setHasFailed(true)}\n />\n </>\n ) : (\n <span className=\"ds-asset-tile__placeholder\">\n <Icon name={hasFailed ? \"alert-triangle\" : icon} size={24} />\n </span>\n )}\n\n {isPending && (\n <span className=\"ds-asset-tile__pending\" role=\"status\">\n <Icon name=\"spinner\" size={20} className=\"ds-asset-tile__spinner\" />\n <span className=\"ds-asset-tile__pending-label\">Uploading</span>\n </span>\n )}\n\n {actions && <div className=\"ds-asset-tile__actions\">{actions}</div>}\n\n {onSelect && (\n <Button\n variant=\"simple\"\n className=\"ds-asset-tile__hit\"\n aria-pressed={isSelected}\n aria-label={name}\n isDisabled={isPending}\n onClick={onSelect}\n />\n )}\n </div>\n\n <div className=\"ds-asset-tile__text\">\n <span className=\"ds-asset-tile__name\">{name}</span>\n {meta && <span className=\"ds-asset-tile__meta\">{meta}</span>}\n </div>\n </div>\n );\n});\n\nexport interface AssetGridEmptyProps {\n title: string;\n description?: ReactNode;\n action?: ReactNode;\n}\n\n/** Nothing in the library yet — one action, the one that fills it. */\nexport function AssetGridEmpty({ title, description, action }: AssetGridEmptyProps) {\n return (\n <div className=\"ds-asset-empty\">\n <Icon name=\"image\" size={24} />\n <span className=\"ds-asset-empty__title\">{title}</span>\n {description && <span className=\"ds-asset-empty__description\">{description}</span>}\n {action}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAASG,EAAG,GAAGC,GAAuC;CACpD,OAAOA,EAAM,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AACvC;AAiBA,IAAaC,IAAYC,EAA2C,SAClE,EAAE,eAAY,IAAO,kBAAe,GAAG,cAAW,aAAU,GAAGC,KAC/D,GACA;CACA,OACE,kBAACC,GAAD;EACO;EACL,SAAQ;EACR,KAAI;EACJ,gBAAe;EACf,WAAWL,EAAG,iBAAiBM,CAAS;EACxC,aAAWC,KAAa,KAAA;EACxB,GAAIH;EAEH,UAAAG,IACG,MAAM,KAAK,EAAE,QAAQC,EAAa,IAAI,GAAG,MAAM,kBAACC,GAAD,EAA2B,OAAOC,EAAI,GAAdA,CAAc,CAAC,IACtFC;CACA,CAAA;AAEV,CAAC;AAQD,SAAgBF,EAAkB,EAAE,WAAQ,KAA6B;CACvE,IAAMG,IAAQ,EAAE,gBAAgB,kCAAkCC,EAAM,GAAG;CAC3E,OACE,kBAAC,OAAD;EAAK,WAAU;EAAgB,gBAAa;EAAO,eAAY;EAA/D,UAAA,CACE,kBAACC,GAAD;GAAU,OAAM;GAAQ,WAAU;GAAuB,OAAOF;EAAQ,CAAA,GACxE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAACE,GAAD;IAAU,OAAM;IAAO,OAAM;IAAM,OAAOF;GAAQ,CAAA,GAClD,kBAACE,GAAD;IAAU,OAAM;IAAO,OAAM;IAAM,OAAOF;GAAQ,CAAA,CAC/C;EACF,CAAA,CAAA;;AAET;AA0BA,IAAaG,IAAYZ,EAA2C,SAClE,EACE,SACA,QACA,UAAO,SACP,SACA,gBAAa,IACb,eAAY,IACZ,aACA,YACA,cACA,GAAGC,KAEL,GACA;CACA,IAAM,CAACY,GAAWC,KAAgBC,EAAS,EAAK,GAC1C,CAACC,GAAUC,KAAeF,EAAS,EAAK,GACxCG,IAAY,EAAQC,KAAQ,CAACN;CAEnC,OACE,kBAAC,OAAD;EACO;EACL,WAAWhB,EAAG,iBAAiBM,CAAS;EACxC,iBAAeiB,KAAc,KAAA;EAC7B,gBAAcC,KAAa,KAAA;EAC3B,GAAIpB;EALN,UAAA,CAOE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACGiB,IACC,kBAAAxB,GAAA,EAAA,UAAA,CACG,CAACsB,KAAY,kBAACL,GAAD;KAAU,OAAM;KAAQ,WAAU;IAAiC,CAAA,GACjF,kBAAC,OAAD;KACO;KACL,KAAI;KACJ,SAAQ;KACR,eAAaK,KAAY,KAAA;KACzB,cAAcC,EAAY,EAAI;KAC9B,eAAeH,EAAa,EAAI;IACjC,CAAA,CACD,EAAA,CAAA,IAEF,kBAAC,QAAD;KAAM,WAAU;KACd,UAAA,kBAACnB,GAAD;MAAM,MAAMkB,IAAY,mBAAmBS;MAAM,MAAM;KAAK,CAAA;IACxD,CAAA;IAGPD,KACC,kBAAC,QAAD;KAAM,WAAU;KAAyB,MAAK;KAA9C,UAAA,CACE,kBAAC1B,GAAD;MAAM,MAAK;MAAU,MAAM;MAAI,WAAU;KAA0B,CAAA,GACnE,kBAAC,QAAD;MAAM,WAAU;MAA+B,UAAA;KAAe,CAAA,CAC1D;;IAGP4B,KAAW,kBAAC,OAAD;KAAK,WAAU;KAA0B,UAAAA;IAAa,CAAA;IAEjEC,KACC,kBAACC,GAAD;KACE,SAAQ;KACR,WAAU;KACV,gBAAcL;KACd,cAAYM;KACZ,YAAYL;KACZ,SAASG;IACV,CAAA;GAEA;EAEL,CAAA,GAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,QAAD;IAAM,WAAU;IAAuB,UAAAE;GAAW,CAAA,GACjDC,KAAQ,kBAAC,QAAD;IAAM,WAAU;IAAuB,UAAAA;GAAW,CAAA,CACxD;EACF,CAAA,CAAA;;AAET,CAAC;AASD,SAAgBC,EAAe,EAAE,UAAO,gBAAa,aAA+B;CAClF,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,kBAACjC,GAAD;IAAM,MAAK;IAAQ,MAAM;GAAK,CAAA;GAC9B,kBAAC,QAAD;IAAM,WAAU;IAAyB,UAAAkC;GAAY,CAAA;GACpDC,KAAe,kBAAC,QAAD;IAAM,WAAU;IAA+B,UAAAA;GAAkB,CAAA;GAChFC;EACE;;AAET"}
1
+ {"version":3,"file":"index.js","names":["Icon","cx","parts","AssetGrid","forwardRef","rest","Grid","className","isLoading","loadingCount","AssetCardSkeleton","loadingCard","i","children","AssetGridEmpty","title","description","action","AssetCard"],"sources":["../../../src/components/AssetGrid/index.tsx"],"sourcesContent":["import {\n forwardRef,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Grid } from \"../Grid\";\nimport { Icon } from \"../Icon\";\nimport { AssetCardSkeleton, type AssetCardSkeletonProps } from \"../AssetCard\";\nimport \"./style.css\";\n\nfunction cx(...parts: (string | undefined | false)[]) {\n return parts.filter(Boolean).join(\" \");\n}\n\nexport interface AssetGridProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Renders placeholder tiles instead of children. The grid keeps its\n * geometry, so nothing moves when the real assets arrive.\n */\n isLoading?: boolean;\n /** How many placeholder tiles to show while loading. */\n loadingCount?: number;\n /** Slots each placeholder reserves; match the cards that replace them. */\n loadingCard?: Omit<AssetCardSkeletonProps, \"index\">;\n children?: ReactNode;\n}\n\n/**\n * The media library surface: an auto-filling grid of asset tiles. Sits inside\n * a `TableFrame` when it is the page's collection, bare inside a drawer.\n */\nexport const AssetGrid = forwardRef<HTMLDivElement, AssetGridProps>(function AssetGrid(\n { isLoading = false, loadingCount = 8, loadingCard, className, children, ...rest },\n ref,\n) {\n return (\n <Grid\n ref={ref}\n columns=\"auto\"\n gap=\"md\"\n minColumnWidth=\"var(--asset-tile-min-width)\"\n className={cx(\"ds-asset-grid\", className)}\n aria-busy={isLoading || undefined}\n {...rest}\n >\n {isLoading\n ? Array.from({ length: loadingCount }, (_, i) => <AssetCardSkeleton key={i} {...loadingCard} index={i} />)\n : children}\n </Grid>\n );\n});\n\n// Existing entry points remain aliases to the shared card implementation.\nexport { AssetCard as AssetTile, AssetCardSkeleton as AssetTileSkeleton } from \"../AssetCard\";\nexport type { AssetCardProps as AssetTileProps, AssetCardSkeletonProps as AssetTileSkeletonProps } from \"../AssetCard\";\n\nexport interface AssetGridEmptyProps {\n title: string;\n description?: ReactNode;\n action?: ReactNode;\n}\n\n/** Nothing in the library yet — one action, the one that fills it. */\nexport function AssetGridEmpty({ title, description, action }: AssetGridEmptyProps) {\n return (\n <div className=\"ds-asset-empty\">\n <Icon name=\"image\" size={24} />\n <span className=\"ds-asset-empty__title\">{title}</span>\n {description && <span className=\"ds-asset-empty__description\">{description}</span>}\n {action}\n </div>\n );\n}\n"],"mappings":";;;;;;AAUA,SAASC,EAAG,GAAGC,GAAuC;CACpD,OAAOA,EAAM,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AACvC;AAmBA,IAAaC,IAAYC,EAA2C,SAClE,EAAE,eAAY,IAAO,kBAAe,GAAG,gBAAa,cAAW,aAAU,GAAGC,KAC5E,GACA;CACA,OACE,kBAACC,GAAD;EACO;EACL,SAAQ;EACR,KAAI;EACJ,gBAAe;EACf,WAAWL,EAAG,iBAAiBM,CAAS;EACxC,aAAWC,KAAa,KAAA;EACxB,GAAIH;EAEH,UAAAG,IACG,MAAM,KAAK,EAAE,QAAQC,EAAa,IAAI,GAAG,MAAM,kBAACC,GAAD;GAA2B,GAAIC;GAAa,OAAOC;EAAI,GAA/BA,CAA+B,CAAC,IACvGC;CACA,CAAA;AAEV,CAAC;AAaD,SAAgBC,EAAe,EAAE,UAAO,gBAAa,aAA+B;CAClF,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,kBAACd,GAAD;IAAM,MAAK;IAAQ,MAAM;GAAK,CAAA;GAC9B,kBAAC,QAAD;IAAM,WAAU;IAAyB,UAAAe;GAAY,CAAA;GACpDC,KAAe,kBAAC,QAAD;IAAM,WAAU;IAA+B,UAAAA;GAAkB,CAAA;GAChFC;EACE;;AAET"}
@@ -4,156 +4,6 @@
4
4
  padding: var(--space-4);
5
5
  }
6
6
 
7
- .ds-asset-tile {
8
- position: relative;
9
- display: flex;
10
- flex-direction: column;
11
- gap: var(--space-3);
12
- padding: var(--space-3);
13
- background: var(--color-surface);
14
- border: var(--control-border-width) solid var(--control-border-color);
15
- border-radius: var(--radius-md);
16
- /* The rail exists on every tile, so selecting one never shifts the grid. */
17
- box-shadow: inset var(--rail-width) 0 0 0 transparent;
18
- transition:
19
- background-color var(--duration-fast) var(--ease-standard),
20
- box-shadow var(--duration-fast) var(--ease-standard);
21
- }
22
-
23
- .ds-asset-tile:hover:not([data-pending]) {
24
- background: var(--field-hover);
25
- }
26
-
27
- .ds-asset-tile[data-selected] {
28
- background: var(--color-primary-soft);
29
- box-shadow: inset var(--rail-width) 0 0 0 var(--rail-color);
30
- }
31
-
32
- .ds-asset-tile[data-pending] {
33
- opacity: 70%;
34
- }
35
-
36
- .ds-asset-tile[data-loading="true"] {
37
- background: var(--color-surface);
38
- }
39
-
40
- .ds-asset-tile__thumb {
41
- position: relative;
42
- aspect-ratio: var(--asset-thumb-aspect);
43
- overflow: hidden;
44
- border-radius: var(--field-radius);
45
- background: var(--field-rest);
46
- display: grid;
47
- place-content: center;
48
- }
49
-
50
- .ds-asset-tile__thumb img {
51
- position: absolute;
52
- inset: 0;
53
- inline-size: 100%;
54
- block-size: 100%;
55
- object-fit: cover;
56
- opacity: 0;
57
- transition: opacity var(--duration-base) var(--ease-standard);
58
- }
59
-
60
- .ds-asset-tile__thumb img[data-loaded] {
61
- opacity: 100%;
62
- }
63
-
64
- .ds-asset-tile__thumb-skeleton {
65
- position: absolute;
66
- inset: 0;
67
- block-size: 100%;
68
- border-radius: 0;
69
- }
70
-
71
- .ds-asset-tile__placeholder {
72
- display: grid;
73
- place-content: center;
74
- color: var(--color-text-muted);
75
- }
76
-
77
- .ds-asset-tile__pending {
78
- position: absolute;
79
- inset: 0;
80
- display: flex;
81
- flex-direction: column;
82
- align-items: center;
83
- justify-content: center;
84
- gap: var(--space-2);
85
- background: color-mix(in srgb, var(--color-surface) 72%, transparent);
86
- color: var(--color-text-muted);
87
- font-size: var(--font-size-xs);
88
- font-weight: var(--font-weight-bold);
89
- }
90
-
91
- .ds-asset-tile__spinner {
92
- animation: ds-asset-spin 900ms linear infinite;
93
- }
94
-
95
- @keyframes ds-asset-spin {
96
- to {
97
- transform: rotate(360deg);
98
- }
99
- }
100
-
101
- .ds-asset-tile__actions {
102
- position: absolute;
103
- inset-block-start: var(--space-2);
104
- inset-inline: var(--space-2);
105
- z-index: 1;
106
- display: flex;
107
- justify-content: space-between;
108
- gap: var(--space-2);
109
- pointer-events: none;
110
- }
111
-
112
- .ds-asset-tile__actions > * {
113
- pointer-events: auto;
114
- }
115
-
116
- /* The whole thumbnail is the selection target; the name stays readable text
117
- * underneath rather than being swallowed by the button. */
118
- .ds-asset-tile .ds-asset-tile__hit.ds-button {
119
- position: absolute;
120
- inset: 0;
121
- inline-size: 100%;
122
- height: 100%;
123
- padding: 0;
124
- }
125
-
126
- .ds-asset-tile__hit.ds-button:focus-visible {
127
- outline-offset: -2px;
128
- }
129
-
130
- /* Selection reads on the tile, so Button's hover and pressed fills never
131
- * paint over the thumbnail. */
132
- .ds-asset-tile .ds-asset-tile__hit.ds-button:is(:hover, :active):not(:disabled) {
133
- background: transparent;
134
- }
135
-
136
- .ds-asset-tile__text {
137
- display: flex;
138
- flex-direction: column;
139
- gap: var(--space-1);
140
- min-inline-size: 0;
141
- }
142
-
143
- .ds-asset-tile__name {
144
- font-size: var(--font-size-sm);
145
- font-weight: var(--font-weight-bold);
146
- line-height: var(--line-height-body);
147
- overflow-wrap: anywhere;
148
- }
149
-
150
- .ds-asset-tile__meta {
151
- font-family: var(--font-mono);
152
- font-size: var(--font-size-xs);
153
- line-height: var(--line-height-body);
154
- color: var(--color-text-muted);
155
- }
156
-
157
7
  .ds-asset-empty {
158
8
  display: flex;
159
9
  flex-direction: column;
@@ -174,15 +24,4 @@
174
24
  max-inline-size: 34rem;
175
25
  line-height: var(--line-height-body);
176
26
  }
177
-
178
- @media (prefers-reduced-motion: reduce) {
179
- .ds-asset-tile,
180
- .ds-asset-tile__thumb img {
181
- transition: none;
182
- }
183
-
184
- .ds-asset-tile__spinner {
185
- animation: none;
186
- }
187
- }
188
27
  }
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../chunks/Icon-CWsA1W83.cjs");let t=require("react"),n=require("react/jsx-runtime");var r=(0,t.forwardRef)(function({variant:t=`neutral`,size:r=`md`,iconStart:i,iconEnd:a,isIconOnly:o=!1,isDisabled:s=!1,isPending:c=!1,className:l,children:u,type:d=`button`,...f},p){let m=r===`lg`?20:16;return(0,n.jsxs)(`button`,{ref:p,type:d,disabled:s||c,"data-variant":t,"data-size":r,"data-icon-only":o||void 0,"data-pending":c||void 0,className:[`ds-button`,l].filter(Boolean).join(` `),...f,children:[c?(0,n.jsx)(e.t,{name:`spinner`,size:m,className:`ds-button__spinner`}):i&&(0,n.jsx)(e.t,{name:i,size:m}),(!o||!i&&!c)&&u,a&&!o&&(0,n.jsx)(e.t,{name:a,size:m})]})});exports.Button=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../chunks/Icon-CWsA1W83.cjs");let t=require("react"),n=require("react/jsx-runtime");var r=(0,t.forwardRef)(function({variant:t=`neutral`,size:r=`md`,iconStart:i,iconEnd:a,isIconOnly:o=!1,isDisabled:s=!1,isPending:c=!1,className:l,children:u,type:d=`button`,...f},p){let m=r===`lg`?20:16;return(0,n.jsxs)(`button`,{ref:p,type:d,disabled:s||c,"data-variant":t,"data-size":r,"data-icon-only":o||void 0,"data-pending":c||void 0,className:[`ds-button`,l].filter(Boolean).join(` `),...f,children:[c?(0,n.jsx)(e.t,{name:`spinner`,size:m,className:`ds-button__spinner ds-icon--spin`}):i&&(0,n.jsx)(e.t,{name:i,size:m}),(!o||!i&&!c)&&u,a&&!o&&(0,n.jsx)(e.t,{name:a,size:m})]})});exports.Button=r;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["forwardRef","Icon","require_Icon","react","react_jsx_runtime","Button","rest","glyph","size","isDisabled","isPending","variant","isIconOnly","className","iconStart","children","iconEnd"],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport type { IconName } from \"../Icon/icons\";\nimport \"./style.css\";\n\nexport type ButtonVariant = \"primary\" | \"neutral\" | \"danger\" | \"active\" | \"simple\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"disabled\"> {\n variant?: ButtonVariant;\n /** `xs` is for compact actions inside a field or chip, such as clear and remove. */\n size?: ButtonSize;\n iconStart?: IconName;\n iconEnd?: IconName;\n /** Square button. Label with aria-label/aria-labelledby. Children can supply a custom glyph. */\n isIconOnly?: boolean;\n isDisabled?: boolean;\n /** Label stays, spinner replaces `iconStart`, press is blocked. */\n isPending?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Every action the user can take. One primary per view; a sixth variant is a\n * conversation, not a prop.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"neutral\",\n size = \"md\",\n iconStart,\n iconEnd,\n isIconOnly = false,\n isDisabled = false,\n isPending = false,\n className,\n children,\n type = \"button\",\n ...rest\n },\n ref,\n) {\n const glyph = size === \"lg\" ? 20 : 16;\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled || isPending}\n data-variant={variant}\n data-size={size}\n data-icon-only={isIconOnly || undefined}\n data-pending={isPending || undefined}\n className={[\"ds-button\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {isPending ? (\n <Icon name=\"spinner\" size={glyph} className=\"ds-button__spinner\" />\n ) : (\n iconStart && <Icon name={iconStart} size={glyph} />\n )}\n {(!isIconOnly || (!iconStart && !isPending)) && children}\n {iconEnd && !isIconOnly && <Icon name={iconEnd} size={glyph} />}\n </button>\n );\n});\n"],"mappings":"A,2KA2BA,IAAaK,GAAA,EAASL,EAAAA,WAAAA,CAA2C,SAC/D,CACE,UAAU,UACV,OAAO,KACP,YACA,UACA,aAAa,GACb,aAAa,GACb,YAAY,GACZ,YACA,WACA,OAAO,SACP,GAAGM,GAEL,EACA,CACA,IAAMC,EAAQC,IAAS,KAAO,GAAK,GACnC,OACE,EAAAJ,EAAA,KAAA,CAAC,SAAD,CACO,MACC,OACN,SAAUK,GAAcC,EACxB,eAAcC,EACd,YAAWH,EACX,iBAAgBI,GAAc,IAAA,GAC9B,eAAcF,GAAa,IAAA,GAC3B,UAAW,CAAC,YAAaG,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAC5D,GAAIP,EATN,SAAA,CAWGI,GACC,EAAAN,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAK,UAAU,KAAMM,EAAO,UAAU,oBAAsB,CAAA,EAElEO,IAAa,EAAAV,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAMa,EAAW,KAAMP,CAAQ,CAAA,GAElD,CAACK,GAAe,CAACE,GAAa,CAACJ,IAAeK,EAC/CC,GAAW,CAACJ,IAAc,EAAAR,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAMe,EAAS,KAAMT,CAAQ,CAAA,CACxD,C,EAEZ,CAAC,E"}
1
+ {"version":3,"file":"index.cjs","names":["forwardRef","Icon","require_Icon","react","react_jsx_runtime","Button","rest","glyph","size","isDisabled","isPending","variant","isIconOnly","className","iconStart","children","iconEnd"],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport type { IconName } from \"../Icon/icons\";\nimport \"./style.css\";\n\nexport type ButtonVariant = \"primary\" | \"neutral\" | \"danger\" | \"active\" | \"simple\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"disabled\"> {\n variant?: ButtonVariant;\n /** `xs` is for compact actions inside a field or chip, such as clear and remove. */\n size?: ButtonSize;\n iconStart?: IconName;\n iconEnd?: IconName;\n /** Square button. Label with aria-label/aria-labelledby. Children can supply a custom glyph. */\n isIconOnly?: boolean;\n isDisabled?: boolean;\n /** Label stays, spinner replaces `iconStart`, press is blocked. */\n isPending?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Every action the user can take. One primary per view; a sixth variant is a\n * conversation, not a prop.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"neutral\",\n size = \"md\",\n iconStart,\n iconEnd,\n isIconOnly = false,\n isDisabled = false,\n isPending = false,\n className,\n children,\n type = \"button\",\n ...rest\n },\n ref,\n) {\n const glyph = size === \"lg\" ? 20 : 16;\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled || isPending}\n data-variant={variant}\n data-size={size}\n data-icon-only={isIconOnly || undefined}\n data-pending={isPending || undefined}\n className={[\"ds-button\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {isPending ? (\n <Icon name=\"spinner\" size={glyph} className=\"ds-button__spinner ds-icon--spin\" />\n ) : (\n iconStart && <Icon name={iconStart} size={glyph} />\n )}\n {(!isIconOnly || (!iconStart && !isPending)) && children}\n {iconEnd && !isIconOnly && <Icon name={iconEnd} size={glyph} />}\n </button>\n );\n});\n"],"mappings":"A,2KA2BA,IAAaK,GAAA,EAASL,EAAAA,WAAAA,CAA2C,SAC/D,CACE,UAAU,UACV,OAAO,KACP,YACA,UACA,aAAa,GACb,aAAa,GACb,YAAY,GACZ,YACA,WACA,OAAO,SACP,GAAGM,GAEL,EACA,CACA,IAAMC,EAAQC,IAAS,KAAO,GAAK,GACnC,OACE,EAAAJ,EAAA,KAAA,CAAC,SAAD,CACO,MACC,OACN,SAAUK,GAAcC,EACxB,eAAcC,EACd,YAAWH,EACX,iBAAgBI,GAAc,IAAA,GAC9B,eAAcF,GAAa,IAAA,GAC3B,UAAW,CAAC,YAAaG,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAC5D,GAAIP,EATN,SAAA,CAWGI,GACC,EAAAN,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAK,UAAU,KAAMM,EAAO,UAAU,kCAAoC,CAAA,EAEhFO,IAAa,EAAAV,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAMa,EAAW,KAAMP,CAAQ,CAAA,GAElD,CAACK,GAAe,CAACE,GAAa,CAACJ,IAAeK,EAC/CC,GAAW,CAACJ,IAAc,EAAAR,EAAA,IAAA,CAACH,EAAAA,EAAD,CAAM,KAAMe,EAAS,KAAMT,CAAQ,CAAA,CACxD,C,EAEZ,CAAC,E"}
@@ -18,7 +18,7 @@ var i = t(function({ variant: t = "neutral", size: i = "md", iconStart: a, iconE
18
18
  l ? /* @__PURE__ */ n(e, {
19
19
  name: "spinner",
20
20
  size: h,
21
- className: "ds-button__spinner"
21
+ className: "ds-button__spinner ds-icon--spin"
22
22
  }) : a && /* @__PURE__ */ n(e, {
23
23
  name: a,
24
24
  size: h
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Icon","Button","forwardRef","rest","glyph","size","isDisabled","isPending","variant","isIconOnly","className","iconStart","children","iconEnd"],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport type { IconName } from \"../Icon/icons\";\nimport \"./style.css\";\n\nexport type ButtonVariant = \"primary\" | \"neutral\" | \"danger\" | \"active\" | \"simple\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"disabled\"> {\n variant?: ButtonVariant;\n /** `xs` is for compact actions inside a field or chip, such as clear and remove. */\n size?: ButtonSize;\n iconStart?: IconName;\n iconEnd?: IconName;\n /** Square button. Label with aria-label/aria-labelledby. Children can supply a custom glyph. */\n isIconOnly?: boolean;\n isDisabled?: boolean;\n /** Label stays, spinner replaces `iconStart`, press is blocked. */\n isPending?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Every action the user can take. One primary per view; a sixth variant is a\n * conversation, not a prop.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"neutral\",\n size = \"md\",\n iconStart,\n iconEnd,\n isIconOnly = false,\n isDisabled = false,\n isPending = false,\n className,\n children,\n type = \"button\",\n ...rest\n },\n ref,\n) {\n const glyph = size === \"lg\" ? 20 : 16;\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled || isPending}\n data-variant={variant}\n data-size={size}\n data-icon-only={isIconOnly || undefined}\n data-pending={isPending || undefined}\n className={[\"ds-button\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {isPending ? (\n <Icon name=\"spinner\" size={glyph} className=\"ds-button__spinner\" />\n ) : (\n iconStart && <Icon name={iconStart} size={glyph} />\n )}\n {(!isIconOnly || (!iconStart && !isPending)) && children}\n {iconEnd && !isIconOnly && <Icon name={iconEnd} size={glyph} />}\n </button>\n );\n});\n"],"mappings":";;;;AA2BA,IAAaC,IAASC,EAA2C,SAC/D,EACE,aAAU,WACV,UAAO,MACP,cACA,YACA,gBAAa,IACb,gBAAa,IACb,eAAY,IACZ,cACA,aACA,UAAO,UACP,GAAGC,KAEL,GACA;CACA,IAAMC,IAAQC,MAAS,OAAO,KAAK;CACnC,OACE,kBAAC,UAAD;EACO;EACC;EACN,UAAUC,KAAcC;EACxB,gBAAcC;EACd,aAAWH;EACX,kBAAgBI,KAAc,KAAA;EAC9B,gBAAcF,KAAa,KAAA;EAC3B,WAAW,CAAC,aAAaG,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAC5D,GAAIP;EATN,UAAA;GAWGI,IACC,kBAACP,GAAD;IAAM,MAAK;IAAU,MAAMI;IAAO,WAAU;GAAsB,CAAA,IAElEO,KAAa,kBAACX,GAAD;IAAM,MAAMW;IAAW,MAAMP;GAAQ,CAAA;IAElD,CAACK,KAAe,CAACE,KAAa,CAACJ,MAAeK;GAC/CC,KAAW,CAACJ,KAAc,kBAACT,GAAD;IAAM,MAAMa;IAAS,MAAMT;GAAQ,CAAA;EACxD;;AAEZ,CAAC"}
1
+ {"version":3,"file":"index.js","names":["Icon","Button","forwardRef","rest","glyph","size","isDisabled","isPending","variant","isIconOnly","className","iconStart","children","iconEnd"],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport type { IconName } from \"../Icon/icons\";\nimport \"./style.css\";\n\nexport type ButtonVariant = \"primary\" | \"neutral\" | \"danger\" | \"active\" | \"simple\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"disabled\"> {\n variant?: ButtonVariant;\n /** `xs` is for compact actions inside a field or chip, such as clear and remove. */\n size?: ButtonSize;\n iconStart?: IconName;\n iconEnd?: IconName;\n /** Square button. Label with aria-label/aria-labelledby. Children can supply a custom glyph. */\n isIconOnly?: boolean;\n isDisabled?: boolean;\n /** Label stays, spinner replaces `iconStart`, press is blocked. */\n isPending?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Every action the user can take. One primary per view; a sixth variant is a\n * conversation, not a prop.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = \"neutral\",\n size = \"md\",\n iconStart,\n iconEnd,\n isIconOnly = false,\n isDisabled = false,\n isPending = false,\n className,\n children,\n type = \"button\",\n ...rest\n },\n ref,\n) {\n const glyph = size === \"lg\" ? 20 : 16;\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled || isPending}\n data-variant={variant}\n data-size={size}\n data-icon-only={isIconOnly || undefined}\n data-pending={isPending || undefined}\n className={[\"ds-button\", className].filter(Boolean).join(\" \")}\n {...rest}\n >\n {isPending ? (\n <Icon name=\"spinner\" size={glyph} className=\"ds-button__spinner ds-icon--spin\" />\n ) : (\n iconStart && <Icon name={iconStart} size={glyph} />\n )}\n {(!isIconOnly || (!iconStart && !isPending)) && children}\n {iconEnd && !isIconOnly && <Icon name={iconEnd} size={glyph} />}\n </button>\n );\n});\n"],"mappings":";;;;AA2BA,IAAaC,IAASC,EAA2C,SAC/D,EACE,aAAU,WACV,UAAO,MACP,cACA,YACA,gBAAa,IACb,gBAAa,IACb,eAAY,IACZ,cACA,aACA,UAAO,UACP,GAAGC,KAEL,GACA;CACA,IAAMC,IAAQC,MAAS,OAAO,KAAK;CACnC,OACE,kBAAC,UAAD;EACO;EACC;EACN,UAAUC,KAAcC;EACxB,gBAAcC;EACd,aAAWH;EACX,kBAAgBI,KAAc,KAAA;EAC9B,gBAAcF,KAAa,KAAA;EAC3B,WAAW,CAAC,aAAaG,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAC5D,GAAIP;EATN,UAAA;GAWGI,IACC,kBAACP,GAAD;IAAM,MAAK;IAAU,MAAMI;IAAO,WAAU;GAAoC,CAAA,IAEhFO,KAAa,kBAACX,GAAD;IAAM,MAAMW;IAAW,MAAMP;GAAQ,CAAA;IAElD,CAACK,KAAe,CAACE,KAAa,CAACJ,MAAeK;GAC/CC,KAAW,CAACJ,KAAc,kBAACT,GAAD;IAAM,MAAMa;IAAS,MAAMT;GAAQ,CAAA;EACxD;;AAEZ,CAAC"}
@@ -126,20 +126,4 @@
126
126
  .ds-button[data-pending] {
127
127
  cursor: progress;
128
128
  }
129
-
130
- .ds-button__spinner {
131
- animation: ds-spin 900ms linear infinite;
132
- }
133
-
134
- @keyframes ds-spin {
135
- to {
136
- transform: rotate(360deg);
137
- }
138
- }
139
-
140
- @media (prefers-reduced-motion: reduce) {
141
- .ds-button__spinner {
142
- animation-duration: 2400ms;
143
- }
144
- }
145
129
  }
@@ -7,4 +7,22 @@
7
7
  stroke-linecap: round;
8
8
  stroke-linejoin: round;
9
9
  }
10
+
11
+ /* Every pending spinner shares one motion. Reduced motion slows it rather
12
+ * than stopping it, so the wait still reads as progress. */
13
+ .ds-icon--spin {
14
+ animation: ds-spin 900ms linear infinite;
15
+ }
16
+
17
+ @keyframes ds-spin {
18
+ to {
19
+ transform: rotate(360deg);
20
+ }
21
+ }
22
+
23
+ @media (prefers-reduced-motion: reduce) {
24
+ .ds-icon--spin {
25
+ animation-duration: 2400ms;
26
+ }
27
+ }
10
28
  }
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../ButtonGroup/index.cjs"),n=require("../Field/index.cjs"),r=require("../Grid/index.cjs"),i=require("../Slider/index.cjs");let a=require("react"),o=require("react/jsx-runtime");var s=1,c={offsetX:0,offsetY:0,zoom:1,rotation:0},l=(0,a.forwardRef)(function({src:l,alt:u=``,aspect:d,maxZoom:f=3,rotatable:p=!0,onChange:m,className:h,...g},_){let v=(0,a.useRef)(null),[y,b]=(0,a.useState)({width:0,height:0}),[x,S]=(0,a.useState)({width:0,height:0}),[C,w]=(0,a.useState)(c),T=(0,a.useRef)(null),E=(0,a.useRef)(null);(0,a.useEffect)(()=>{w(e=>({...c,rotation:e.rotation}))},[d]);let D=e=>w(t=>({...c,rotation:(t.rotation+e+360)%360})),O=(0,a.useCallback)(e=>{e.naturalWidth&&(S({width:e.naturalWidth,height:e.naturalHeight}),w(c))},[]);(0,a.useEffect)(()=>{let e=E.current;e?.complete&&O(e)},[O,l]);let k=C.rotation%180!=0,A={width:k?x.height:x.width,height:k?x.width:x.height},j=A.width&&y.width?Math.max(y.width/A.width,y.height/A.height):0;(0,a.useLayoutEffect)(()=>{let e=v.current;if(!e)return;let t=()=>b({width:e.clientWidth,height:e.clientHeight});t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[d]);let M=(0,a.useCallback)(e=>{let t=Math.min(Math.max(s,f),Math.max(s,e.zoom)),n=j*t,r=n?Math.max(0,(A.width*n-y.width)/2):0,i=n?Math.max(0,(A.height*n-y.height)/2):0,a=Math.min(r,Math.max(-r,e.offsetX)),o=Math.min(i,Math.max(-i,e.offsetY));return t===e.zoom&&a===e.offsetX&&o===e.offsetY?e:{...e,zoom:t,offsetX:a,offsetY:o}},[j,f,A.height,A.width,y.height,y.width]),N=(0,a.useMemo)(()=>M(C),[M,C]);(0,a.useEffect)(()=>{w(N)},[N]);let P=j*N.zoom,F=P?Math.max(0,(A.width*P-y.width)/2):0,I=P?Math.max(0,(A.height*P-y.height)/2):0,L=`Drag the picture to reposition it. Nothing is written until you save — ${F===0&&I===0?`at this zoom the whole picture is in frame.`:`the veiled edges are what gets trimmed.`}`;(0,a.useEffect)(()=>{if(!P||!y.width)return;let e=Math.min(A.width,Math.round(y.width/P)),t=Math.min(A.height,Math.round(y.height/P));m?.({x:Math.max(0,Math.min(A.width-e,Math.round(A.width/2-N.offsetX/P-y.width/P/2))),y:Math.max(0,Math.min(A.height-t,Math.round(A.height/2-N.offsetY/P-y.height/P/2))),width:e,height:t},N)},[P,y.width,y.height,A.width,A.height,N]);let R=e=>{e.button===0&&(T.current={pointerId:e.pointerId,x:e.clientX,y:e.clientY},e.currentTarget.setPointerCapture(e.pointerId))},z=e=>{let t=T.current;if(!t||t.pointerId!==e.pointerId)return;let n=e.clientX-t.x,r=e.clientY-t.y;T.current={...t,x:e.clientX,y:e.clientY},w(e=>M({...e,offsetX:e.offsetX+n,offsetY:e.offsetY+r}))},B=e=>{T.current?.pointerId===e.pointerId&&(T.current=null,e.currentTarget.releasePointerCapture?.(e.pointerId))},V=(e,t)=>w(n=>M({...n,offsetX:n.offsetX+e,offsetY:n.offsetY+t}));return(0,o.jsxs)(r.Stack,{ref:_,className:[`ds-cropper`,h].filter(Boolean).join(` `),...g,children:[(0,o.jsxs)(`div`,{role:`application`,"aria-label":`Crop area — drag to reposition, arrow keys to nudge`,tabIndex:0,className:`ds-cropper__stage`,style:{"--cropper-aspect":String(d)},onPointerDown:R,onPointerMove:z,onPointerUp:B,onPointerCancel:B,onKeyDown:e=>{let t=e.shiftKey?24:8,n={ArrowLeft:[-t,0],ArrowRight:[t,0],ArrowUp:[0,-t],ArrowDown:[0,t]}[e.key];n&&(e.preventDefault(),V(n[0],n[1]))},children:[(0,o.jsx)(`img`,{ref:E,src:l,alt:u,draggable:!1,className:`ds-cropper__image`,onLoad:e=>O(e.currentTarget),style:{inlineSize:x.width*P||void 0,blockSize:x.height*P||void 0,transform:`translate(calc(-50% + ${N.offsetX}px), calc(-50% + ${N.offsetY}px)) rotate(${N.rotation}deg)`}}),(0,o.jsx)(`div`,{className:`ds-cropper__frame`,ref:v,"aria-hidden":`true`,children:(0,o.jsx)(`div`,{className:`ds-cropper__guides`})})]}),(0,o.jsx)(n.Field,{label:`Zoom`,hint:L,children:n=>(0,o.jsxs)(r.Stack,{direction:`horizontal`,gap:`md`,align:`center`,wrap:!0,children:[(0,o.jsx)(r.Stack,{style:{flex:`1 1 50%`,minInlineSize:0},children:(0,o.jsx)(i.Slider,{...n,min:s,max:Math.max(s,f),step:.01,value:N.zoom,suffix:`×`,onValueChange:e=>w(t=>M({...t,zoom:e}))})}),(0,o.jsxs)(t.ButtonGroup,{"aria-label":`Crop actions`,children:[p&&(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(e.Button,{size:`sm`,iconStart:`rotate-ccw`,"aria-label":`Rotate left`,onClick:()=>D(-90)}),(0,o.jsx)(e.Button,{size:`sm`,iconStart:`rotate-cw`,"aria-label":`Rotate right`,onClick:()=>D(90)})]}),(0,o.jsx)(e.Button,{size:`sm`,onClick:()=>w(c),children:`Reset`})]})]})})]})});async function u(e,t,{outputWidth:n,rotation:r=0,type:i=`image/png`,quality:a=.92}={}){let o=new Image;o.crossOrigin=`anonymous`,o.src=e,await o.decode();let s=r%180!=0,c=document.createElement(`canvas`);c.width=s?o.naturalHeight:o.naturalWidth,c.height=s?o.naturalWidth:o.naturalHeight;let l=c.getContext(`2d`);if(!l)throw Error(`Canvas 2D context unavailable`);l.translate(c.width/2,c.height/2),l.rotate(r*Math.PI/180),l.drawImage(o,-o.naturalWidth/2,-o.naturalHeight/2);let u=n??t.width,d=Math.round(t.height/t.width*u),f=document.createElement(`canvas`);f.width=u,f.height=d;let p=f.getContext(`2d`);if(!p)throw Error(`Canvas 2D context unavailable`);return p.drawImage(c,t.x,t.y,t.width,t.height,0,0,u,d),new Promise((e,t)=>{f.toBlob(n=>n?e(n):t(Error(`Could not encode the crop`)),i,a)})}exports.ImageCropper=l,exports.cropToBlob=u;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../ButtonGroup/index.cjs"),n=require("../Field/index.cjs"),r=require("../Grid/index.cjs"),i=require("../Slider/index.cjs");let a=require("react"),o=require("react/jsx-runtime");var s=1,c={zoom:1,rotation:0,x:.5,y:.5};function l(e,t,n){let r=[e,t];for(let e=Math.round(n/90)%4;e>0;e--)r=[1-r[1],r[0]];return r}function u(e,t,n,r){let i=n%180!=0,a=i?e.height:e.width,o=i?e.width:e.height;return{width:a,height:o,scale:Math.max(t.width/a,t.height/o)*r}}function d(e){let t=(e,t)=>e!=null&&Number.isFinite(e)?e:t,n=e=>Math.min(1,Math.max(0,t(e,.5))),r=(Math.round(t(e?.rotation,0)/90)*90%360+360)%360,[i,a]=l(n(e?.cx),n(e?.cy),r);return{zoom:t(e?.zoom,1),rotation:r,x:i,y:a}}var f=(0,a.forwardRef)(function({src:f,alt:p=``,aspect:m,maxZoom:h=3,rotatable:g=!0,onChange:_,defaultState:v,className:y,...b},x){let S=(0,a.useRef)(null),C=(0,a.useRef)(null),w=(0,a.useRef)(null),[T,E]=(0,a.useState)({width:0,height:0}),[D,O]=(0,a.useState)(null),[k,A]=(0,a.useState)(()=>d(v)),[j,M]=(0,a.useState)({src:f,aspect:m});(j.src!==f||j.aspect!==m)&&(M({src:f,aspect:m}),A(e=>j.src===f?{...c,rotation:e.rotation}:d(v)));let N=(0,a.useCallback)(e=>{let{naturalWidth:t,naturalHeight:n}=e;t&&n&&O(e=>e?.src===f&&e.width===t&&e.height===n?e:{src:f,width:t,height:n})},[f]);(0,a.useEffect)(()=>{let e=C.current;e?.complete&&N(e)},[N]),(0,a.useLayoutEffect)(()=>{let e=S.current;if(!e)return;let t=()=>{let t=e.clientWidth,n=e.clientHeight;E(e=>e.width===t&&e.height===n?e:{width:t,height:n})};t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[m]);let P=D?.src===f&&T.width>0&&T.height>0?D:null,F=(0,a.useCallback)(e=>{let t=Math.min(Math.max(s,h),Math.max(s,e.zoom)),{x:n,y:r}=e;if(P){let i=u(P,T,e.rotation,t),a=Math.min(.5,T.width/i.scale/i.width/2),o=Math.min(.5,T.height/i.scale/i.height/2);n=Math.min(1-a,Math.max(a,n)),r=Math.min(1-o,Math.max(o,r))}return t===e.zoom&&n===e.x&&r===e.y?e:{...e,zoom:t,x:n,y:r}},[T,h,P]),I=(0,a.useMemo)(()=>F(k),[F,k]);(0,a.useEffect)(()=>{A(I)},[I]);let L=(0,a.useMemo)(()=>P?u(P,T,I.rotation,I.zoom):null,[P,T,I.rotation,I.zoom]),R=L?.scale??0,z=L?(.5-I.x)*L.width*R:0,B=L?(.5-I.y)*L.height*R:0,V=L?Math.max(0,(L.width*R-T.width)/2):0,H=L?Math.max(0,(L.height*R-T.height)/2):0,U=`Drag the picture to reposition it. Nothing is written until you save — ${V===0&&H===0?`at this zoom the whole picture is in frame.`:`the veiled edges are what gets trimmed.`}`;(0,a.useEffect)(()=>{if(!L)return;let e=Math.min(L.width,Math.round(T.width/L.scale)),t=Math.min(L.height,Math.round(T.height/L.scale)),[n,r]=l(I.x,I.y,360-I.rotation);_?.({x:Math.max(0,Math.min(L.width-e,Math.round(I.x*L.width-T.width/L.scale/2))),y:Math.max(0,Math.min(L.height-t,Math.round(I.y*L.height-T.height/L.scale/2))),width:e,height:t},{offsetX:z,offsetY:B,zoom:I.zoom,rotation:I.rotation,cx:n,cy:r})},[L,I,T,z,B]);let W=(e,t)=>A(n=>{if(!P)return n;let{width:r,height:i,scale:a}=u(P,T,n.rotation,n.zoom);return F({...n,x:n.x-e/(r*a),y:n.y-t/(i*a)})}),G=e=>A(t=>({...c,rotation:(t.rotation+e+360)%360})),K=e=>{e.button===0&&(w.current={pointerId:e.pointerId,x:e.clientX,y:e.clientY},e.currentTarget.setPointerCapture(e.pointerId))},q=e=>{let t=w.current;if(!t||t.pointerId!==e.pointerId)return;let n=e.clientX-t.x,r=e.clientY-t.y;w.current={...t,x:e.clientX,y:e.clientY},W(n,r)},J=e=>{w.current?.pointerId===e.pointerId&&(w.current=null,e.currentTarget.releasePointerCapture?.(e.pointerId))};return(0,o.jsxs)(r.Stack,{ref:x,className:[`ds-cropper`,y].filter(Boolean).join(` `),...b,children:[(0,o.jsxs)(`div`,{role:`application`,"aria-label":`Crop area — drag to reposition, arrow keys to nudge`,tabIndex:0,className:`ds-cropper__stage`,style:{"--cropper-aspect":String(m)},onPointerDown:K,onPointerMove:q,onPointerUp:J,onPointerCancel:J,onKeyDown:e=>{let t=e.shiftKey?24:8,n={ArrowLeft:[-t,0],ArrowRight:[t,0],ArrowUp:[0,-t],ArrowDown:[0,t]}[e.key];n&&(e.preventDefault(),W(n[0],n[1]))},children:[(0,o.jsx)(`img`,{ref:C,src:f,alt:p,draggable:!1,className:`ds-cropper__image`,onLoad:e=>N(e.currentTarget),style:{inlineSize:P?P.width*R:void 0,blockSize:P?P.height*R:void 0,transform:`translate(calc(-50% + ${z}px), calc(-50% + ${B}px)) rotate(${I.rotation}deg)`}}),(0,o.jsx)(`div`,{className:`ds-cropper__frame`,ref:S,"aria-hidden":`true`,children:(0,o.jsx)(`div`,{className:`ds-cropper__guides`})})]}),(0,o.jsx)(n.Field,{label:`Zoom`,hint:U,children:n=>(0,o.jsxs)(r.Stack,{direction:`horizontal`,gap:`md`,align:`center`,wrap:!0,children:[(0,o.jsx)(r.Stack,{style:{flex:`1 1 50%`,minInlineSize:0},children:(0,o.jsx)(i.Slider,{...n,min:s,max:Math.max(s,h),step:.01,value:I.zoom,suffix:`×`,onValueChange:e=>A(t=>F({...t,zoom:e}))})}),(0,o.jsxs)(t.ButtonGroup,{"aria-label":`Crop actions`,children:[g&&(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(e.Button,{size:`sm`,iconStart:`rotate-ccw`,"aria-label":`Rotate left`,onClick:()=>G(-90)}),(0,o.jsx)(e.Button,{size:`sm`,iconStart:`rotate-cw`,"aria-label":`Rotate right`,onClick:()=>G(90)})]}),(0,o.jsx)(e.Button,{size:`sm`,onClick:()=>A(c),children:`Reset`})]})]})})]})});async function p(e,t,{outputWidth:n,rotation:r=0,type:i=`image/png`,quality:a=.92}={}){let o=new Image;o.crossOrigin=`anonymous`,o.src=e,await o.decode();let s=r%180!=0,c=document.createElement(`canvas`);c.width=s?o.naturalHeight:o.naturalWidth,c.height=s?o.naturalWidth:o.naturalHeight;let l=c.getContext(`2d`);if(!l)throw Error(`Canvas 2D context unavailable`);l.translate(c.width/2,c.height/2),l.rotate(r*Math.PI/180),l.drawImage(o,-o.naturalWidth/2,-o.naturalHeight/2);let u=n??t.width,d=Math.round(t.height/t.width*u),f=document.createElement(`canvas`);f.width=u,f.height=d;let p=f.getContext(`2d`);if(!p)throw Error(`Canvas 2D context unavailable`);return p.drawImage(c,t.x,t.y,t.width,t.height,0,0,u,d),new Promise((e,t)=>{f.toBlob(n=>n?e(n):t(Error(`Could not encode the crop`)),i,a)})}exports.ImageCropper=f,exports.cropToBlob=p;
2
2
  //# sourceMappingURL=index.cjs.map