ppij-design-system 0.0.0-stage → 0.2.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/banners.d.ts +31 -0
- package/dist/banners.js +18 -0
- package/dist/card-themes.d.ts +14 -0
- package/dist/card-themes.js +325 -0
- package/dist/components/account-menu.d.ts +24 -0
- package/dist/components/account-menu.js +13 -0
- package/dist/components/admin-drawer.d.ts +34 -0
- package/dist/components/admin-drawer.js +30 -0
- package/dist/components/alert-dialog.d.ts +16 -0
- package/dist/components/alert-dialog.js +39 -0
- package/dist/components/alert.d.ts +11 -0
- package/dist/components/alert.js +29 -0
- package/dist/components/avatar.d.ts +11 -0
- package/dist/components/avatar.js +22 -0
- package/dist/components/badge.d.ts +9 -0
- package/dist/components/badge.js +27 -0
- package/dist/components/banner-carousel.d.ts +14 -0
- package/dist/components/banner-carousel.js +90 -0
- package/dist/components/bottom-bar.d.ts +22 -0
- package/dist/components/bottom-bar.js +12 -0
- package/dist/components/button.d.ts +10 -0
- package/dist/components/button.js +37 -0
- package/dist/components/card-upload.d.ts +30 -0
- package/dist/components/card-upload.js +19 -0
- package/dist/components/card.d.ts +9 -0
- package/dist/components/card.js +24 -0
- package/dist/components/checkbox.d.ts +4 -0
- package/dist/components/checkbox.js +9 -0
- package/dist/components/data-table.d.ts +26 -0
- package/dist/components/data-table.js +17 -0
- package/dist/components/dialog.d.ts +17 -0
- package/dist/components/dialog.js +37 -0
- package/dist/components/drawer.d.ts +13 -0
- package/dist/components/drawer.js +34 -0
- package/dist/components/dropdown-menu.d.ts +25 -0
- package/dist/components/dropdown-menu.js +50 -0
- package/dist/components/editable-panel.d.ts +25 -0
- package/dist/components/editable-panel.js +18 -0
- package/dist/components/empty.d.ts +12 -0
- package/dist/components/empty.js +33 -0
- package/dist/components/file-upload.d.ts +28 -0
- package/dist/components/file-upload.js +21 -0
- package/dist/components/form-panel.d.ts +15 -0
- package/dist/components/form-panel.js +9 -0
- package/dist/components/funding-picker.d.ts +20 -0
- package/dist/components/funding-picker.js +16 -0
- package/dist/components/input.d.ts +3 -0
- package/dist/components/input.js +6 -0
- package/dist/components/invitation-card.d.ts +14 -0
- package/dist/components/invitation-card.js +13 -0
- package/dist/components/label.d.ts +4 -0
- package/dist/components/label.js +7 -0
- package/dist/components/member-card-block.d.ts +47 -0
- package/dist/components/member-card-block.js +329 -0
- package/dist/components/member-card.css +92 -0
- package/dist/components/member-card.d.ts +29 -0
- package/dist/components/member-card.js +22 -0
- package/dist/components/month-picker.d.ts +16 -0
- package/dist/components/month-picker.js +19 -0
- package/dist/components/offline-banner.d.ts +14 -0
- package/dist/components/offline-banner.js +14 -0
- package/dist/components/page-header.d.ts +14 -0
- package/dist/components/page-header.js +10 -0
- package/dist/components/phone-field.d.ts +25 -0
- package/dist/components/phone-field.js +64 -0
- package/dist/components/pill-radio.d.ts +21 -0
- package/dist/components/pill-radio.js +15 -0
- package/dist/components/portal-shell.d.ts +53 -0
- package/dist/components/portal-shell.js +19 -0
- package/dist/components/postcode-field.d.ts +26 -0
- package/dist/components/postcode-field.js +14 -0
- package/dist/components/progress.d.ts +4 -0
- package/dist/components/progress.js +8 -0
- package/dist/components/radio-group.d.ts +5 -0
- package/dist/components/radio-group.js +11 -0
- package/dist/components/review-panel.d.ts +20 -0
- package/dist/components/review-panel.js +14 -0
- package/dist/components/saving-indicator.d.ts +15 -0
- package/dist/components/saving-indicator.js +19 -0
- package/dist/components/scroll-area.d.ts +5 -0
- package/dist/components/scroll-area.js +13 -0
- package/dist/components/search-filter.d.ts +39 -0
- package/dist/components/search-filter.js +19 -0
- package/dist/components/segmented-progress.d.ts +16 -0
- package/dist/components/segmented-progress.js +12 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/select.js +38 -0
- package/dist/components/separator.d.ts +4 -0
- package/dist/components/separator.js +7 -0
- package/dist/components/service-tiles.d.ts +26 -0
- package/dist/components/service-tiles.js +17 -0
- package/dist/components/sheet.d.ts +14 -0
- package/dist/components/sheet.js +40 -0
- package/dist/components/sidebar.d.ts +69 -0
- package/dist/components/sidebar.js +210 -0
- package/dist/components/skeleton.d.ts +11 -0
- package/dist/components/skeleton.js +11 -0
- package/dist/components/sonner.d.ts +9 -0
- package/dist/components/sonner.js +25 -0
- package/dist/components/spinner.d.ts +14 -0
- package/dist/components/spinner.js +22 -0
- package/dist/components/stat-card.d.ts +22 -0
- package/dist/components/stat-card.js +12 -0
- package/dist/components/status-card.d.ts +24 -0
- package/dist/components/status-card.js +26 -0
- package/dist/components/stepper.d.ts +25 -0
- package/dist/components/stepper.js +23 -0
- package/dist/components/submit-overlay.d.ts +16 -0
- package/dist/components/submit-overlay.js +15 -0
- package/dist/components/suggestion-sheet.d.ts +20 -0
- package/dist/components/suggestion-sheet.js +36 -0
- package/dist/components/switch.d.ts +6 -0
- package/dist/components/switch.js +8 -0
- package/dist/components/table.d.ts +10 -0
- package/dist/components/table.js +28 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +28 -0
- package/dist/components/tag-input.d.ts +15 -0
- package/dist/components/tag-input.js +31 -0
- package/dist/components/textarea.d.ts +3 -0
- package/dist/components/textarea.js +6 -0
- package/dist/components/timeline.d.ts +18 -0
- package/dist/components/timeline.js +11 -0
- package/dist/components/todo-strip.d.ts +14 -0
- package/dist/components/todo-strip.js +11 -0
- package/dist/components/tooltip.d.ts +7 -0
- package/dist/components/tooltip.js +16 -0
- package/dist/components/top-bar.d.ts +23 -0
- package/dist/components/top-bar.js +14 -0
- package/dist/components/type-ahead.d.ts +20 -0
- package/dist/components/type-ahead.js +22 -0
- package/dist/components/unit-card.d.ts +26 -0
- package/dist/components/unit-card.js +18 -0
- package/dist/components/view-toggle.d.ts +20 -0
- package/dist/components/view-toggle.js +15 -0
- package/dist/components/wizard-footer.d.ts +24 -0
- package/dist/components/wizard-footer.js +17 -0
- package/dist/components/wizard-header.d.ts +29 -0
- package/dist/components/wizard-header.js +18 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +11 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +70 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/sample-banners.d.ts +8 -0
- package/dist/sample-banners.js +28 -0
- package/dist/tokens.css +119 -0
- package/dist/tokens.json +50 -0
- package/package.json +60 -4
- package/README.md +0 -3
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
|
|
3
|
+
declare function ScrollArea({ className, children, ...props }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function ScrollBar({ className, orientation, ...props }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>): React.JSX.Element;
|
|
5
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
|
|
5
|
+
function ScrollArea({ className, children, ...props }) {
|
|
6
|
+
return (_jsxs(ScrollAreaPrimitive.Root, { "data-slot": "scroll-area", className: cn("relative", className), ...props, children: [_jsx(ScrollAreaPrimitive.Viewport, { "data-slot": "scroll-area-viewport", className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1", children: children }), _jsx(ScrollBar, {}), _jsx(ScrollAreaPrimitive.Corner, {})] }));
|
|
7
|
+
}
|
|
8
|
+
function ScrollBar({ className, orientation = "vertical", ...props }) {
|
|
9
|
+
return (_jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, { "data-slot": "scroll-area-scrollbar", orientation: orientation, className: cn("flex touch-none p-px transition-colors select-none", orientation === "vertical" &&
|
|
10
|
+
"h-full w-2.5 border-l border-l-transparent", orientation === "horizontal" &&
|
|
11
|
+
"h-2.5 flex-col border-t border-t-transparent", className), ...props, children: _jsx(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "relative flex-1 rounded-full bg-border" }) }));
|
|
12
|
+
}
|
|
13
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface ActiveFilter {
|
|
3
|
+
/** Group name shown before the value, for example "Komsat". */
|
|
4
|
+
label: string;
|
|
5
|
+
/** The chosen value, for example "Okayama". */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Removes this filter. */
|
|
8
|
+
onRemove: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchFilterProps {
|
|
11
|
+
searchValue: string;
|
|
12
|
+
onSearchChange: (value: string) => void;
|
|
13
|
+
searchPlaceholder?: string;
|
|
14
|
+
/** The one or two quick filters that stay visible beside the search field. */
|
|
15
|
+
filters?: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Advanced filter fields, hidden behind the filter icon button and
|
|
18
|
+
* shown in a drawer (right on desktop, bottom sheet on phone).
|
|
19
|
+
* Use this when a page has more filters than the bar can hold.
|
|
20
|
+
*/
|
|
21
|
+
advancedFilters?: React.ReactNode;
|
|
22
|
+
/** How many advanced filters are active; shown as a count on the icon button. */
|
|
23
|
+
advancedActiveCount?: number;
|
|
24
|
+
advancedTitle?: string;
|
|
25
|
+
/** Currently active filters, rendered as removable chips. */
|
|
26
|
+
activeFilters?: ActiveFilter[];
|
|
27
|
+
/** Clears every active filter at once. Shown when two or more are active. */
|
|
28
|
+
onClearAll?: () => void;
|
|
29
|
+
clearAllLabel?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The filter bar above admin lists: one search field, one or two quick
|
|
34
|
+
* filters, and every further filter behind the filter icon, which opens
|
|
35
|
+
* them in a drawer. One removable chip per active filter keeps the
|
|
36
|
+
* current selection visible even while the advanced filters are hidden.
|
|
37
|
+
* Presentation only: the page owns the values and the filtering itself.
|
|
38
|
+
*/
|
|
39
|
+
export declare function SearchFilter({ searchValue, onSearchChange, searchPlaceholder, filters, advancedFilters, advancedActiveCount, advancedTitle, activeFilters, onClearAll, clearAllLabel, className, }: SearchFilterProps): React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { IconSearch, IconX, IconAdjustmentsHorizontal } from "@tabler/icons-react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
import { Input } from "./input";
|
|
6
|
+
import { Badge } from "./badge";
|
|
7
|
+
import { Button } from "./button";
|
|
8
|
+
import { AdminDrawer } from "./admin-drawer";
|
|
9
|
+
/**
|
|
10
|
+
* The filter bar above admin lists: one search field, one or two quick
|
|
11
|
+
* filters, and every further filter behind the filter icon, which opens
|
|
12
|
+
* them in a drawer. One removable chip per active filter keeps the
|
|
13
|
+
* current selection visible even while the advanced filters are hidden.
|
|
14
|
+
* Presentation only: the page owns the values and the filtering itself.
|
|
15
|
+
*/
|
|
16
|
+
export function SearchFilter({ searchValue, onSearchChange, searchPlaceholder = "Cari...", filters, advancedFilters, advancedActiveCount = 0, advancedTitle = "Filter lanjutan", activeFilters = [], onClearAll, clearAllLabel = "Hapus semua filter", className, }) {
|
|
17
|
+
const [advancedOpen, setAdvancedOpen] = React.useState(false);
|
|
18
|
+
return (_jsxs("div", { className: cn("grid gap-3", className), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "relative min-w-52 flex-1", children: [_jsx(IconSearch, { className: "pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: searchValue, onChange: (event) => onSearchChange(event.target.value), placeholder: searchPlaceholder, className: "pr-9 pl-9", "aria-label": searchPlaceholder }), searchValue !== "" && (_jsx("button", { type: "button", onClick: () => onSearchChange(""), "aria-label": "Hapus kata kunci", className: "absolute top-1/2 right-2 flex size-6 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(IconX, { className: "size-3.5" }) }))] }), filters, advancedFilters && (_jsxs("span", { className: "relative inline-flex", children: [_jsxs(Button, { type: "button", variant: "outline", onClick: () => setAdvancedOpen(true), "aria-label": advancedTitle, children: [_jsx(IconAdjustmentsHorizontal, {}), _jsx("span", { className: "max-sm:hidden", children: "Filter" })] }), advancedActiveCount > 0 && (_jsx("span", { className: "absolute -top-1.5 -right-1.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-brand-red px-1 text-[11px] font-semibold text-white", children: advancedActiveCount }))] }))] }), activeFilters.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [activeFilters.map((filter) => (_jsxs(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [_jsxs("span", { children: [filter.label, ": ", filter.value] }), _jsx("button", { type: "button", onClick: filter.onRemove, "aria-label": "Hapus filter " + filter.label + " " + filter.value, className: "flex size-4 items-center justify-center rounded-full outline-none hover:bg-background/60 focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(IconX, { className: "size-3" }) })] }, filter.label + filter.value))), activeFilters.length > 1 && onClearAll && (_jsx("button", { type: "button", onClick: onClearAll, className: "ml-1 text-sm font-medium text-brand-red outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring", children: clearAllLabel }))] })), advancedFilters && (_jsx(AdminDrawer, { open: advancedOpen, onOpenChange: setAdvancedOpen, title: advancedTitle, description: "Filter tambahan untuk mempersempit daftar. Hasil berubah saat filter dipilih.", locale: "id", footer: _jsx(Button, { onClick: () => setAdvancedOpen(false), children: "Terapkan filter" }), children: _jsx("div", { className: "grid gap-4", children: advancedFilters }) }))] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SegmentedProgressProps {
|
|
2
|
+
/** Segments filled, counting from 1: the current step number. */
|
|
3
|
+
current: number;
|
|
4
|
+
total: number;
|
|
5
|
+
/** Accessible name for the progressbar, for example "Progres pendaftaran". */
|
|
6
|
+
label: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The wizard's progress indicator (WIZ106-09): one rounded segment
|
|
11
|
+
* per step in a flex row, filled up to and including the current
|
|
12
|
+
* step. This is the standard progress treatment for multi-step
|
|
13
|
+
* flows in the portal; the numbered Stepper is deprecated in its
|
|
14
|
+
* favour.
|
|
15
|
+
*/
|
|
16
|
+
export declare function SegmentedProgress({ current, total, label, className }: SegmentedProgressProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
/**
|
|
4
|
+
* The wizard's progress indicator (WIZ106-09): one rounded segment
|
|
5
|
+
* per step in a flex row, filled up to and including the current
|
|
6
|
+
* step. This is the standard progress treatment for multi-step
|
|
7
|
+
* flows in the portal; the numbered Stepper is deprecated in its
|
|
8
|
+
* favour.
|
|
9
|
+
*/
|
|
10
|
+
export function SegmentedProgress({ current, total, label, className }) {
|
|
11
|
+
return (_jsx("div", { role: "progressbar", "aria-label": label, "aria-valuemin": 1, "aria-valuemax": total, "aria-valuenow": current, className: cn("flex gap-1.5", className), children: Array.from({ length: total }, (_, i) => (_jsx("span", { className: cn("h-1.5 min-w-0 flex-1 rounded-full", i < current ? "bg-primary" : "bg-secondary") }, i))) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
3
|
+
declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): React.JSX.Element;
|
|
5
|
+
declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
|
|
6
|
+
declare function SelectTrigger({ className, size, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
7
|
+
size?: "sm" | "default";
|
|
8
|
+
}): React.JSX.Element;
|
|
9
|
+
declare function SelectContent({ className, children, position, align, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>): React.JSX.Element;
|
|
10
|
+
declare function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>): React.JSX.Element;
|
|
11
|
+
declare function SelectItem({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Item>): React.JSX.Element;
|
|
12
|
+
declare function SelectSeparator({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Separator>): React.JSX.Element;
|
|
13
|
+
declare function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>): React.JSX.Element;
|
|
14
|
+
declare function SelectScrollDownButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>): React.JSX.Element;
|
|
15
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { IconCheck, IconChevronDown, IconChevronUp } from "@tabler/icons-react";
|
|
5
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
6
|
+
function Select({ ...props }) {
|
|
7
|
+
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
8
|
+
}
|
|
9
|
+
function SelectGroup({ ...props }) {
|
|
10
|
+
return _jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
11
|
+
}
|
|
12
|
+
function SelectValue({ ...props }) {
|
|
13
|
+
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
14
|
+
}
|
|
15
|
+
function SelectTrigger({ className, size = "default", children, ...props }) {
|
|
16
|
+
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-card px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(IconChevronDown, { className: "size-4 opacity-50" }) })] }));
|
|
17
|
+
}
|
|
18
|
+
function SelectContent({ className, children, position = "item-aligned", align = "center", ...props }) {
|
|
19
|
+
return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", className: cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", position === "popper" &&
|
|
20
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, align: align, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
|
|
21
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), _jsx(SelectScrollDownButton, {})] }) }));
|
|
22
|
+
}
|
|
23
|
+
function SelectLabel({ className, ...props }) {
|
|
24
|
+
return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props }));
|
|
25
|
+
}
|
|
26
|
+
function SelectItem({ className, children, ...props }) {
|
|
27
|
+
return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("relative flex min-h-11 w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex size-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(IconCheck, { className: "size-4" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
|
|
28
|
+
}
|
|
29
|
+
function SelectSeparator({ className, ...props }) {
|
|
30
|
+
return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn("pointer-events-none -mx-1 my-1 h-px bg-border", className), ...props }));
|
|
31
|
+
}
|
|
32
|
+
function SelectScrollUpButton({ className, ...props }) {
|
|
33
|
+
return (_jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: _jsx(IconChevronUp, { className: "size-4" }) }));
|
|
34
|
+
}
|
|
35
|
+
function SelectScrollDownButton({ className, ...props }) {
|
|
36
|
+
return (_jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: _jsx(IconChevronDown, { className: "size-4" }) }));
|
|
37
|
+
}
|
|
38
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
import { Separator as SeparatorPrimitive } from "radix-ui";
|
|
4
|
+
function Separator({ className, orientation = "horizontal", decorative = true, ...props }) {
|
|
5
|
+
return (_jsx(SeparatorPrimitive.Root, { "data-slot": "separator", decorative: decorative, orientation: orientation, className: cn("shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
export { Separator };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface ServiceTile {
|
|
3
|
+
title: string;
|
|
4
|
+
href: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ServiceTileGroup {
|
|
7
|
+
heading: string;
|
|
8
|
+
/** Group icon shown in brand red on every tile. */
|
|
9
|
+
icon: React.ReactNode;
|
|
10
|
+
tiles: ServiceTile[];
|
|
11
|
+
/** Shown in a dashed empty box when the group has no tiles. */
|
|
12
|
+
emptyText?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ServiceTilesProps {
|
|
15
|
+
groups: ServiceTileGroup[];
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The Beranda service groups: national, korda, and komsat services as
|
|
20
|
+
* white tile cards with a red group icon, two columns on phones and
|
|
21
|
+
* three from the medium breakpoint. A group with no services shows its
|
|
22
|
+
* empty line in a dashed box instead of disappearing, so the member
|
|
23
|
+
* learns the group exists. Tiles in a row always share one height:
|
|
24
|
+
* uneven card bottoms in a grid read as a defect, never as rhythm.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ServiceTiles({ groups, className }: ServiceTilesProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconAppsOff } from "@tabler/icons-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
/**
|
|
5
|
+
* The Beranda service groups: national, korda, and komsat services as
|
|
6
|
+
* white tile cards with a red group icon, two columns on phones and
|
|
7
|
+
* three from the medium breakpoint. A group with no services shows its
|
|
8
|
+
* empty line in a dashed box instead of disappearing, so the member
|
|
9
|
+
* learns the group exists. Tiles in a row always share one height:
|
|
10
|
+
* uneven card bottoms in a grid read as a defect, never as rhythm.
|
|
11
|
+
*/
|
|
12
|
+
export function ServiceTiles({ groups, className }) {
|
|
13
|
+
const visible = groups.filter((g) => g.tiles.length > 0 || Boolean(g.emptyText));
|
|
14
|
+
if (visible.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
return (_jsx("div", { className: cn("flex flex-col gap-5", className), children: visible.map((group) => (_jsxs("section", { "aria-label": group.heading, children: [_jsx("h2", { className: "mb-2 text-base font-bold text-brand-ink", children: group.heading }), group.tiles.length === 0 ? (_jsxs("div", { className: "flex items-center gap-3 rounded-xl border border-dashed border-brand-border bg-card p-4 text-sm text-brand-muted", children: [_jsx(IconAppsOff, { className: "size-5 shrink-0", "aria-hidden": "true" }), _jsx("p", { children: group.emptyText })] })) : (_jsx("ul", { className: "grid grid-cols-2 gap-3 md:grid-cols-3", children: group.tiles.map((tile) => (_jsx("li", { className: "flex", children: _jsxs("a", { href: tile.href, className: "flex min-h-11 w-full flex-col gap-2 rounded-xl border border-brand-border bg-card p-4 font-semibold text-brand-ink hover:border-brand-red focus-visible:outline-2 focus-visible:outline-brand-red", children: [_jsx("span", { className: "text-brand-red", children: group.icon }), tile.title] }) }, tile.title))) }))] }, group.heading))) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
3
|
+
declare function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>): React.JSX.Element;
|
|
5
|
+
declare function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>): React.JSX.Element;
|
|
6
|
+
declare function SheetContent({ className, children, side, showCloseButton, ...props }: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
7
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
8
|
+
showCloseButton?: boolean;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
declare function SheetHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
11
|
+
declare function SheetFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
declare function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>): React.JSX.Element;
|
|
13
|
+
declare function SheetDescription({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Description>): React.JSX.Element;
|
|
14
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { IconX } from "@tabler/icons-react";
|
|
5
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
6
|
+
function Sheet({ ...props }) {
|
|
7
|
+
return _jsx(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
8
|
+
}
|
|
9
|
+
function SheetTrigger({ ...props }) {
|
|
10
|
+
return _jsx(SheetPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
11
|
+
}
|
|
12
|
+
function SheetClose({ ...props }) {
|
|
13
|
+
return _jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", ...props });
|
|
14
|
+
}
|
|
15
|
+
function SheetPortal({ ...props }) {
|
|
16
|
+
return _jsx(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
17
|
+
}
|
|
18
|
+
function SheetOverlay({ className, ...props }) {
|
|
19
|
+
return (_jsx(SheetPrimitive.Overlay, { "data-slot": "sheet-overlay", className: cn("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0", className), ...props }));
|
|
20
|
+
}
|
|
21
|
+
function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
|
|
22
|
+
return (_jsxs(SheetPortal, { children: [_jsx(SheetOverlay, {}), _jsxs(SheetPrimitive.Content, { "data-slot": "sheet-content", className: cn("fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500", side === "right" &&
|
|
23
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", side === "left" &&
|
|
24
|
+
"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", side === "top" &&
|
|
25
|
+
"inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", side === "bottom" &&
|
|
26
|
+
"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", className), ...props, children: [children, showCloseButton && (_jsxs(SheetPrimitive.Close, { className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary", children: [_jsx(IconX, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
|
|
27
|
+
}
|
|
28
|
+
function SheetHeader({ className, ...props }) {
|
|
29
|
+
return (_jsx("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-4", className), ...props }));
|
|
30
|
+
}
|
|
31
|
+
function SheetFooter({ className, ...props }) {
|
|
32
|
+
return (_jsx("div", { "data-slot": "sheet-footer", className: cn("mt-auto flex flex-col gap-2 p-4", className), ...props }));
|
|
33
|
+
}
|
|
34
|
+
function SheetTitle({ className, ...props }) {
|
|
35
|
+
return (_jsx(SheetPrimitive.Title, { "data-slot": "sheet-title", className: cn("font-semibold text-foreground", className), ...props }));
|
|
36
|
+
}
|
|
37
|
+
function SheetDescription({ className, ...props }) {
|
|
38
|
+
return (_jsx(SheetPrimitive.Description, { "data-slot": "sheet-description", className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
39
|
+
}
|
|
40
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
import { Input } from "./input";
|
|
5
|
+
import { Separator } from "./separator";
|
|
6
|
+
import { TooltipContent } from "./tooltip";
|
|
7
|
+
type SidebarContextProps = {
|
|
8
|
+
state: "expanded" | "collapsed";
|
|
9
|
+
open: boolean;
|
|
10
|
+
setOpen: (open: boolean) => void;
|
|
11
|
+
openMobile: boolean;
|
|
12
|
+
setOpenMobile: (open: boolean) => void;
|
|
13
|
+
isMobile: boolean;
|
|
14
|
+
toggleSidebar: () => void;
|
|
15
|
+
};
|
|
16
|
+
declare function useSidebar(): SidebarContextProps;
|
|
17
|
+
declare function SidebarProvider({ defaultOpen, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }: React.ComponentProps<"div"> & {
|
|
18
|
+
defaultOpen?: boolean;
|
|
19
|
+
open?: boolean;
|
|
20
|
+
onOpenChange?: (open: boolean) => void;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
declare function Sidebar({ side, variant, collapsible, className, children, ...props }: React.ComponentProps<"div"> & {
|
|
23
|
+
side?: "left" | "right";
|
|
24
|
+
variant?: "sidebar" | "floating" | "inset";
|
|
25
|
+
collapsible?: "offcanvas" | "icon" | "none";
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
declare function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>): React.JSX.Element;
|
|
28
|
+
declare function SidebarRail({ className, ...props }: React.ComponentProps<"button">): React.JSX.Element;
|
|
29
|
+
declare function SidebarInset({ className, ...props }: React.ComponentProps<"main">): React.JSX.Element;
|
|
30
|
+
declare function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>): React.JSX.Element;
|
|
31
|
+
declare function SidebarHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
32
|
+
declare function SidebarFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
33
|
+
declare function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>): React.JSX.Element;
|
|
34
|
+
declare function SidebarContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
35
|
+
declare function SidebarGroup({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
36
|
+
declare function SidebarGroupLabel({ className, asChild, ...props }: React.ComponentProps<"div"> & {
|
|
37
|
+
asChild?: boolean;
|
|
38
|
+
}): React.JSX.Element;
|
|
39
|
+
declare function SidebarGroupAction({ className, asChild, ...props }: React.ComponentProps<"button"> & {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
}): React.JSX.Element;
|
|
42
|
+
declare function SidebarGroupContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
43
|
+
declare function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
44
|
+
declare function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
45
|
+
declare const sidebarMenuButtonVariants: (props?: ({
|
|
46
|
+
variant?: "default" | "outline" | null | undefined;
|
|
47
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
48
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
49
|
+
declare function SidebarMenuButton({ asChild, isActive, variant, size, tooltip, className, ...props }: React.ComponentProps<"button"> & {
|
|
50
|
+
asChild?: boolean;
|
|
51
|
+
isActive?: boolean;
|
|
52
|
+
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
|
|
53
|
+
} & VariantProps<typeof sidebarMenuButtonVariants>): React.JSX.Element;
|
|
54
|
+
declare function SidebarMenuAction({ className, asChild, showOnHover, ...props }: React.ComponentProps<"button"> & {
|
|
55
|
+
asChild?: boolean;
|
|
56
|
+
showOnHover?: boolean;
|
|
57
|
+
}): React.JSX.Element;
|
|
58
|
+
declare function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
59
|
+
declare function SidebarMenuSkeleton({ className, showIcon, ...props }: React.ComponentProps<"div"> & {
|
|
60
|
+
showIcon?: boolean;
|
|
61
|
+
}): React.JSX.Element;
|
|
62
|
+
declare function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
63
|
+
declare function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
64
|
+
declare function SidebarMenuSubButton({ asChild, size, isActive, className, ...props }: React.ComponentProps<"a"> & {
|
|
65
|
+
asChild?: boolean;
|
|
66
|
+
size?: "sm" | "md";
|
|
67
|
+
isActive?: boolean;
|
|
68
|
+
}): React.JSX.Element;
|
|
69
|
+
export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, };
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
import { IconLayoutSidebar } from "@tabler/icons-react";
|
|
7
|
+
import { Slot } from "radix-ui";
|
|
8
|
+
import { useIsMobile } from "../hooks/use-mobile";
|
|
9
|
+
import { Button } from "./button";
|
|
10
|
+
import { Input } from "./input";
|
|
11
|
+
import { Separator } from "./separator";
|
|
12
|
+
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "./sheet";
|
|
13
|
+
import { Skeleton } from "./skeleton";
|
|
14
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip";
|
|
15
|
+
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
16
|
+
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
17
|
+
const SIDEBAR_WIDTH = "16rem";
|
|
18
|
+
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
19
|
+
const SIDEBAR_WIDTH_ICON = "3rem";
|
|
20
|
+
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
21
|
+
const SidebarContext = React.createContext(null);
|
|
22
|
+
function useSidebar() {
|
|
23
|
+
const context = React.useContext(SidebarContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error("useSidebar must be used within a SidebarProvider.");
|
|
26
|
+
}
|
|
27
|
+
return context;
|
|
28
|
+
}
|
|
29
|
+
function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }) {
|
|
30
|
+
const isMobile = useIsMobile();
|
|
31
|
+
const [openMobile, setOpenMobile] = React.useState(false);
|
|
32
|
+
// This is the internal state of the sidebar.
|
|
33
|
+
// We use openProp and setOpenProp for control from outside the component.
|
|
34
|
+
const [_open, _setOpen] = React.useState(defaultOpen);
|
|
35
|
+
const open = openProp ?? _open;
|
|
36
|
+
const setOpen = React.useCallback((value) => {
|
|
37
|
+
const openState = typeof value === "function" ? value(open) : value;
|
|
38
|
+
if (setOpenProp) {
|
|
39
|
+
setOpenProp(openState);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
_setOpen(openState);
|
|
43
|
+
}
|
|
44
|
+
// This sets the cookie to keep the sidebar state.
|
|
45
|
+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
|
46
|
+
}, [setOpenProp, open]);
|
|
47
|
+
// Helper to toggle the sidebar.
|
|
48
|
+
const toggleSidebar = React.useCallback(() => {
|
|
49
|
+
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
|
|
50
|
+
}, [isMobile, setOpen, setOpenMobile]);
|
|
51
|
+
// Adds a keyboard shortcut to toggle the sidebar.
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
const handleKeyDown = (event) => {
|
|
54
|
+
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
|
|
55
|
+
(event.metaKey || event.ctrlKey)) {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
toggleSidebar();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
61
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
62
|
+
}, [toggleSidebar]);
|
|
63
|
+
// We add a state so that we can do data-state="expanded" or "collapsed".
|
|
64
|
+
// This makes it easier to style the sidebar with Tailwind classes.
|
|
65
|
+
const state = open ? "expanded" : "collapsed";
|
|
66
|
+
const contextValue = React.useMemo(() => ({
|
|
67
|
+
state,
|
|
68
|
+
open,
|
|
69
|
+
setOpen,
|
|
70
|
+
isMobile,
|
|
71
|
+
openMobile,
|
|
72
|
+
setOpenMobile,
|
|
73
|
+
toggleSidebar,
|
|
74
|
+
}), [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]);
|
|
75
|
+
return (_jsx(SidebarContext.Provider, { value: contextValue, children: _jsx(TooltipProvider, { delayDuration: 0, children: _jsx("div", { "data-slot": "sidebar-wrapper", style: {
|
|
76
|
+
"--sidebar-width": SIDEBAR_WIDTH,
|
|
77
|
+
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
|
78
|
+
...style,
|
|
79
|
+
}, className: cn("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", className), ...props, children: children }) }) }));
|
|
80
|
+
}
|
|
81
|
+
function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, ...props }) {
|
|
82
|
+
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
83
|
+
if (collapsible === "none") {
|
|
84
|
+
return (_jsx("div", { "data-slot": "sidebar", className: cn("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", className), ...props, children: children }));
|
|
85
|
+
}
|
|
86
|
+
if (isMobile) {
|
|
87
|
+
return (_jsx(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: _jsxs(SheetContent, { "data-sidebar": "sidebar", "data-slot": "sidebar", "data-mobile": "true", className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden", style: {
|
|
88
|
+
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
|
89
|
+
}, side: side, children: [_jsxs(SheetHeader, { className: "sr-only", children: [_jsx(SheetTitle, { children: "Sidebar" }), _jsx(SheetDescription, { children: "Displays the mobile sidebar." })] }), _jsx("div", { className: "flex h-full w-full flex-col", children: children })] }) }));
|
|
90
|
+
}
|
|
91
|
+
return (_jsxs("div", { className: "group peer hidden text-sidebar-foreground md:block", "data-state": state, "data-collapsible": state === "collapsed" ? collapsible : "", "data-variant": variant, "data-side": side, "data-slot": "sidebar", children: [_jsx("div", { "data-slot": "sidebar-gap", className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", variant === "floating" || variant === "inset"
|
|
92
|
+
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
|
|
93
|
+
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon)") }), _jsx("div", { "data-slot": "sidebar-container", className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex", side === "left"
|
|
94
|
+
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
|
95
|
+
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
96
|
+
// Adjust the padding for floating and inset variants.
|
|
97
|
+
variant === "floating" || variant === "inset"
|
|
98
|
+
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
|
99
|
+
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className), ...props, children: _jsx("div", { "data-sidebar": "sidebar", "data-slot": "sidebar-inner", className: "flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm", children: children }) })] }));
|
|
100
|
+
}
|
|
101
|
+
function SidebarTrigger({ className, onClick, ...props }) {
|
|
102
|
+
const { toggleSidebar } = useSidebar();
|
|
103
|
+
return (_jsxs(Button, { "data-sidebar": "trigger", "data-slot": "sidebar-trigger", variant: "ghost", size: "icon", className: cn("size-7", className), onClick: (event) => {
|
|
104
|
+
onClick?.(event);
|
|
105
|
+
toggleSidebar();
|
|
106
|
+
}, ...props, children: [_jsx(IconLayoutSidebar, {}), _jsx("span", { className: "sr-only", children: "Toggle Sidebar" })] }));
|
|
107
|
+
}
|
|
108
|
+
function SidebarRail({ className, ...props }) {
|
|
109
|
+
const { toggleSidebar } = useSidebar();
|
|
110
|
+
return (_jsx("button", { "data-sidebar": "rail", "data-slot": "sidebar-rail", "aria-label": "Toggle Sidebar", tabIndex: -1, onClick: toggleSidebar, title: "Toggle Sidebar", className: cn("absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar", "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", className), ...props }));
|
|
111
|
+
}
|
|
112
|
+
function SidebarInset({ className, ...props }) {
|
|
113
|
+
return (_jsx("main", { "data-slot": "sidebar-inset", className: cn("relative flex w-full flex-1 flex-col bg-background", "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", className), ...props }));
|
|
114
|
+
}
|
|
115
|
+
function SidebarInput({ className, ...props }) {
|
|
116
|
+
return (_jsx(Input, { "data-slot": "sidebar-input", "data-sidebar": "input", className: cn("h-8 w-full bg-background shadow-none", className), ...props }));
|
|
117
|
+
}
|
|
118
|
+
function SidebarHeader({ className, ...props }) {
|
|
119
|
+
return (_jsx("div", { "data-slot": "sidebar-header", "data-sidebar": "header", className: cn("flex flex-col gap-2 p-2", className), ...props }));
|
|
120
|
+
}
|
|
121
|
+
function SidebarFooter({ className, ...props }) {
|
|
122
|
+
return (_jsx("div", { "data-slot": "sidebar-footer", "data-sidebar": "footer", className: cn("flex flex-col gap-2 p-2", className), ...props }));
|
|
123
|
+
}
|
|
124
|
+
function SidebarSeparator({ className, ...props }) {
|
|
125
|
+
return (_jsx(Separator, { "data-slot": "sidebar-separator", "data-sidebar": "separator", className: cn("mx-2 w-auto bg-sidebar-border", className), ...props }));
|
|
126
|
+
}
|
|
127
|
+
function SidebarContent({ className, ...props }) {
|
|
128
|
+
return (_jsx("div", { "data-slot": "sidebar-content", "data-sidebar": "content", className: cn("flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", className), ...props }));
|
|
129
|
+
}
|
|
130
|
+
function SidebarGroup({ className, ...props }) {
|
|
131
|
+
return (_jsx("div", { "data-slot": "sidebar-group", "data-sidebar": "group", className: cn("relative flex w-full min-w-0 flex-col p-2", className), ...props }));
|
|
132
|
+
}
|
|
133
|
+
function SidebarGroupLabel({ className, asChild = false, ...props }) {
|
|
134
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
135
|
+
return (_jsx(Comp, { "data-slot": "sidebar-group-label", "data-sidebar": "group-label", className: cn("flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", className), ...props }));
|
|
136
|
+
}
|
|
137
|
+
function SidebarGroupAction({ className, asChild = false, ...props }) {
|
|
138
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
139
|
+
return (_jsx(Comp, { "data-slot": "sidebar-group-action", "data-sidebar": "group-action", className: cn("absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
140
|
+
// Increases the hit area of the button on mobile.
|
|
141
|
+
"after:absolute after:-inset-2 md:after:hidden", "group-data-[collapsible=icon]:hidden", className), ...props }));
|
|
142
|
+
}
|
|
143
|
+
function SidebarGroupContent({ className, ...props }) {
|
|
144
|
+
return (_jsx("div", { "data-slot": "sidebar-group-content", "data-sidebar": "group-content", className: cn("w-full text-sm", className), ...props }));
|
|
145
|
+
}
|
|
146
|
+
function SidebarMenu({ className, ...props }) {
|
|
147
|
+
return (_jsx("ul", { "data-slot": "sidebar-menu", "data-sidebar": "menu", className: cn("flex w-full min-w-0 flex-col gap-1", className), ...props }));
|
|
148
|
+
}
|
|
149
|
+
function SidebarMenuItem({ className, ...props }) {
|
|
150
|
+
return (_jsx("li", { "data-slot": "sidebar-menu-item", "data-sidebar": "menu-item", className: cn("group/menu-item relative", className), ...props }));
|
|
151
|
+
}
|
|
152
|
+
const sidebarMenuButtonVariants = cva("peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", {
|
|
153
|
+
variants: {
|
|
154
|
+
variant: {
|
|
155
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
156
|
+
outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
|
|
157
|
+
},
|
|
158
|
+
size: {
|
|
159
|
+
default: "h-8 text-sm",
|
|
160
|
+
sm: "h-7 text-xs",
|
|
161
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
defaultVariants: {
|
|
165
|
+
variant: "default",
|
|
166
|
+
size: "default",
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
function SidebarMenuButton({ asChild = false, isActive = false, variant = "default", size = "default", tooltip, className, ...props }) {
|
|
170
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
171
|
+
const { isMobile, state } = useSidebar();
|
|
172
|
+
const button = (_jsx(Comp, { "data-slot": "sidebar-menu-button", "data-sidebar": "menu-button", "data-size": size, "data-active": isActive, className: cn(sidebarMenuButtonVariants({ variant, size }), className), ...props }));
|
|
173
|
+
if (!tooltip) {
|
|
174
|
+
return button;
|
|
175
|
+
}
|
|
176
|
+
if (typeof tooltip === "string") {
|
|
177
|
+
tooltip = {
|
|
178
|
+
children: tooltip,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: button }), _jsx(TooltipContent, { side: "right", align: "center", hidden: state !== "collapsed" || isMobile, ...tooltip })] }));
|
|
182
|
+
}
|
|
183
|
+
function SidebarMenuAction({ className, asChild = false, showOnHover = false, ...props }) {
|
|
184
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
185
|
+
return (_jsx(Comp, { "data-slot": "sidebar-menu-action", "data-sidebar": "menu-action", className: cn("absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
186
|
+
// Increases the hit area of the button on mobile.
|
|
187
|
+
"after:absolute after:-inset-2 md:after:hidden", "peer-data-[size=sm]/menu-button:top-1", "peer-data-[size=default]/menu-button:top-1.5", "peer-data-[size=lg]/menu-button:top-2.5", "group-data-[collapsible=icon]:hidden", showOnHover &&
|
|
188
|
+
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground data-[state=open]:opacity-100 md:opacity-0", className), ...props }));
|
|
189
|
+
}
|
|
190
|
+
function SidebarMenuBadge({ className, ...props }) {
|
|
191
|
+
return (_jsx("div", { "data-slot": "sidebar-menu-badge", "data-sidebar": "menu-badge", className: cn("pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none", "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", "peer-data-[size=sm]/menu-button:top-1", "peer-data-[size=default]/menu-button:top-1.5", "peer-data-[size=lg]/menu-button:top-2.5", "group-data-[collapsible=icon]:hidden", className), ...props }));
|
|
192
|
+
}
|
|
193
|
+
function SidebarMenuSkeleton({ className, showIcon = false, ...props }) {
|
|
194
|
+
// Fixed width: a random one would differ between server and client render.
|
|
195
|
+
const width = "70%";
|
|
196
|
+
return (_jsxs("div", { "data-slot": "sidebar-menu-skeleton", "data-sidebar": "menu-skeleton", className: cn("flex h-8 items-center gap-2 rounded-md px-2", className), ...props, children: [showIcon && (_jsx(Skeleton, { className: "size-4 rounded-md", "data-sidebar": "menu-skeleton-icon" })), _jsx(Skeleton, { className: "h-4 max-w-(--skeleton-width) flex-1", "data-sidebar": "menu-skeleton-text", style: {
|
|
197
|
+
"--skeleton-width": width,
|
|
198
|
+
} })] }));
|
|
199
|
+
}
|
|
200
|
+
function SidebarMenuSub({ className, ...props }) {
|
|
201
|
+
return (_jsx("ul", { "data-slot": "sidebar-menu-sub", "data-sidebar": "menu-sub", className: cn("mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", "group-data-[collapsible=icon]:hidden", className), ...props }));
|
|
202
|
+
}
|
|
203
|
+
function SidebarMenuSubItem({ className, ...props }) {
|
|
204
|
+
return (_jsx("li", { "data-slot": "sidebar-menu-sub-item", "data-sidebar": "menu-sub-item", className: cn("group/menu-sub-item relative", className), ...props }));
|
|
205
|
+
}
|
|
206
|
+
function SidebarMenuSubButton({ asChild = false, size = "md", isActive = false, className, ...props }) {
|
|
207
|
+
const Comp = asChild ? Slot.Root : "a";
|
|
208
|
+
return (_jsx(Comp, { "data-slot": "sidebar-menu-sub-button", "data-sidebar": "menu-sub-button", "data-size": size, "data-active": isActive, className: cn("flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", size === "sm" && "text-xs", size === "md" && "text-sm", "group-data-[collapsible=icon]:hidden", className), ...props }));
|
|
209
|
+
}
|
|
210
|
+
export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
label?: string;
|
|
4
|
+
busy?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Shared accessible Skeleton component.
|
|
8
|
+
* Tailwind only, respects motion-reduce with motion-reduce:animate-none,
|
|
9
|
+
* role="status", screen-reader text, aria-busy.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Skeleton({ className, label, busy, "aria-hidden": ariaHidden, children, ...props }: SkeletonProps): React.JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Shared accessible Skeleton component.
|
|
5
|
+
* Tailwind only, respects motion-reduce with motion-reduce:animate-none,
|
|
6
|
+
* role="status", screen-reader text, aria-busy.
|
|
7
|
+
*/
|
|
8
|
+
export function Skeleton({ className, label, busy = true, "aria-hidden": ariaHidden, children, ...props }) {
|
|
9
|
+
const isHidden = ariaHidden === true || ariaHidden === "true";
|
|
10
|
+
return (_jsxs("div", { role: isHidden ? undefined : "status", "aria-busy": isHidden ? undefined : busy, "aria-hidden": ariaHidden, "data-skeleton": true, className: cn("animate-pulse rounded-md bg-muted motion-reduce:animate-none", className), ...props, children: [!isHidden && label && _jsx("span", { className: "sr-only", children: label }), children] }));
|
|
11
|
+
}
|