@sixthshift/design-system 0.7.0 → 0.8.0
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/dist/components/DataTable/DataTable.d.ts +89 -0
- package/dist/components/DataTable/DataTable.d.ts.map +1 -0
- package/dist/components/DataTable/DataTable.js +107 -0
- package/dist/components/DataTable/DataTable.js.map +1 -0
- package/dist/components/DataTable/index.d.ts +2 -0
- package/dist/components/DataTable/index.d.ts.map +1 -0
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/DataTable/index.js.map +1 -0
- package/dist/components/FileDropzone/FileDropzone.d.ts +87 -0
- package/dist/components/FileDropzone/FileDropzone.d.ts.map +1 -0
- package/dist/components/FileDropzone/FileDropzone.js +146 -0
- package/dist/components/FileDropzone/FileDropzone.js.map +1 -0
- package/dist/components/FileDropzone/index.d.ts +2 -0
- package/dist/components/FileDropzone/index.d.ts.map +1 -0
- package/dist/components/FileDropzone/index.js +2 -0
- package/dist/components/FileDropzone/index.js.map +1 -0
- package/dist/components/ImageGallery/ImageGallery.d.ts +93 -0
- package/dist/components/ImageGallery/ImageGallery.d.ts.map +1 -0
- package/dist/components/ImageGallery/ImageGallery.js +121 -0
- package/dist/components/ImageGallery/ImageGallery.js.map +1 -0
- package/dist/components/ImageGallery/index.d.ts +2 -0
- package/dist/components/ImageGallery/index.d.ts.map +1 -0
- package/dist/components/ImageGallery/index.js +2 -0
- package/dist/components/ImageGallery/index.js.map +1 -0
- package/dist/components/NumberStepper/NumberStepper.d.ts +78 -0
- package/dist/components/NumberStepper/NumberStepper.d.ts.map +1 -0
- package/dist/components/NumberStepper/NumberStepper.js +129 -0
- package/dist/components/NumberStepper/NumberStepper.js.map +1 -0
- package/dist/components/NumberStepper/index.d.ts +2 -0
- package/dist/components/NumberStepper/index.d.ts.map +1 -0
- package/dist/components/NumberStepper/index.js +2 -0
- package/dist/components/NumberStepper/index.js.map +1 -0
- package/dist/components/Steps/Steps.d.ts +65 -0
- package/dist/components/Steps/Steps.d.ts.map +1 -0
- package/dist/components/Steps/Steps.js +62 -0
- package/dist/components/Steps/Steps.js.map +1 -0
- package/dist/components/Steps/index.d.ts +2 -0
- package/dist/components/Steps/index.d.ts.map +1 -0
- package/dist/components/Steps/index.js +2 -0
- package/dist/components/Steps/index.js.map +1 -0
- package/package.json +21 -1
- package/src/components/DataTable/DataTable.tsx +261 -0
- package/src/components/DataTable/data-table.recipe.css +28 -0
- package/src/components/DataTable/index.ts +10 -0
- package/src/components/FileDropzone/FileDropzone.tsx +239 -0
- package/src/components/FileDropzone/file-dropzone.recipe.css +38 -0
- package/src/components/FileDropzone/index.ts +1 -0
- package/src/components/ImageGallery/ImageGallery.tsx +285 -0
- package/src/components/ImageGallery/image-gallery.recipe.css +50 -0
- package/src/components/ImageGallery/index.ts +7 -0
- package/src/components/NumberStepper/NumberStepper.tsx +227 -0
- package/src/components/NumberStepper/index.ts +1 -0
- package/src/components/NumberStepper/number-stepper.recipe.css +31 -0
- package/src/components/Steps/Steps.tsx +149 -0
- package/src/components/Steps/index.ts +1 -0
- package/src/components/Steps/steps.recipe.css +51 -0
- package/src/theming/tailwind.css +5 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { type HTMLAttributes, type ReactElement, type ReactNode, type Ref } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Geometry only. Every colour reads a `--data-table-*` component token whose
|
|
5
|
+
* value is decided by src/components/DataTable/data-table.recipe.css.
|
|
6
|
+
*
|
|
7
|
+
* `size` is cell density: `sm` for back-office lists scanned dozens of rows at
|
|
8
|
+
* a time, `md` for tables that are the page's main content.
|
|
9
|
+
*/
|
|
10
|
+
declare const cellVariants: (props?: ({
|
|
11
|
+
size?: "sm" | "md" | null | undefined;
|
|
12
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
13
|
+
export type DataTableSize = NonNullable<VariantProps<typeof cellVariants>["size"]>;
|
|
14
|
+
export type DataTableAlign = "start" | "center" | "end";
|
|
15
|
+
export type DataTableColumn<Row> = {
|
|
16
|
+
/** Stable key — also what `sort.columnId` refers to. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Header content. */
|
|
19
|
+
header: ReactNode;
|
|
20
|
+
/** Cell content for one row. */
|
|
21
|
+
cell: (row: Row) => ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Makes the column sortable. The returned value is what rows are compared
|
|
24
|
+
* by — return cents, not a formatted price, and an ISO date, not "Tue 3rd".
|
|
25
|
+
*/
|
|
26
|
+
sortValue?: (row: Row) => string | number;
|
|
27
|
+
/** Text alignment for header and cells. Numbers read best at `end`. */
|
|
28
|
+
align?: DataTableAlign;
|
|
29
|
+
/** A CSS width for the column, e.g. `"8rem"` or `"20%"`. */
|
|
30
|
+
width?: string;
|
|
31
|
+
/** Extra classes for this column's `<td>`s. */
|
|
32
|
+
className?: string;
|
|
33
|
+
};
|
|
34
|
+
export type DataTableSortDirection = "asc" | "desc";
|
|
35
|
+
export type DataTableSort = {
|
|
36
|
+
columnId: string;
|
|
37
|
+
direction: DataTableSortDirection;
|
|
38
|
+
};
|
|
39
|
+
export type DataTableProps<Row> = Omit<HTMLAttributes<HTMLDivElement>, "children"> & VariantProps<typeof cellVariants> & {
|
|
40
|
+
columns: readonly DataTableColumn<Row>[];
|
|
41
|
+
rows: readonly Row[];
|
|
42
|
+
/** Stable id per row, used as the React key. */
|
|
43
|
+
getRowId: (row: Row) => string;
|
|
44
|
+
/** Controlled sort. `null` is unsorted — rows in the order given. */
|
|
45
|
+
sort?: DataTableSort | null;
|
|
46
|
+
/** Initial sort in uncontrolled mode. */
|
|
47
|
+
defaultSort?: DataTableSort | null;
|
|
48
|
+
/** Called when a header is clicked: ascending, then descending, then unsorted. */
|
|
49
|
+
onSortChange?: (sort: DataTableSort | null) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Report sort changes without reordering `rows`, for data sorted where it
|
|
52
|
+
* is fetched. The headers still show the sort state.
|
|
53
|
+
*/
|
|
54
|
+
manualSorting?: boolean;
|
|
55
|
+
/** Makes every row activatable by pointer, `Enter` or `Space`. */
|
|
56
|
+
onRowClick?: (row: Row) => void;
|
|
57
|
+
/** Shown in place of the body when `rows` is empty. */
|
|
58
|
+
empty?: ReactNode;
|
|
59
|
+
/** Names the table for assistive tech. Visually hidden unless `showCaption`. */
|
|
60
|
+
caption?: ReactNode;
|
|
61
|
+
showCaption?: boolean;
|
|
62
|
+
/** Keep the header row visible while the wrapper scrolls vertically. Give the wrapper a height via `className`. */
|
|
63
|
+
stickyHeader?: boolean;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A data table for back-office lists — orders, products, customers.
|
|
67
|
+
*
|
|
68
|
+
* Props-based, per docs/component-api-design.md: rows are uniform, so they
|
|
69
|
+
* arrive as a `rows` array and a `columns` config, and each column's `cell`
|
|
70
|
+
* renders one row's content. Anything interactive in a cell — a status
|
|
71
|
+
* Badge, an action Button — is just what `cell` returns.
|
|
72
|
+
*
|
|
73
|
+
* A column with `sortValue` gets a header button cycling ascending →
|
|
74
|
+
* descending → unsorted, and `aria-sort` on its `<th>`. Sorting is
|
|
75
|
+
* client-side and stable; pass `manualSorting` to only report the change when
|
|
76
|
+
* the data is sorted where it is fetched. Sort state is controlled or
|
|
77
|
+
* uncontrolled through `sort`/`defaultSort`/`onSortChange`.
|
|
78
|
+
*
|
|
79
|
+
* `onRowClick` makes every row a tab stop activated by pointer, `Enter` or
|
|
80
|
+
* `Space` — keys pressed on a control inside the row are left to that control.
|
|
81
|
+
*
|
|
82
|
+
* The wrapper scrolls, so a wide table on a phone scrolls sideways inside its
|
|
83
|
+
* border rather than widening the page.
|
|
84
|
+
*/
|
|
85
|
+
export declare const DataTable: <Row>(props: DataTableProps<Row> & {
|
|
86
|
+
ref?: Ref<HTMLDivElement>;
|
|
87
|
+
}) => ReactElement;
|
|
88
|
+
export { cellVariants as dataTableCellVariants };
|
|
89
|
+
//# sourceMappingURL=DataTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/DataTable.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAc,KAAK,cAAc,EAAsB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAW,MAAM,OAAO,CAAC;AAElI;;;;;;GAMG;AACH,QAAA,MAAM,YAAY;;8EAUhB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnF,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD,MAAM,MAAM,eAAe,CAAC,GAAG,IAAI;IACjC,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,MAAM,EAAE,SAAS,CAAC;IAClB,gCAAgC;IAChC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,SAAS,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,CAAC;IAC1C,uEAAuE;IACvE,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,sBAAsB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAChF,YAAY,CAAC,OAAO,YAAY,CAAC,GAAG;IAClC,OAAO,EAAE,SAAS,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,SAAS,GAAG,EAAE,CAAC;IACrB,gDAAgD;IAChD,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IAC/B,qEAAqE;IACrE,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,yCAAyC;IACzC,WAAW,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC,kFAAkF;IAClF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAChC,uDAAuD;IACvD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mHAAmH;IACnH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AA0JJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,SAAS,EAAoB,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAAK,YAAY,CAAC;AAE5H,OAAO,EAAE,YAAY,IAAI,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
4
|
+
import { cn } from "@sixthshift/design-system/utils";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
|
|
7
|
+
import { forwardRef, useMemo } from "react";
|
|
8
|
+
/**
|
|
9
|
+
* Geometry only. Every colour reads a `--data-table-*` component token whose
|
|
10
|
+
* value is decided by src/components/DataTable/data-table.recipe.css.
|
|
11
|
+
*
|
|
12
|
+
* `size` is cell density: `sm` for back-office lists scanned dozens of rows at
|
|
13
|
+
* a time, `md` for tables that are the page's main content.
|
|
14
|
+
*/
|
|
15
|
+
const cellVariants = cva("px-3 align-middle", {
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: "h-9 py-1.5 text-xs",
|
|
19
|
+
md: "h-11 py-2 text-sm",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
size: "md",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const alignClass = {
|
|
27
|
+
start: "text-start",
|
|
28
|
+
center: "text-center",
|
|
29
|
+
end: "text-end",
|
|
30
|
+
};
|
|
31
|
+
const ariaSort = { asc: "ascending", desc: "descending" };
|
|
32
|
+
function nextSort(current, columnId) {
|
|
33
|
+
if (current?.columnId !== columnId)
|
|
34
|
+
return { columnId, direction: "asc" };
|
|
35
|
+
return current.direction === "asc" ? { columnId, direction: "desc" } : null;
|
|
36
|
+
}
|
|
37
|
+
function compare(a, b) {
|
|
38
|
+
if (typeof a === "number" && typeof b === "number")
|
|
39
|
+
return a - b;
|
|
40
|
+
return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: "base" });
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `forwardRef` erases the generic, so the implementation takes the ref as an
|
|
44
|
+
* argument and the export below restores `<Row>` — the same arrangement
|
|
45
|
+
* Select.tsx uses for `<T>`.
|
|
46
|
+
*/
|
|
47
|
+
const DataTableRoot = forwardRef(function DataTableRoot({ className, columns, rows, getRowId, size = "md", sort: controlledSort, defaultSort = null, onSortChange, manualSorting = false, onRowClick, empty, caption, showCaption = false, stickyHeader = false, ...props }, ref) {
|
|
48
|
+
const [sort, setSort] = useControllableState({
|
|
49
|
+
value: controlledSort,
|
|
50
|
+
defaultValue: defaultSort,
|
|
51
|
+
onChange: onSortChange,
|
|
52
|
+
});
|
|
53
|
+
const sortedRows = useMemo(() => {
|
|
54
|
+
if (manualSorting || sort === null)
|
|
55
|
+
return rows;
|
|
56
|
+
const sortValue = columns.find((column) => column.id === sort.columnId)?.sortValue;
|
|
57
|
+
if (!sortValue)
|
|
58
|
+
return rows;
|
|
59
|
+
const factor = sort.direction === "asc" ? 1 : -1;
|
|
60
|
+
// Array.prototype.sort is stable, so equal keys keep the caller's order.
|
|
61
|
+
return [...rows].sort((a, b) => factor * compare(sortValue(a), sortValue(b)));
|
|
62
|
+
}, [rows, columns, sort, manualSorting]);
|
|
63
|
+
const interactive = onRowClick !== undefined;
|
|
64
|
+
const handleRowKeyDown = (event, row) => {
|
|
65
|
+
if (event.target !== event.currentTarget)
|
|
66
|
+
return; // a button inside the row keeps its own keys
|
|
67
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
onRowClick?.(row);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return (_jsx("div", { ref: ref, className: cn("data-table border-(color:--data-table-border) w-full overflow-auto rounded-lg border bg-(--data-table-bg) text-(--data-table-fg)", className), ...props, children: _jsxs("table", { className: "w-full border-collapse", children: [caption !== undefined && _jsx("caption", { className: cn(showCaption ? "px-3 py-2 text-start font-medium text-sm" : "sr-only"), children: caption }), _jsx("thead", { className: cn("bg-(--data-table-header-bg)", stickyHeader && "sticky top-0 z-10"), children: _jsx("tr", { className: "border-(color:--data-table-border) border-b", children: columns.map((column) => {
|
|
73
|
+
const align = column.align ?? "start";
|
|
74
|
+
const sorted = sort?.columnId === column.id ? sort.direction : undefined;
|
|
75
|
+
const Icon = sorted === "asc" ? ArrowUp : sorted === "desc" ? ArrowDown : ChevronsUpDown;
|
|
76
|
+
return (_jsx("th", { scope: "col", "aria-sort": column.sortValue ? (sorted ? ariaSort[sorted] : "none") : undefined, style: column.width ? { width: column.width } : undefined, className: cn(cellVariants({ size }), alignClass[align], "whitespace-nowrap font-medium text-(--data-table-header-fg)"), children: column.sortValue ? (_jsxs("button", { type: "button", onClick: () => setSort(nextSort(sort, column.id)), className: cn("focus-visible:ring-(color:--data-table-ring) -mx-1 inline-flex cursor-pointer items-center gap-1 rounded-sm px-1 hover:text-(--data-table-fg) focus-visible:outline-hidden focus-visible:ring-2", align === "end" && "flex-row-reverse"), children: [column.header, _jsx(Icon, { "aria-hidden": "true", className: cn("size-3.5 shrink-0", sorted === undefined && "opacity-50") })] })) : (column.header) }, column.id));
|
|
77
|
+
}) }) }), _jsx("tbody", { children: sortedRows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: columns.length, className: "px-3 py-10 text-center text-(--data-table-empty-fg) text-sm", children: empty ?? "Nothing to show" }) })) : (sortedRows.map((row) => (_jsx("tr", { "data-interactive": interactive || undefined, onClick: interactive ? () => onRowClick(row) : undefined, onKeyDown: interactive ? (event) => handleRowKeyDown(event, row) : undefined,
|
|
78
|
+
// A row is not a native control, but a clickable table row is the
|
|
79
|
+
// established back-office pattern. It stays a `row` so the table
|
|
80
|
+
// semantics survive, and gains a tab stop and Enter/Space like
|
|
81
|
+
// Card does when it is clickable.
|
|
82
|
+
tabIndex: interactive ? 0 : undefined, className: cn("border-(color:--data-table-border) border-b last:border-b-0", interactive &&
|
|
83
|
+
"focus-visible:outline-(color:--data-table-ring) cursor-pointer hover:bg-(--data-table-row-bg-hovered) focus-visible:outline-2 focus-visible:-outline-offset-2"), children: columns.map((column) => (_jsx("td", { className: cn(cellVariants({ size }), alignClass[column.align ?? "start"], column.className), children: column.cell(row) }, column.id))) }, getRowId(row))))) })] }) }));
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* A data table for back-office lists — orders, products, customers.
|
|
87
|
+
*
|
|
88
|
+
* Props-based, per docs/component-api-design.md: rows are uniform, so they
|
|
89
|
+
* arrive as a `rows` array and a `columns` config, and each column's `cell`
|
|
90
|
+
* renders one row's content. Anything interactive in a cell — a status
|
|
91
|
+
* Badge, an action Button — is just what `cell` returns.
|
|
92
|
+
*
|
|
93
|
+
* A column with `sortValue` gets a header button cycling ascending →
|
|
94
|
+
* descending → unsorted, and `aria-sort` on its `<th>`. Sorting is
|
|
95
|
+
* client-side and stable; pass `manualSorting` to only report the change when
|
|
96
|
+
* the data is sorted where it is fetched. Sort state is controlled or
|
|
97
|
+
* uncontrolled through `sort`/`defaultSort`/`onSortChange`.
|
|
98
|
+
*
|
|
99
|
+
* `onRowClick` makes every row a tab stop activated by pointer, `Enter` or
|
|
100
|
+
* `Space` — keys pressed on a control inside the row are left to that control.
|
|
101
|
+
*
|
|
102
|
+
* The wrapper scrolls, so a wide table on a phone scrolls sideways inside its
|
|
103
|
+
* border rather than widening the page.
|
|
104
|
+
*/
|
|
105
|
+
export const DataTable = DataTableRoot;
|
|
106
|
+
export { cellVariants as dataTableCellVariants };
|
|
107
|
+
//# sourceMappingURL=DataTable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTable.js","sourceRoot":"","sources":["../../../src/components/DataTable/DataTable.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/DataTable/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAOT,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Geometry only. Every colour reads a `--file-dropzone-*` component token
|
|
5
|
+
* whose value is decided by src/components/FileDropzone/file-dropzone.recipe.css.
|
|
6
|
+
*
|
|
7
|
+
* `size` is the target's footprint: `sm` sits inline in a form row beside other
|
|
8
|
+
* fields, `md` is a panel of its own (an image manager's "add photos" tile).
|
|
9
|
+
*/
|
|
10
|
+
declare const fileDropzoneVariants: (props?: ({
|
|
11
|
+
size?: "sm" | "md" | null | undefined;
|
|
12
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
13
|
+
export type FileDropzoneSize = NonNullable<VariantProps<typeof fileDropzoneVariants>["size"]>;
|
|
14
|
+
export type FileDropzoneRejection = {
|
|
15
|
+
file: File;
|
|
16
|
+
/** `type` — not in `accept`; `size` — over `maxSize`. */
|
|
17
|
+
reason: "type" | "size";
|
|
18
|
+
};
|
|
19
|
+
export type FileDropzoneProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onDrop" | "children"> & VariantProps<typeof fileDropzoneVariants> & {
|
|
20
|
+
/** Called with the accepted files from a drop or the file picker. Never called with an empty list. */
|
|
21
|
+
onFilesChange: (files: File[]) => void;
|
|
22
|
+
/** Called with files that failed `accept` or `maxSize`, when any did. */
|
|
23
|
+
onFilesRejected?: (rejections: FileDropzoneRejection[]) => void;
|
|
24
|
+
/** Same syntax as the native `accept` attribute: `"image/*"`, `".pdf,.docx"`. Also checked on drop. */
|
|
25
|
+
accept?: string;
|
|
26
|
+
/** Allow more than one file per drop or pick. */
|
|
27
|
+
multiple?: boolean;
|
|
28
|
+
/** Largest accepted file, in bytes. */
|
|
29
|
+
maxSize?: number;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** Shows a busy state (e.g. while uploading) and blocks new files without disabling the control. */
|
|
32
|
+
pending?: boolean;
|
|
33
|
+
/** The main line. Default: "Drop files here or click to browse". */
|
|
34
|
+
label?: React.ReactNode;
|
|
35
|
+
/** A secondary line, e.g. accepted types and size limit. */
|
|
36
|
+
hint?: React.ReactNode;
|
|
37
|
+
/** Replaces the default upload icon. Pass `null` for none. */
|
|
38
|
+
icon?: React.ReactNode;
|
|
39
|
+
/** Input name, for native form submission of the picked files. */
|
|
40
|
+
name?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* A drop target for files that doubles as a file-picker button — product
|
|
44
|
+
* photos, attachments, imports.
|
|
45
|
+
*
|
|
46
|
+
* The whole surface is one control: `role="button"`, a tab stop, and `Enter`
|
|
47
|
+
* or `Space` opens the native picker, the same as a click. Dragging files over
|
|
48
|
+
* it sets `data-state="active"`, which the recipe styles. The hidden `<input
|
|
49
|
+
* type="file">` is the real picker, so `accept`, `multiple` and `name` behave
|
|
50
|
+
* natively; `accept` and `maxSize` are also enforced on drop, where the
|
|
51
|
+
* browser does not, and failures go to `onFilesRejected` rather than being
|
|
52
|
+
* silently dropped. Without `multiple`, a drop of several files keeps the first.
|
|
53
|
+
*
|
|
54
|
+
* `pending` shows a spinner-free busy state (`aria-busy`) and ignores new
|
|
55
|
+
* files while an upload runs, without leaving the tab order — the same split
|
|
56
|
+
* Switch makes between `pending` and `disabled`.
|
|
57
|
+
*
|
|
58
|
+
* The component holds no file list of its own: it reports files and the
|
|
59
|
+
* caller decides what to show, so there is no value/defaultValue triad.
|
|
60
|
+
*/
|
|
61
|
+
export declare const FileDropzone: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "onDrop"> & VariantProps<(props?: ({
|
|
62
|
+
size?: "sm" | "md" | null | undefined;
|
|
63
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
64
|
+
/** Called with the accepted files from a drop or the file picker. Never called with an empty list. */
|
|
65
|
+
onFilesChange: (files: File[]) => void;
|
|
66
|
+
/** Called with files that failed `accept` or `maxSize`, when any did. */
|
|
67
|
+
onFilesRejected?: (rejections: FileDropzoneRejection[]) => void;
|
|
68
|
+
/** Same syntax as the native `accept` attribute: `"image/*"`, `".pdf,.docx"`. Also checked on drop. */
|
|
69
|
+
accept?: string;
|
|
70
|
+
/** Allow more than one file per drop or pick. */
|
|
71
|
+
multiple?: boolean;
|
|
72
|
+
/** Largest accepted file, in bytes. */
|
|
73
|
+
maxSize?: number;
|
|
74
|
+
disabled?: boolean;
|
|
75
|
+
/** Shows a busy state (e.g. while uploading) and blocks new files without disabling the control. */
|
|
76
|
+
pending?: boolean;
|
|
77
|
+
/** The main line. Default: "Drop files here or click to browse". */
|
|
78
|
+
label?: React.ReactNode;
|
|
79
|
+
/** A secondary line, e.g. accepted types and size limit. */
|
|
80
|
+
hint?: React.ReactNode;
|
|
81
|
+
/** Replaces the default upload icon. Pass `null` for none. */
|
|
82
|
+
icon?: React.ReactNode;
|
|
83
|
+
/** Input name, for native form submission of the picked files. */
|
|
84
|
+
name?: string;
|
|
85
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
86
|
+
export { fileDropzoneVariants };
|
|
87
|
+
//# sourceMappingURL=FileDropzone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileDropzone.d.ts","sourceRoot":"","sources":["../../../src/components/FileDropzone/FileDropzone.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,QAAA,MAAM,oBAAoB;;8EAczB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAE9F,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,yDAAyD;IACzD,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC,GAC/F,YAAY,CAAC,OAAO,oBAAoB,CAAC,GAAG;IAC1C,sGAAsG;IACtG,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,yEAAyE;IACzE,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,qBAAqB,EAAE,KAAK,IAAI,CAAC;IAChE,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oGAAoG;IACpG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAkBJ;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY;;;IA1DrB,sGAAsG;mBACvF,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI;IACtC,yEAAyE;sBACvD,CAAC,UAAU,EAAE,qBAAqB,EAAE,KAAK,IAAI;IAC/D,uGAAuG;aAC9F,MAAM;IACf,iDAAiD;eACtC,OAAO;IAClB,uCAAuC;cAC7B,MAAM;eACL,OAAO;IAClB,oGAAoG;cAC1F,OAAO;IACjB,oEAAoE;YAC5D,KAAK,CAAC,SAAS;IACvB,4DAA4D;WACrD,KAAK,CAAC,SAAS;IACtB,8DAA8D;WACvD,KAAK,CAAC,SAAS;IACtB,kEAAkE;WAC3D,MAAM;wCAgLhB,CAAC;AAGF,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { Upload } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
/**
|
|
7
|
+
* Geometry only. Every colour reads a `--file-dropzone-*` component token
|
|
8
|
+
* whose value is decided by src/components/FileDropzone/file-dropzone.recipe.css.
|
|
9
|
+
*
|
|
10
|
+
* `size` is the target's footprint: `sm` sits inline in a form row beside other
|
|
11
|
+
* fields, `md` is a panel of its own (an image manager's "add photos" tile).
|
|
12
|
+
*/
|
|
13
|
+
const fileDropzoneVariants = cva(
|
|
14
|
+
// One literal, deliberately — see Button.tsx for why a `+` concatenation is a trap.
|
|
15
|
+
"file-dropzone border-(color:--file-dropzone-border) focus-visible:ring-(color:--file-dropzone-ring) relative flex w-full cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed bg-(--file-dropzone-bg) text-center text-(--file-dropzone-fg) transition-colors hover:bg-(--file-dropzone-bg-hovered) focus-visible:outline-hidden focus-visible:ring-2 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50", {
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: "min-h-20 gap-1 px-4 py-3 text-xs [&_svg]:size-4",
|
|
19
|
+
md: "min-h-36 gap-2 px-6 py-6 text-sm [&_svg]:size-6",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
size: "md",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
function matchesAccept(file, accept) {
|
|
27
|
+
if (!accept)
|
|
28
|
+
return true;
|
|
29
|
+
const rules = accept
|
|
30
|
+
.split(",")
|
|
31
|
+
.map((rule) => rule.trim().toLowerCase())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
if (rules.length === 0)
|
|
34
|
+
return true;
|
|
35
|
+
const name = file.name.toLowerCase();
|
|
36
|
+
const type = file.type.toLowerCase();
|
|
37
|
+
return rules.some((rule) => {
|
|
38
|
+
if (rule.startsWith("."))
|
|
39
|
+
return name.endsWith(rule);
|
|
40
|
+
if (rule.endsWith("/*"))
|
|
41
|
+
return type.startsWith(rule.slice(0, -1));
|
|
42
|
+
return type === rule;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A drop target for files that doubles as a file-picker button — product
|
|
47
|
+
* photos, attachments, imports.
|
|
48
|
+
*
|
|
49
|
+
* The whole surface is one control: `role="button"`, a tab stop, and `Enter`
|
|
50
|
+
* or `Space` opens the native picker, the same as a click. Dragging files over
|
|
51
|
+
* it sets `data-state="active"`, which the recipe styles. The hidden `<input
|
|
52
|
+
* type="file">` is the real picker, so `accept`, `multiple` and `name` behave
|
|
53
|
+
* natively; `accept` and `maxSize` are also enforced on drop, where the
|
|
54
|
+
* browser does not, and failures go to `onFilesRejected` rather than being
|
|
55
|
+
* silently dropped. Without `multiple`, a drop of several files keeps the first.
|
|
56
|
+
*
|
|
57
|
+
* `pending` shows a spinner-free busy state (`aria-busy`) and ignores new
|
|
58
|
+
* files while an upload runs, without leaving the tab order — the same split
|
|
59
|
+
* Switch makes between `pending` and `disabled`.
|
|
60
|
+
*
|
|
61
|
+
* The component holds no file list of its own: it reports files and the
|
|
62
|
+
* caller decides what to show, so there is no value/defaultValue triad.
|
|
63
|
+
*/
|
|
64
|
+
export const FileDropzone = React.forwardRef(({ className, size = "md", onFilesChange, onFilesRejected, accept, multiple = false, maxSize, disabled = false, pending = false, label = "Drop files here or click to browse", hint, icon, name, onKeyDown, onClick, ...props }, ref) => {
|
|
65
|
+
const inputRef = React.useRef(null);
|
|
66
|
+
const [active, setActive] = React.useState(false);
|
|
67
|
+
// dragenter/dragleave fire for every child crossed; count them so the state does not flicker.
|
|
68
|
+
const depth = React.useRef(0);
|
|
69
|
+
const labelId = React.useId();
|
|
70
|
+
const hintId = React.useId();
|
|
71
|
+
const interactive = !disabled && !pending;
|
|
72
|
+
const handleFiles = (list) => {
|
|
73
|
+
if (!list || !interactive)
|
|
74
|
+
return;
|
|
75
|
+
const files = Array.from(list);
|
|
76
|
+
const accepted = [];
|
|
77
|
+
const rejected = [];
|
|
78
|
+
for (const file of files) {
|
|
79
|
+
if (!matchesAccept(file, accept))
|
|
80
|
+
rejected.push({ file, reason: "type" });
|
|
81
|
+
else if (maxSize !== undefined && file.size > maxSize)
|
|
82
|
+
rejected.push({ file, reason: "size" });
|
|
83
|
+
else
|
|
84
|
+
accepted.push(file);
|
|
85
|
+
}
|
|
86
|
+
const kept = multiple ? accepted : accepted.slice(0, 1);
|
|
87
|
+
if (rejected.length > 0)
|
|
88
|
+
onFilesRejected?.(rejected);
|
|
89
|
+
if (kept.length > 0)
|
|
90
|
+
onFilesChange(kept);
|
|
91
|
+
};
|
|
92
|
+
const openPicker = () => {
|
|
93
|
+
if (interactive)
|
|
94
|
+
inputRef.current?.click();
|
|
95
|
+
};
|
|
96
|
+
const handleKeyDown = (event) => {
|
|
97
|
+
onKeyDown?.(event);
|
|
98
|
+
if (event.defaultPrevented || event.target !== event.currentTarget)
|
|
99
|
+
return;
|
|
100
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
openPicker();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const handleDragEnter = (event) => {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
if (!interactive)
|
|
108
|
+
return;
|
|
109
|
+
depth.current += 1;
|
|
110
|
+
setActive(true);
|
|
111
|
+
};
|
|
112
|
+
const handleDragLeave = (event) => {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
depth.current = Math.max(0, depth.current - 1);
|
|
115
|
+
if (depth.current === 0)
|
|
116
|
+
setActive(false);
|
|
117
|
+
};
|
|
118
|
+
const handleDragOver = (event) => {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
event.dataTransfer.dropEffect = interactive ? "copy" : "none";
|
|
121
|
+
};
|
|
122
|
+
const handleDrop = (event) => {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
depth.current = 0;
|
|
125
|
+
setActive(false);
|
|
126
|
+
handleFiles(event.dataTransfer.files);
|
|
127
|
+
};
|
|
128
|
+
return (
|
|
129
|
+
// biome-ignore lint/a11y/useSemanticElements: the target holds the native file <input>, and interactive content is not allowed inside a <button>; role="button" with Enter/Space handling is the WAI-ARIA button pattern
|
|
130
|
+
_jsxs("div", { ref: ref, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, "aria-busy": pending || undefined, "aria-labelledby": labelId, "aria-describedby": hint ? hintId : undefined, "data-state": active ? "active" : "idle", "data-disabled": disabled, className: fileDropzoneVariants({ size, className }), onClick: (event) => {
|
|
131
|
+
onClick?.(event);
|
|
132
|
+
if (!event.defaultPrevented)
|
|
133
|
+
openPicker();
|
|
134
|
+
}, onKeyDown: handleKeyDown, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, ...props, children: [icon === undefined ? _jsx(Upload, { "aria-hidden": "true", className: "text-(--file-dropzone-icon-fg)" }) : icon, _jsx("span", { id: labelId, className: "font-medium", children: label }), hint && (_jsx("span", { id: hintId, className: "text-(--file-dropzone-hint-fg)", children: hint })), _jsx("input", { ref: inputRef, type: "file",
|
|
135
|
+
// `hidden`, not `sr-only`: a rendered input inside role="button" is a nested
|
|
136
|
+
// interactive control (axe `nested-interactive`). `click()` and form submission
|
|
137
|
+
// still work on a display:none file input.
|
|
138
|
+
hidden: true, accept: accept, multiple: multiple, name: name, disabled: disabled, onClick: (event) => event.stopPropagation(), onChange: (event) => {
|
|
139
|
+
handleFiles(event.target.files);
|
|
140
|
+
// Reset so picking the same file again still fires change.
|
|
141
|
+
event.target.value = "";
|
|
142
|
+
} })] }));
|
|
143
|
+
});
|
|
144
|
+
FileDropzone.displayName = "FileDropzone";
|
|
145
|
+
export { fileDropzoneVariants };
|
|
146
|
+
//# sourceMappingURL=FileDropzone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileDropzone.js","sourceRoot":"","sources":["../../../src/components/FileDropzone/FileDropzone.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileDropzone/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/FileDropzone/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6E,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Geometry only. Every colour reads an `--image-gallery-*` component token
|
|
5
|
+
* whose value is decided by src/components/ImageGallery/image-gallery.recipe.css.
|
|
6
|
+
*
|
|
7
|
+
* `ratio` is the frame's shape, not the images': every slide is cropped to it
|
|
8
|
+
* with `object-cover`, so a mixed set of uploads still produces a steady
|
|
9
|
+
* frame that does not jump as you swipe. `portrait` is 4:5, the tallest crop
|
|
10
|
+
* Instagram allows in a feed — photography shot for the feed lands on it
|
|
11
|
+
* uncropped.
|
|
12
|
+
*/
|
|
13
|
+
declare const frameVariants: (props?: ({
|
|
14
|
+
ratio?: "square" | "portrait" | "landscape" | "wide" | null | undefined;
|
|
15
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
|
+
export type ImageGalleryRatio = NonNullable<VariantProps<typeof frameVariants>["ratio"]>;
|
|
17
|
+
export type ImageGalleryImage = {
|
|
18
|
+
src: string;
|
|
19
|
+
/** Required: describe the photo. The gallery adds position ("2 of 5") on its own. */
|
|
20
|
+
alt: string;
|
|
21
|
+
srcSet?: string;
|
|
22
|
+
sizes?: string;
|
|
23
|
+
width?: number;
|
|
24
|
+
height?: number;
|
|
25
|
+
};
|
|
26
|
+
export type ImageGalleryProps = Omit<React.HTMLAttributes<HTMLElement>, "onChange"> & VariantProps<typeof frameVariants> & {
|
|
27
|
+
images: readonly ImageGalleryImage[];
|
|
28
|
+
/** Controlled index of the image shown. */
|
|
29
|
+
index?: number;
|
|
30
|
+
/** Initial index in uncontrolled mode. */
|
|
31
|
+
defaultIndex?: number;
|
|
32
|
+
/** Called when the shown image changes — by swipe, arrow, key or thumbnail. */
|
|
33
|
+
onIndexChange?: (index: number) => void;
|
|
34
|
+
/**
|
|
35
|
+
* What sits under the frame: a strip of `thumbnails`, a row of `dots`, or
|
|
36
|
+
* `none`. Only rendered when there is more than one image.
|
|
37
|
+
*/
|
|
38
|
+
indicator?: "thumbnails" | "dots" | "none";
|
|
39
|
+
/** Accessible name for the carousel, e.g. the product's name. */
|
|
40
|
+
label?: string;
|
|
41
|
+
/** Accessible name for the previous-image button. */
|
|
42
|
+
previousLabel?: string;
|
|
43
|
+
/** Accessible name for the next-image button. */
|
|
44
|
+
nextLabel?: string;
|
|
45
|
+
/** Shown in the frame when `images` is empty. */
|
|
46
|
+
placeholder?: React.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A product photo carousel: one frame, swipeable on touch, with previous/next
|
|
50
|
+
* buttons, arrow-key navigation and an optional thumbnail strip or dots.
|
|
51
|
+
*
|
|
52
|
+
* Swiping is native scrolling — a horizontal `scroll-snap` track, not a
|
|
53
|
+
* pointer-event reimplementation — so it has the platform's own momentum and
|
|
54
|
+
* edge behaviour on phones. The shown index follows the scroll position, and
|
|
55
|
+
* setting the index (by button, key, thumbnail or a controlled `index`)
|
|
56
|
+
* scrolls the track. Both directions meet at one rule: the effect only scrolls
|
|
57
|
+
* when the track is not already showing the index, so a swipe that updates
|
|
58
|
+
* the index never fights itself.
|
|
59
|
+
*
|
|
60
|
+
* Controlled or uncontrolled through `index`/`defaultIndex`/`onIndexChange`.
|
|
61
|
+
*
|
|
62
|
+
* Follows the WAI-ARIA carousel pattern: the root is a `<section>` with
|
|
63
|
+
* `aria-roledescription="carousel"` (a landmark region once `label` names
|
|
64
|
+
* it), each slide a group named "n of total", and slides out of view are
|
|
65
|
+
* `aria-hidden`. The first image loads eagerly and
|
|
66
|
+
* the rest lazily, so a gallery of ten costs one download up front.
|
|
67
|
+
*/
|
|
68
|
+
export declare const ImageGallery: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement>, "onChange"> & VariantProps<(props?: ({
|
|
69
|
+
ratio?: "square" | "portrait" | "landscape" | "wide" | null | undefined;
|
|
70
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
71
|
+
images: readonly ImageGalleryImage[];
|
|
72
|
+
/** Controlled index of the image shown. */
|
|
73
|
+
index?: number;
|
|
74
|
+
/** Initial index in uncontrolled mode. */
|
|
75
|
+
defaultIndex?: number;
|
|
76
|
+
/** Called when the shown image changes — by swipe, arrow, key or thumbnail. */
|
|
77
|
+
onIndexChange?: (index: number) => void;
|
|
78
|
+
/**
|
|
79
|
+
* What sits under the frame: a strip of `thumbnails`, a row of `dots`, or
|
|
80
|
+
* `none`. Only rendered when there is more than one image.
|
|
81
|
+
*/
|
|
82
|
+
indicator?: "thumbnails" | "dots" | "none";
|
|
83
|
+
/** Accessible name for the carousel, e.g. the product's name. */
|
|
84
|
+
label?: string;
|
|
85
|
+
/** Accessible name for the previous-image button. */
|
|
86
|
+
previousLabel?: string;
|
|
87
|
+
/** Accessible name for the next-image button. */
|
|
88
|
+
nextLabel?: string;
|
|
89
|
+
/** Shown in the frame when `images` is empty. */
|
|
90
|
+
placeholder?: React.ReactNode;
|
|
91
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
92
|
+
export { frameVariants as imageGalleryFrameVariants };
|
|
93
|
+
//# sourceMappingURL=ImageGallery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageGallery.d.ts","sourceRoot":"","sources":["../../../src/components/ImageGallery/ImageGallery.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;;;;;;GASG;AACH,QAAA,MAAM,aAAa;;8EAYjB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,GACjF,YAAY,CAAC,OAAO,aAAa,CAAC,GAAG;IACnC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;IAC3C,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC/B,CAAC;AAMJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY;;;YA9Cb,SAAS,iBAAiB,EAAE;IACpC,2CAA2C;YACnC,MAAM;IACd,0CAA0C;mBAC3B,MAAM;IACrB,+EAA+E;oBAC/D,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IACvC;;;OAGG;gBACS,YAAY,GAAG,MAAM,GAAG,MAAM;IAC1C,iEAAiE;YACzD,MAAM;IACd,qDAAqD;oBACrC,MAAM;IACtB,iDAAiD;gBACrC,MAAM;IAClB,iDAAiD;kBACnC,KAAK,CAAC,SAAS;qCAwNhC,CAAC;AAGF,OAAO,EAAE,aAAa,IAAI,yBAAyB,EAAE,CAAC"}
|