@jackbernnie/hiyf 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/MapEmbed.d.ts +27 -0
- package/dist/components/MapEmbed.js +30 -0
- package/dist/components/Sidebar.d.ts +9 -1
- package/dist/components/Sidebar.js +63 -9
- package/dist/components/Table.d.ts +27 -2
- package/dist/components/Table.js +179 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MapEmbed — the closed, sanctioned way to render an embedded location map.
|
|
3
|
+
*
|
|
4
|
+
* Raw <iframe> map embeds are an off-system escape hatch: every provider URL,
|
|
5
|
+
* viewport shape, loading behavior, and frame treatment would be up for grabs.
|
|
6
|
+
* This wrapper closes it:
|
|
7
|
+
* - Location is expressed only as decimal latitude/longitude with an
|
|
8
|
+
* enumerated map view and clamped zoom.
|
|
9
|
+
* - The embed always uses the approved responsive 16:9 frame, rounded
|
|
10
|
+
* corners, muted loading surface, and Google Maps embed URL.
|
|
11
|
+
* - NO `className`/`style` escape hatch. Need a new map affordance? Add it
|
|
12
|
+
* here so every map stays on-system.
|
|
13
|
+
*/
|
|
14
|
+
export interface MapEmbedProps {
|
|
15
|
+
/** Latitude in decimal degrees. */
|
|
16
|
+
lat: number;
|
|
17
|
+
/** Longitude in decimal degrees. */
|
|
18
|
+
lon: number;
|
|
19
|
+
/** Zoom level 1-21. Defaults to 14. */
|
|
20
|
+
zoom?: number;
|
|
21
|
+
/** 'satellite' (default) or 'map'. */
|
|
22
|
+
view?: 'map' | 'satellite';
|
|
23
|
+
/** Accessible title for the embed. Defaults to 'Map'. */
|
|
24
|
+
title?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Closed responsive map embed — no className/style. */
|
|
27
|
+
export declare function MapEmbed({ lat, lon, zoom, view, title, }: MapEmbedProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { AspectRatio } from './ui/aspect-ratio.js';
|
|
3
|
+
|
|
4
|
+
function MapEmbed({
|
|
5
|
+
lat,
|
|
6
|
+
lon,
|
|
7
|
+
zoom = 14,
|
|
8
|
+
view = "satellite",
|
|
9
|
+
title = "Map"
|
|
10
|
+
}) {
|
|
11
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const zoomValue = Number.isNaN(zoom) ? 14 : zoom;
|
|
15
|
+
const clampedZoom = Math.min(21, Math.max(1, zoomValue));
|
|
16
|
+
const src = `https://maps.google.com/maps?ll=${lat},${lon}&q=${lat},${lon}&z=${clampedZoom}&t=${view === "map" ? "m" : "k"}&output=embed`;
|
|
17
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-lg bg-muted", children: /* @__PURE__ */ jsx(AspectRatio, { ratio: 16 / 9, children: /* @__PURE__ */ jsx(
|
|
18
|
+
"iframe",
|
|
19
|
+
{
|
|
20
|
+
src,
|
|
21
|
+
title,
|
|
22
|
+
loading: "lazy",
|
|
23
|
+
referrerPolicy: "no-referrer-when-downgrade",
|
|
24
|
+
allowFullScreen: true,
|
|
25
|
+
className: "size-full border-0"
|
|
26
|
+
}
|
|
27
|
+
) }) });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { MapEmbed };
|
|
@@ -8,7 +8,8 @@ import * as React from "react";
|
|
|
8
8
|
* - items take an `icon`, can be `active`, and may nest `items` to become a
|
|
9
9
|
* collapsible sub-menu (chevron expands an indented list).
|
|
10
10
|
* - optional `brand` header (icon + title + subtitle) and `user` footer
|
|
11
|
-
* (avatar + name + email), both of which also collapse to
|
|
11
|
+
* (avatar + name + email + optional menu), both of which also collapse to
|
|
12
|
+
* their icon.
|
|
12
13
|
* - `children` is the main inset content; it gets a header with the trigger.
|
|
13
14
|
* - No `className`/`style` escape hatch. Need badges or actions? Add typed
|
|
14
15
|
* fields to SidebarNavItem here.
|
|
@@ -38,10 +39,17 @@ export interface SidebarBrand {
|
|
|
38
39
|
title: string;
|
|
39
40
|
subtitle?: string;
|
|
40
41
|
}
|
|
42
|
+
export interface SidebarUserMenuItem {
|
|
43
|
+
label: string;
|
|
44
|
+
tone?: "default" | "danger";
|
|
45
|
+
onSelect?: () => void;
|
|
46
|
+
separatorBefore?: boolean;
|
|
47
|
+
}
|
|
41
48
|
export interface SidebarUser {
|
|
42
49
|
name: string;
|
|
43
50
|
email?: string;
|
|
44
51
|
avatar?: React.ReactNode;
|
|
52
|
+
menuItems?: SidebarUserMenuItem[];
|
|
45
53
|
}
|
|
46
54
|
export interface SidebarProps {
|
|
47
55
|
groups: SidebarNavGroup[];
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { SidebarMenuButton, SidebarProvider, Sidebar as Sidebar$1, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarRail, SidebarInset, SidebarTrigger, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton, useSidebar, SidebarFooter } from './ui/sidebar.js';
|
|
4
5
|
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from './ui/collapsible.js';
|
|
6
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuItem } from './ui/dropdown-menu.js';
|
|
5
7
|
import { TooltipProvider } from './ui/tooltip.js';
|
|
6
8
|
|
|
7
9
|
function Chevron() {
|
|
@@ -20,6 +22,25 @@ function Chevron() {
|
|
|
20
22
|
}
|
|
21
23
|
);
|
|
22
24
|
}
|
|
25
|
+
function ChevronsUpDown() {
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"svg",
|
|
28
|
+
{
|
|
29
|
+
className: "ml-auto size-4 shrink-0 text-sidebar-foreground/60",
|
|
30
|
+
viewBox: "0 0 24 24",
|
|
31
|
+
fill: "none",
|
|
32
|
+
stroke: "currentColor",
|
|
33
|
+
strokeWidth: "2",
|
|
34
|
+
strokeLinecap: "round",
|
|
35
|
+
strokeLinejoin: "round",
|
|
36
|
+
"aria-hidden": "true",
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
|
|
39
|
+
/* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
23
44
|
function NavItem({ item }) {
|
|
24
45
|
if (item.items && item.items.length > 0) {
|
|
25
46
|
return /* @__PURE__ */ jsx(
|
|
@@ -59,6 +80,45 @@ function NavItem({ item }) {
|
|
|
59
80
|
}
|
|
60
81
|
) });
|
|
61
82
|
}
|
|
83
|
+
const UserMenuButton = React.forwardRef(function UserMenuButton2({ user, showChevron, ...props }, ref) {
|
|
84
|
+
return /* @__PURE__ */ jsxs(SidebarMenuButton, { ref, size: "lg", ...props, children: [
|
|
85
|
+
user.avatar && /* @__PURE__ */ jsx("div", { className: "size-8 shrink-0 overflow-hidden rounded-lg", children: user.avatar }),
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
|
|
87
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-semibold", children: user.name }),
|
|
88
|
+
user.email && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", children: user.email })
|
|
89
|
+
] }),
|
|
90
|
+
showChevron && /* @__PURE__ */ jsx(ChevronsUpDown, {})
|
|
91
|
+
] });
|
|
92
|
+
});
|
|
93
|
+
function UserFooter({ user }) {
|
|
94
|
+
const { isMobile } = useSidebar();
|
|
95
|
+
const menuItems = user.menuItems ?? [];
|
|
96
|
+
if (menuItems.length === 0) {
|
|
97
|
+
return /* @__PURE__ */ jsx(SidebarFooter, { children: /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(UserMenuButton, { user }) }) }) });
|
|
98
|
+
}
|
|
99
|
+
return /* @__PURE__ */ jsx(SidebarFooter, { children: /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
100
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(UserMenuButton, { user, showChevron: true }) }),
|
|
101
|
+
/* @__PURE__ */ jsx(
|
|
102
|
+
DropdownMenuContent,
|
|
103
|
+
{
|
|
104
|
+
side: isMobile ? "bottom" : "right",
|
|
105
|
+
align: "end",
|
|
106
|
+
sideOffset: 4,
|
|
107
|
+
children: menuItems.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
108
|
+
item.separatorBefore && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
DropdownMenuItem,
|
|
111
|
+
{
|
|
112
|
+
variant: item.tone === "danger" ? "destructive" : "default",
|
|
113
|
+
onSelect: item.onSelect,
|
|
114
|
+
children: item.label
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] }, index))
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
] }) }) }) });
|
|
121
|
+
}
|
|
62
122
|
function Sidebar({ groups, brand, user, children }) {
|
|
63
123
|
return (
|
|
64
124
|
// The icon-rail tooltips on SidebarMenuButton need a TooltipProvider; this
|
|
@@ -76,13 +136,7 @@ function Sidebar({ groups, brand, user, children }) {
|
|
|
76
136
|
group.label && /* @__PURE__ */ jsx(SidebarGroupLabel, { children: group.label }),
|
|
77
137
|
/* @__PURE__ */ jsx(SidebarMenu, { children: group.items.map((item) => /* @__PURE__ */ jsx(NavItem, { item }, item.label)) })
|
|
78
138
|
] }, groupIndex)) }),
|
|
79
|
-
user && /* @__PURE__ */ jsx(
|
|
80
|
-
user.avatar && /* @__PURE__ */ jsx("div", { className: "size-8 shrink-0 overflow-hidden rounded-lg", children: user.avatar }),
|
|
81
|
-
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
|
|
82
|
-
/* @__PURE__ */ jsx("span", { className: "truncate font-semibold", children: user.name }),
|
|
83
|
-
user.email && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", children: user.email })
|
|
84
|
-
] })
|
|
85
|
-
] }) }) }) }),
|
|
139
|
+
user && /* @__PURE__ */ jsx(UserFooter, { user }),
|
|
86
140
|
/* @__PURE__ */ jsx(SidebarRail, {})
|
|
87
141
|
] }),
|
|
88
142
|
/* @__PURE__ */ jsxs(SidebarInset, { children: [
|
|
@@ -6,14 +6,25 @@ import * as React from 'react';
|
|
|
6
6
|
* apply arbitrary one-off styles and compose rows however they like. This wrapper
|
|
7
7
|
* closes it:
|
|
8
8
|
* - `columns` is a typed column descriptor array with `key`, `header`, `align`,
|
|
9
|
-
* and an optional `render` function; no
|
|
9
|
+
* controlled sort/filter metadata, and an optional `render` function; no
|
|
10
|
+
* className escapes.
|
|
10
11
|
* - `data` is a generic array of row objects; the type parameter `T` flows
|
|
11
12
|
* through so column keys and render callbacks are fully type-checked.
|
|
12
13
|
* - `caption` is an optional accessible table caption.
|
|
14
|
+
* - sorting, filtering, and row clicks are controlled callbacks only; Table
|
|
15
|
+
* never mutates, sorts, or filters `data`.
|
|
13
16
|
* - NO `className`/`style` escape hatch. Every table on screen is rendered by
|
|
14
17
|
* the same correct, on-brand structure.
|
|
15
18
|
* - Need a new alignment or row variant? Add it to the column descriptor here.
|
|
16
19
|
*/
|
|
20
|
+
export interface TableFilterOption {
|
|
21
|
+
value: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}
|
|
24
|
+
export type TableSort = {
|
|
25
|
+
key: string;
|
|
26
|
+
direction: 'asc' | 'desc';
|
|
27
|
+
};
|
|
17
28
|
export interface TableColumn<T> {
|
|
18
29
|
/** Property key on each data row (used as default cell content). */
|
|
19
30
|
key: string;
|
|
@@ -21,6 +32,14 @@ export interface TableColumn<T> {
|
|
|
21
32
|
header: string;
|
|
22
33
|
/** Text alignment for both the header and cells. Defaults to 'left'. */
|
|
23
34
|
align?: 'left' | 'right' | 'center';
|
|
35
|
+
/** Whether this column exposes controlled sort actions. */
|
|
36
|
+
sortable?: boolean;
|
|
37
|
+
/** Controlled filter options. The first option conventionally means no filter/all. */
|
|
38
|
+
filterOptions?: TableFilterOption[];
|
|
39
|
+
/** Current controlled filter value for this column. */
|
|
40
|
+
filterValue?: string;
|
|
41
|
+
/** Called when a filter option is selected. */
|
|
42
|
+
onFilterChange?: (value: string) => void;
|
|
24
43
|
/**
|
|
25
44
|
* Optional custom renderer for this column's cell.
|
|
26
45
|
* Receives the full row object and returns a React node.
|
|
@@ -34,6 +53,12 @@ export interface TableProps<T> {
|
|
|
34
53
|
data: T[];
|
|
35
54
|
/** Optional accessible caption rendered below the table. */
|
|
36
55
|
caption?: string;
|
|
56
|
+
/** Current controlled sort state. */
|
|
57
|
+
sort?: TableSort;
|
|
58
|
+
/** Called when a column sort direction is selected. */
|
|
59
|
+
onSortChange?: (sort: TableSort) => void;
|
|
60
|
+
/** Called when a non-interactive part of a row is clicked or keyboard-activated. */
|
|
61
|
+
onRowClick?: (row: T) => void;
|
|
37
62
|
}
|
|
38
63
|
/** Closed generic data table — no className/style. */
|
|
39
|
-
export declare function Table<T extends Record<string, unknown>>({ columns, data, caption, }: TableProps<T>): React.JSX.Element;
|
|
64
|
+
export declare function Table<T extends Record<string, unknown>>({ columns, data, caption, sort, onSortChange, onRowClick, }: TableProps<T>): React.JSX.Element;
|
package/dist/components/Table.js
CHANGED
|
@@ -1,21 +1,196 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { Table as Table$1, TableCaption, TableHeader, TableRow, TableHead, TableBody, TableCell } from './ui/table.js';
|
|
4
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from './ui/dropdown-menu.js';
|
|
4
5
|
|
|
5
6
|
const ALIGN_CLASS = {
|
|
6
7
|
left: "text-left",
|
|
7
8
|
right: "text-right",
|
|
8
9
|
center: "text-center"
|
|
9
10
|
};
|
|
11
|
+
const HEADER_FLEX_CLASS = {
|
|
12
|
+
left: "justify-start",
|
|
13
|
+
right: "justify-end",
|
|
14
|
+
center: "justify-center"
|
|
15
|
+
};
|
|
16
|
+
function CheckGlyph({ visible }) {
|
|
17
|
+
return /* @__PURE__ */ jsx(
|
|
18
|
+
"svg",
|
|
19
|
+
{
|
|
20
|
+
className: visible ? "size-4 shrink-0" : "size-4 shrink-0 opacity-0",
|
|
21
|
+
viewBox: "0 0 24 24",
|
|
22
|
+
fill: "none",
|
|
23
|
+
stroke: "currentColor",
|
|
24
|
+
strokeWidth: "2",
|
|
25
|
+
strokeLinecap: "round",
|
|
26
|
+
strokeLinejoin: "round",
|
|
27
|
+
"aria-hidden": "true",
|
|
28
|
+
children: /* @__PURE__ */ jsx("path", { d: "m20 6-11 11-5-5" })
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
function SortGlyph({ direction }) {
|
|
33
|
+
return /* @__PURE__ */ jsx(
|
|
34
|
+
"svg",
|
|
35
|
+
{
|
|
36
|
+
className: "size-4 shrink-0",
|
|
37
|
+
viewBox: "0 0 24 24",
|
|
38
|
+
fill: "none",
|
|
39
|
+
stroke: "currentColor",
|
|
40
|
+
strokeWidth: "2",
|
|
41
|
+
strokeLinecap: "round",
|
|
42
|
+
strokeLinejoin: "round",
|
|
43
|
+
"aria-hidden": "true",
|
|
44
|
+
children: direction === "asc" ? /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) : /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function ChevronDownGlyph({ active }) {
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
"svg",
|
|
51
|
+
{
|
|
52
|
+
className: active ? "size-4 shrink-0" : "size-4 shrink-0 opacity-50 transition-opacity group-hover/header:opacity-100 group-focus-within/header:opacity-100",
|
|
53
|
+
viewBox: "0 0 24 24",
|
|
54
|
+
fill: "none",
|
|
55
|
+
stroke: "currentColor",
|
|
56
|
+
strokeWidth: "2",
|
|
57
|
+
strokeLinecap: "round",
|
|
58
|
+
strokeLinejoin: "round",
|
|
59
|
+
"aria-hidden": "true",
|
|
60
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function HeaderMenu({
|
|
65
|
+
column,
|
|
66
|
+
sort,
|
|
67
|
+
onSortChange
|
|
68
|
+
}) {
|
|
69
|
+
const activeSort = sort?.key === column.key ? sort : void 0;
|
|
70
|
+
const firstFilterValue = column.filterOptions?.[0]?.value;
|
|
71
|
+
const filterActive = column.filterValue !== void 0 && firstFilterValue !== void 0 && column.filterValue !== firstFilterValue;
|
|
72
|
+
const active = Boolean(activeSort) || filterActive;
|
|
73
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
75
|
+
"button",
|
|
76
|
+
{
|
|
77
|
+
type: "button",
|
|
78
|
+
className: active ? "inline-flex size-6 items-center justify-center rounded-md text-foreground outline-hidden transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring" : "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground outline-hidden transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring",
|
|
79
|
+
"aria-label": `${column.header} options`,
|
|
80
|
+
children: activeSort ? /* @__PURE__ */ jsx(SortGlyph, { direction: activeSort.direction }) : /* @__PURE__ */ jsx(ChevronDownGlyph, { active })
|
|
81
|
+
}
|
|
82
|
+
) }),
|
|
83
|
+
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
|
|
84
|
+
column.sortable && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
+
/* @__PURE__ */ jsxs(
|
|
86
|
+
DropdownMenuItem,
|
|
87
|
+
{
|
|
88
|
+
onSelect: () => onSortChange?.({ key: column.key, direction: "asc" }),
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsx(CheckGlyph, { visible: activeSort?.direction === "asc" }),
|
|
91
|
+
"Sort ascending"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/* @__PURE__ */ jsxs(
|
|
96
|
+
DropdownMenuItem,
|
|
97
|
+
{
|
|
98
|
+
onSelect: () => onSortChange?.({ key: column.key, direction: "desc" }),
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ jsx(CheckGlyph, { visible: activeSort?.direction === "desc" }),
|
|
101
|
+
"Sort descending"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] }),
|
|
106
|
+
column.sortable && column.filterOptions && column.filterOptions.length > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
107
|
+
column.filterOptions?.map((option) => /* @__PURE__ */ jsxs(
|
|
108
|
+
DropdownMenuItem,
|
|
109
|
+
{
|
|
110
|
+
onSelect: () => column.onFilterChange?.(option.value),
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsx(CheckGlyph, { visible: column.filterValue === option.value }),
|
|
113
|
+
option.label
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
option.value
|
|
117
|
+
))
|
|
118
|
+
] })
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
function HeaderContent({
|
|
122
|
+
column,
|
|
123
|
+
sort,
|
|
124
|
+
onSortChange
|
|
125
|
+
}) {
|
|
126
|
+
const hasMenu = column.sortable || column.filterOptions && column.filterOptions.length > 0;
|
|
127
|
+
if (!hasMenu) {
|
|
128
|
+
return /* @__PURE__ */ jsx(Fragment, { children: column.header });
|
|
129
|
+
}
|
|
130
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-1 ${HEADER_FLEX_CLASS[column.align ?? "left"]}`, children: [
|
|
131
|
+
/* @__PURE__ */ jsx("span", { children: column.header }),
|
|
132
|
+
/* @__PURE__ */ jsx(HeaderMenu, { column, sort, onSortChange })
|
|
133
|
+
] });
|
|
134
|
+
}
|
|
135
|
+
function rowClickIsFromInteractiveElement(event) {
|
|
136
|
+
return Boolean(
|
|
137
|
+
event.target.closest(
|
|
138
|
+
'button, a, input, textarea, select, [role="menuitem"], [role="checkbox"]'
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
10
142
|
function Table({
|
|
11
143
|
columns,
|
|
12
144
|
data,
|
|
13
|
-
caption
|
|
145
|
+
caption,
|
|
146
|
+
sort,
|
|
147
|
+
onSortChange,
|
|
148
|
+
onRowClick
|
|
14
149
|
}) {
|
|
15
150
|
return /* @__PURE__ */ jsxs(Table$1, { children: [
|
|
16
151
|
caption && /* @__PURE__ */ jsx(TableCaption, { children: caption }),
|
|
17
|
-
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
18
|
-
|
|
152
|
+
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
153
|
+
TableHead,
|
|
154
|
+
{
|
|
155
|
+
className: col.sortable || col.filterOptions && col.filterOptions.length > 0 ? `${ALIGN_CLASS[col.align ?? "left"]} group/header` : ALIGN_CLASS[col.align ?? "left"],
|
|
156
|
+
children: /* @__PURE__ */ jsx(
|
|
157
|
+
HeaderContent,
|
|
158
|
+
{
|
|
159
|
+
column: col,
|
|
160
|
+
sort,
|
|
161
|
+
onSortChange
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
},
|
|
165
|
+
col.key
|
|
166
|
+
)) }) }),
|
|
167
|
+
/* @__PURE__ */ jsx(TableBody, { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx(
|
|
168
|
+
TableRow,
|
|
169
|
+
{
|
|
170
|
+
className: onRowClick ? "cursor-pointer" : void 0,
|
|
171
|
+
tabIndex: onRowClick ? 0 : void 0,
|
|
172
|
+
onClick: onRowClick ? (event) => {
|
|
173
|
+
if (rowClickIsFromInteractiveElement(event)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
onRowClick(row);
|
|
177
|
+
} : void 0,
|
|
178
|
+
onKeyDown: onRowClick ? (event) => {
|
|
179
|
+
if (event.key !== "Enter" && event.key !== " ") {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (event.target.closest(
|
|
183
|
+
'button, a, input, textarea, select, [role="menuitem"], [role="checkbox"]'
|
|
184
|
+
)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
onRowClick(row);
|
|
189
|
+
} : void 0,
|
|
190
|
+
children: columns.map((col) => /* @__PURE__ */ jsx(TableCell, { className: ALIGN_CLASS[col.align ?? "left"], children: col.render ? col.render(row) : String(row[col.key] ?? "") }, col.key))
|
|
191
|
+
},
|
|
192
|
+
rowIndex
|
|
193
|
+
)) })
|
|
19
194
|
] });
|
|
20
195
|
}
|
|
21
196
|
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export { Image } from './components/Image';
|
|
|
64
64
|
export type { ImageProps, ImageRadius, ImageFit } from './components/Image';
|
|
65
65
|
export { Kbd } from './components/Kbd';
|
|
66
66
|
export type { KbdProps } from './components/Kbd';
|
|
67
|
+
export { MapEmbed } from './components/MapEmbed';
|
|
68
|
+
export type { MapEmbedProps } from './components/MapEmbed';
|
|
67
69
|
export { Empty } from './components/Empty';
|
|
68
70
|
export type { EmptyProps } from './components/Empty';
|
|
69
71
|
export { Toaster } from './components/Toaster';
|
|
@@ -75,7 +77,7 @@ export type { AspectRatioProps } from './components/AspectRatio';
|
|
|
75
77
|
export { ScrollArea } from './components/ScrollArea';
|
|
76
78
|
export type { ScrollAreaProps } from './components/ScrollArea';
|
|
77
79
|
export { Table } from './components/Table';
|
|
78
|
-
export type { TableProps, TableColumn } from './components/Table';
|
|
80
|
+
export type { TableProps, TableColumn, TableFilterOption, TableSort } from './components/Table';
|
|
79
81
|
export { Carousel } from './components/Carousel';
|
|
80
82
|
export type { CarouselProps } from './components/Carousel';
|
|
81
83
|
export { Tabs } from './components/Tabs';
|
|
@@ -113,7 +115,7 @@ export type { NavigationMenuProps, NavItem } from './components/NavigationMenu';
|
|
|
113
115
|
export { Menubar } from './components/Menubar';
|
|
114
116
|
export type { MenubarProps, MenuDef, MenubarItemDef } from './components/Menubar';
|
|
115
117
|
export { Sidebar } from './components/Sidebar';
|
|
116
|
-
export type { SidebarProps, SidebarNavGroup, SidebarNavItem, SidebarNavSubItem, SidebarBrand, SidebarUser, } from './components/Sidebar';
|
|
118
|
+
export type { SidebarProps, SidebarNavGroup, SidebarNavItem, SidebarNavSubItem, SidebarBrand, SidebarUser, SidebarUserMenuItem, } from './components/Sidebar';
|
|
117
119
|
export { Resizable } from './components/Resizable';
|
|
118
120
|
export type { ResizableProps } from './components/Resizable';
|
|
119
121
|
export { Chart } from './components/Chart';
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export { Spinner } from './components/Spinner.js';
|
|
|
31
31
|
export { Avatar } from './components/Avatar.js';
|
|
32
32
|
export { Image } from './components/Image.js';
|
|
33
33
|
export { Kbd } from './components/Kbd.js';
|
|
34
|
+
export { MapEmbed } from './components/MapEmbed.js';
|
|
34
35
|
export { Empty } from './components/Empty.js';
|
|
35
36
|
export { Toaster } from './components/Toaster.js';
|
|
36
37
|
export { Separator } from './components/Separator.js';
|
package/package.json
CHANGED