@pichetch08/trip-ui 0.1.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/README.md +46 -0
- package/dist/accordion.d.ts +18 -0
- package/dist/accordion.js +76 -0
- package/dist/agreement-modal.d.ts +16 -0
- package/dist/agreement-modal.js +67 -0
- package/dist/alert.d.ts +13 -0
- package/dist/alert.js +47 -0
- package/dist/auth-hero.d.ts +7 -0
- package/dist/auth-hero.js +63 -0
- package/dist/avatar.d.ts +21 -0
- package/dist/avatar.js +114 -0
- package/dist/badge.d.ts +13 -0
- package/dist/badge.js +36 -0
- package/dist/banner.d.ts +14 -0
- package/dist/banner.js +57 -0
- package/dist/breadcrumb.d.ts +15 -0
- package/dist/breadcrumb.js +37 -0
- package/dist/button.d.ts +15 -0
- package/dist/button.js +81 -0
- package/dist/card.d.ts +30 -0
- package/dist/card.js +66 -0
- package/dist/change-summary-modal.d.ts +35 -0
- package/dist/change-summary-modal.js +128 -0
- package/dist/channel-badge.d.ts +8 -0
- package/dist/channel-badge.js +17 -0
- package/dist/checkbox.d.ts +28 -0
- package/dist/checkbox.js +108 -0
- package/dist/chunk-ORMEWXMH.js +37 -0
- package/dist/color-picker.d.ts +15 -0
- package/dist/color-picker.js +159 -0
- package/dist/confirm-dialog.d.ts +23 -0
- package/dist/confirm-dialog.js +108 -0
- package/dist/copy-button.d.ts +13 -0
- package/dist/copy-button.js +69 -0
- package/dist/dashed-add-button.d.ts +8 -0
- package/dist/dashed-add-button.js +24 -0
- package/dist/data-table.d.ts +27 -0
- package/dist/data-table.js +152 -0
- package/dist/date-picker.d.ts +19 -0
- package/dist/date-picker.js +234 -0
- package/dist/date-range-picker.d.ts +25 -0
- package/dist/date-range-picker.js +456 -0
- package/dist/dev-auto-fill.d.ts +12 -0
- package/dist/dev-auto-fill.js +22 -0
- package/dist/divider.d.ts +10 -0
- package/dist/divider.js +44 -0
- package/dist/drawer.d.ts +16 -0
- package/dist/drawer.js +111 -0
- package/dist/dropdown-menu.d.ts +20 -0
- package/dist/dropdown-menu.js +94 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +24 -0
- package/dist/file-upload.d.ts +32 -0
- package/dist/file-upload.js +212 -0
- package/dist/filter-tabs.d.ts +16 -0
- package/dist/filter-tabs.js +30 -0
- package/dist/footer-action-bar.d.ts +21 -0
- package/dist/footer-action-bar.js +95 -0
- package/dist/form-input.d.ts +16 -0
- package/dist/form-input.js +58 -0
- package/dist/form-textarea.d.ts +13 -0
- package/dist/form-textarea.js +41 -0
- package/dist/icon-button.d.ts +12 -0
- package/dist/icon-button.js +54 -0
- package/dist/icon-picker.d.ts +15 -0
- package/dist/icon-picker.js +311 -0
- package/dist/icon-wrapper.d.ts +15 -0
- package/dist/icon-wrapper.js +52 -0
- package/dist/image-upload.d.ts +24 -0
- package/dist/image-upload.js +122 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +155 -0
- package/dist/kbd.d.ts +15 -0
- package/dist/kbd.js +27 -0
- package/dist/mobile-preview.d.ts +36 -0
- package/dist/mobile-preview.js +167 -0
- package/dist/modal.d.ts +19 -0
- package/dist/modal.js +110 -0
- package/dist/multi-select.d.ts +30 -0
- package/dist/multi-select.js +261 -0
- package/dist/number-input.d.ts +21 -0
- package/dist/number-input.js +129 -0
- package/dist/otp-input.d.ts +13 -0
- package/dist/otp-input.js +114 -0
- package/dist/page-header.d.ts +15 -0
- package/dist/page-header.js +43 -0
- package/dist/page-state.d.ts +14 -0
- package/dist/page-state.js +29 -0
- package/dist/pagination.d.ts +20 -0
- package/dist/pagination.js +87 -0
- package/dist/popover.d.ts +11 -0
- package/dist/popover.js +70 -0
- package/dist/preview-drawer.d.ts +33 -0
- package/dist/preview-drawer.js +74 -0
- package/dist/progress-bar.d.ts +15 -0
- package/dist/progress-bar.js +56 -0
- package/dist/qr-code-display.d.ts +10 -0
- package/dist/qr-code-display.js +43 -0
- package/dist/radio-group.d.ts +19 -0
- package/dist/radio-group.js +78 -0
- package/dist/rating.d.ts +12 -0
- package/dist/rating.js +123 -0
- package/dist/rich-editor.d.ts +13 -0
- package/dist/rich-editor.js +97 -0
- package/dist/search-bar.d.ts +14 -0
- package/dist/search-bar.js +64 -0
- package/dist/section-header.d.ts +12 -0
- package/dist/section-header.js +41 -0
- package/dist/segmented-control.d.ts +24 -0
- package/dist/segmented-control.js +38 -0
- package/dist/select-picker.d.ts +24 -0
- package/dist/select-picker.js +157 -0
- package/dist/skeleton.d.ts +14 -0
- package/dist/skeleton.js +53 -0
- package/dist/slider.d.ts +17 -0
- package/dist/slider.js +151 -0
- package/dist/spinner.d.ts +13 -0
- package/dist/spinner.js +38 -0
- package/dist/stat-card.d.ts +20 -0
- package/dist/stat-card.js +87 -0
- package/dist/stats-summary.d.ts +13 -0
- package/dist/stats-summary.js +28 -0
- package/dist/status-badge.d.ts +19 -0
- package/dist/status-badge.js +41 -0
- package/dist/stepper.d.ts +12 -0
- package/dist/stepper.js +89 -0
- package/dist/tabs.d.ts +18 -0
- package/dist/tabs.js +70 -0
- package/dist/tag.d.ts +23 -0
- package/dist/tag.js +158 -0
- package/dist/time-picker.d.ts +19 -0
- package/dist/time-picker.js +222 -0
- package/dist/timeline.d.ts +15 -0
- package/dist/timeline.js +49 -0
- package/dist/toast.d.ts +18 -0
- package/dist/toast.js +108 -0
- package/dist/toggle-switch.d.ts +12 -0
- package/dist/toggle-switch.js +34 -0
- package/dist/tooltip.d.ts +9 -0
- package/dist/tooltip.js +69 -0
- package/dist/trip-day-map-lazy.d.ts +15 -0
- package/dist/trip-day-map-lazy.js +16 -0
- package/dist/trip-day-map.d.ts +15 -0
- package/dist/trip-day-map.js +62 -0
- package/package.json +73 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
__objRest,
|
|
4
|
+
__spreadValues
|
|
5
|
+
} from "./chunk-ORMEWXMH.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { forwardRef } from "react";
|
|
8
|
+
const FormTextarea = forwardRef(
|
|
9
|
+
(_a, ref) => {
|
|
10
|
+
var _b = _a, { label, error, required, hint, inputId, id, className = "", requiredText = "(\u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19)" } = _b, props = __objRest(_b, ["label", "error", "required", "hint", "inputId", "id", "className", "requiredText"]);
|
|
11
|
+
var _a2;
|
|
12
|
+
const resolvedId = (_a2 = inputId != null ? inputId : id) != null ? _a2 : label ? label.replace(/\s+/g, "-").toLowerCase() : void 0;
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
14
|
+
label && /* @__PURE__ */ jsxs("label", { htmlFor: resolvedId, className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
|
|
15
|
+
label,
|
|
16
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", "aria-hidden": "true", children: "*" }),
|
|
17
|
+
required && /* @__PURE__ */ jsx("span", { className: "sr-only", children: requiredText })
|
|
18
|
+
] }),
|
|
19
|
+
/* @__PURE__ */ jsx(
|
|
20
|
+
"textarea",
|
|
21
|
+
__spreadValues({
|
|
22
|
+
ref,
|
|
23
|
+
id: resolvedId,
|
|
24
|
+
"aria-required": required,
|
|
25
|
+
"aria-invalid": error ? true : void 0,
|
|
26
|
+
"aria-describedby": error ? `${resolvedId}-error` : hint ? `${resolvedId}-hint` : void 0,
|
|
27
|
+
className: `w-full bg-surface-container-low border rounded-xl py-4 px-6 focus-visible:bg-surface focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:border-primary transition-[border-color,box-shadow,background-color] text-on-surface font-medium placeholder:text-outline/40 outline-none resize-y ${error ? "border-red-400 bg-red-50/30" : "border-transparent"} ${className}`
|
|
28
|
+
}, props)
|
|
29
|
+
),
|
|
30
|
+
hint && !error && /* @__PURE__ */ jsx("p", { id: `${resolvedId}-hint`, className: "text-[11px] text-on-surface-variant px-1", children: hint }),
|
|
31
|
+
error && /* @__PURE__ */ jsxs("p", { id: `${resolvedId}-error`, className: "text-xs text-red-500 px-1 flex items-center gap-1", role: "alert", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", "aria-hidden": "true", children: "error" }),
|
|
33
|
+
error
|
|
34
|
+
] })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
FormTextarea.displayName = "FormTextarea";
|
|
39
|
+
export {
|
|
40
|
+
FormTextarea
|
|
41
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
|
|
3
|
+
type IconButtonVariant = "default" | "ghost" | "primary" | "danger";
|
|
4
|
+
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
icon: string;
|
|
6
|
+
variant?: IconButtonVariant;
|
|
7
|
+
size?: "sm" | "md";
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const IconButton: React$1.ForwardRefExoticComponent<IconButtonProps & React$1.RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
|
|
12
|
+
export { IconButton, type IconButtonVariant };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
__objRest,
|
|
4
|
+
__spreadProps,
|
|
5
|
+
__spreadValues
|
|
6
|
+
} from "./chunk-ORMEWXMH.js";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
import { forwardRef } from "react";
|
|
9
|
+
const variants = {
|
|
10
|
+
default: "bg-surface-container-low text-on-surface-variant hover:bg-surface-container hover:text-on-surface",
|
|
11
|
+
ghost: "bg-transparent text-on-surface-variant hover:bg-surface-container hover:text-on-surface",
|
|
12
|
+
primary: "bg-surface-container-low text-primary hover:bg-primary hover:text-on-primary",
|
|
13
|
+
danger: "bg-surface-container-low text-on-surface-variant hover:bg-red-50 hover:text-red-600"
|
|
14
|
+
};
|
|
15
|
+
const sizes = {
|
|
16
|
+
sm: "w-8 h-8 text-base",
|
|
17
|
+
md: "w-10 h-10 text-xl"
|
|
18
|
+
};
|
|
19
|
+
const IconButton = forwardRef(
|
|
20
|
+
(_a, ref) => {
|
|
21
|
+
var _b = _a, { icon, variant = "default", size = "md", loading = false, className = "", disabled } = _b, props = __objRest(_b, ["icon", "variant", "size", "loading", "className", "disabled"]);
|
|
22
|
+
return /* @__PURE__ */ jsx(
|
|
23
|
+
"button",
|
|
24
|
+
__spreadProps(__spreadValues({
|
|
25
|
+
ref,
|
|
26
|
+
type: "button",
|
|
27
|
+
disabled: disabled || loading,
|
|
28
|
+
className: [
|
|
29
|
+
"rounded-full flex items-center justify-center shrink-0",
|
|
30
|
+
"transition-all duration-150",
|
|
31
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
|
|
32
|
+
"active:scale-[0.92]",
|
|
33
|
+
"disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none",
|
|
34
|
+
sizes[size],
|
|
35
|
+
variants[variant],
|
|
36
|
+
className
|
|
37
|
+
].filter(Boolean).join(" ")
|
|
38
|
+
}, props), {
|
|
39
|
+
children: /* @__PURE__ */ jsx(
|
|
40
|
+
"span",
|
|
41
|
+
{
|
|
42
|
+
className: `material-symbols-outlined leading-none ${loading ? "animate-spin" : ""}`,
|
|
43
|
+
"aria-hidden": "true",
|
|
44
|
+
children: loading ? "progress_activity" : icon
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
IconButton.displayName = "IconButton";
|
|
52
|
+
export {
|
|
53
|
+
IconButton
|
|
54
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface IconPickerProps {
|
|
2
|
+
value: string;
|
|
3
|
+
onChange: (value: string) => void;
|
|
4
|
+
open: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
title?: string;
|
|
7
|
+
emptyText?: string;
|
|
8
|
+
searchPlaceholder?: string;
|
|
9
|
+
searchLabel?: string;
|
|
10
|
+
closeAriaLabel?: string;
|
|
11
|
+
categories?: Record<string, string[]>;
|
|
12
|
+
}
|
|
13
|
+
declare function IconPicker({ value, onChange, open, onClose, title, emptyText, searchPlaceholder, searchLabel, closeAriaLabel, categories, }: IconPickerProps): React.ReactNode;
|
|
14
|
+
|
|
15
|
+
export { IconPicker };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./chunk-ORMEWXMH.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useState, useRef, useEffect } from "react";
|
|
5
|
+
import { IconButton } from "./icon-button";
|
|
6
|
+
const ICON_LIBRARIES = [
|
|
7
|
+
{ id: "material", label: "Material Symbols", prefix: "" }
|
|
8
|
+
];
|
|
9
|
+
const MATERIAL_CATEGORIES = {
|
|
10
|
+
"\u0E17\u0E31\u0E48\u0E27\u0E44\u0E1B": [
|
|
11
|
+
"dashboard",
|
|
12
|
+
"home",
|
|
13
|
+
"settings",
|
|
14
|
+
"search",
|
|
15
|
+
"menu",
|
|
16
|
+
"close",
|
|
17
|
+
"add",
|
|
18
|
+
"remove",
|
|
19
|
+
"check",
|
|
20
|
+
"check_circle",
|
|
21
|
+
"error",
|
|
22
|
+
"warning",
|
|
23
|
+
"info",
|
|
24
|
+
"help",
|
|
25
|
+
"visibility",
|
|
26
|
+
"visibility_off",
|
|
27
|
+
"refresh",
|
|
28
|
+
"more_vert",
|
|
29
|
+
"more_horiz",
|
|
30
|
+
"arrow_back",
|
|
31
|
+
"arrow_forward",
|
|
32
|
+
"expand_more",
|
|
33
|
+
"expand_less",
|
|
34
|
+
"launch",
|
|
35
|
+
"open_in_new"
|
|
36
|
+
],
|
|
37
|
+
"\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49": [
|
|
38
|
+
"person",
|
|
39
|
+
"group",
|
|
40
|
+
"people",
|
|
41
|
+
"admin_panel_settings",
|
|
42
|
+
"manage_accounts",
|
|
43
|
+
"badge",
|
|
44
|
+
"account_circle",
|
|
45
|
+
"face",
|
|
46
|
+
"supervisor_account",
|
|
47
|
+
"person_add",
|
|
48
|
+
"person_remove",
|
|
49
|
+
"groups",
|
|
50
|
+
"support_agent",
|
|
51
|
+
"engineering",
|
|
52
|
+
"verified_user"
|
|
53
|
+
],
|
|
54
|
+
"\u0E01\u0E32\u0E23\u0E19\u0E33\u0E17\u0E32\u0E07": [
|
|
55
|
+
"route",
|
|
56
|
+
"flight_takeoff",
|
|
57
|
+
"flight_land",
|
|
58
|
+
"directions_car",
|
|
59
|
+
"directions_bus",
|
|
60
|
+
"train",
|
|
61
|
+
"directions_boat",
|
|
62
|
+
"airport_shuttle",
|
|
63
|
+
"luggage",
|
|
64
|
+
"map",
|
|
65
|
+
"location_on",
|
|
66
|
+
"explore",
|
|
67
|
+
"near_me",
|
|
68
|
+
"navigation",
|
|
69
|
+
"travel_explore",
|
|
70
|
+
"landscape"
|
|
71
|
+
],
|
|
72
|
+
"\u0E01\u0E32\u0E23\u0E40\u0E07\u0E34\u0E19": [
|
|
73
|
+
"payments",
|
|
74
|
+
"credit_card",
|
|
75
|
+
"account_balance",
|
|
76
|
+
"receipt_long",
|
|
77
|
+
"paid",
|
|
78
|
+
"price_check",
|
|
79
|
+
"currency_exchange",
|
|
80
|
+
"savings",
|
|
81
|
+
"shopping_cart",
|
|
82
|
+
"card_membership",
|
|
83
|
+
"layers",
|
|
84
|
+
"speed",
|
|
85
|
+
"trending_up",
|
|
86
|
+
"trending_down"
|
|
87
|
+
],
|
|
88
|
+
"\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32": [
|
|
89
|
+
"article",
|
|
90
|
+
"description",
|
|
91
|
+
"text_snippet",
|
|
92
|
+
"edit_note",
|
|
93
|
+
"post_add",
|
|
94
|
+
"photo_library",
|
|
95
|
+
"image",
|
|
96
|
+
"video_library",
|
|
97
|
+
"folder",
|
|
98
|
+
"cloud_upload",
|
|
99
|
+
"file_copy",
|
|
100
|
+
"attach_file",
|
|
101
|
+
"link",
|
|
102
|
+
"campaign",
|
|
103
|
+
"newspaper"
|
|
104
|
+
],
|
|
105
|
+
"\u0E01\u0E32\u0E23\u0E2A\u0E37\u0E48\u0E2D\u0E2A\u0E32\u0E23": [
|
|
106
|
+
"mail",
|
|
107
|
+
"chat",
|
|
108
|
+
"forum",
|
|
109
|
+
"comment",
|
|
110
|
+
"notifications",
|
|
111
|
+
"notifications_active",
|
|
112
|
+
"send",
|
|
113
|
+
"call",
|
|
114
|
+
"contact_phone",
|
|
115
|
+
"sms",
|
|
116
|
+
"inbox",
|
|
117
|
+
"forward_to_inbox",
|
|
118
|
+
"mark_email_read",
|
|
119
|
+
"announcement",
|
|
120
|
+
"share"
|
|
121
|
+
],
|
|
122
|
+
"\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25 & \u0E27\u0E34\u0E40\u0E04\u0E23\u0E32\u0E30\u0E2B\u0E4C": [
|
|
123
|
+
"analytics",
|
|
124
|
+
"assessment",
|
|
125
|
+
"bar_chart",
|
|
126
|
+
"pie_chart",
|
|
127
|
+
"show_chart",
|
|
128
|
+
"timeline",
|
|
129
|
+
"insights",
|
|
130
|
+
"summarize",
|
|
131
|
+
"leaderboard",
|
|
132
|
+
"query_stats",
|
|
133
|
+
"monitoring",
|
|
134
|
+
"data_usage",
|
|
135
|
+
"storage",
|
|
136
|
+
"database",
|
|
137
|
+
"table_chart"
|
|
138
|
+
],
|
|
139
|
+
"\u0E23\u0E30\u0E1A\u0E1A": [
|
|
140
|
+
"settings",
|
|
141
|
+
"tune",
|
|
142
|
+
"build",
|
|
143
|
+
"code",
|
|
144
|
+
"terminal",
|
|
145
|
+
"dns",
|
|
146
|
+
"cloud",
|
|
147
|
+
"security",
|
|
148
|
+
"lock",
|
|
149
|
+
"vpn_key",
|
|
150
|
+
"api",
|
|
151
|
+
"integration_instructions",
|
|
152
|
+
"monitor_heart",
|
|
153
|
+
"health_and_safety",
|
|
154
|
+
"shield",
|
|
155
|
+
"task_alt"
|
|
156
|
+
],
|
|
157
|
+
"\u0E01\u0E32\u0E23\u0E08\u0E31\u0E14\u0E01\u0E32\u0E23": [
|
|
158
|
+
"confirmation_number",
|
|
159
|
+
"assignment",
|
|
160
|
+
"checklist",
|
|
161
|
+
"event",
|
|
162
|
+
"calendar_month",
|
|
163
|
+
"schedule",
|
|
164
|
+
"alarm",
|
|
165
|
+
"bookmark",
|
|
166
|
+
"star",
|
|
167
|
+
"flag",
|
|
168
|
+
"label",
|
|
169
|
+
"category",
|
|
170
|
+
"inventory_2",
|
|
171
|
+
"package_2",
|
|
172
|
+
"local_shipping"
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
function IconPicker({
|
|
176
|
+
value,
|
|
177
|
+
onChange,
|
|
178
|
+
open,
|
|
179
|
+
onClose,
|
|
180
|
+
title = "\u0E40\u0E25\u0E37\u0E2D\u0E01 Icon",
|
|
181
|
+
emptyText = "\u0E44\u0E21\u0E48\u0E1E\u0E1A icon",
|
|
182
|
+
searchPlaceholder = "\u0E04\u0E49\u0E19\u0E2B\u0E32 icon...",
|
|
183
|
+
searchLabel = "\u0E04\u0E49\u0E19\u0E2B\u0E32 icon",
|
|
184
|
+
closeAriaLabel = "\u0E1B\u0E34\u0E14",
|
|
185
|
+
categories
|
|
186
|
+
}) {
|
|
187
|
+
const activeCategories = categories != null ? categories : MATERIAL_CATEGORIES;
|
|
188
|
+
const [library, setLibrary] = useState("material");
|
|
189
|
+
const [category, setCategory] = useState(Object.keys(activeCategories)[0]);
|
|
190
|
+
const [search, setSearch] = useState("");
|
|
191
|
+
const dialogRef = useRef(null);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
var _a;
|
|
194
|
+
if (!open) return;
|
|
195
|
+
(_a = dialogRef.current) == null ? void 0 : _a.focus();
|
|
196
|
+
const handleKeyDown = (e) => {
|
|
197
|
+
if (e.key === "Escape") {
|
|
198
|
+
onClose();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (e.key === "Tab") {
|
|
202
|
+
const dialog = dialogRef.current;
|
|
203
|
+
if (!dialog) return;
|
|
204
|
+
const focusable = dialog.querySelectorAll('button, input, [tabindex]:not([tabindex="-1"])');
|
|
205
|
+
const first = focusable[0];
|
|
206
|
+
const last = focusable[focusable.length - 1];
|
|
207
|
+
if (e.shiftKey) {
|
|
208
|
+
if (document.activeElement === first) {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
last == null ? void 0 : last.focus();
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
if (document.activeElement === last) {
|
|
214
|
+
e.preventDefault();
|
|
215
|
+
first == null ? void 0 : first.focus();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
221
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
222
|
+
}, [open, onClose]);
|
|
223
|
+
if (!open) return null;
|
|
224
|
+
const categoryKeys = Object.keys(activeCategories);
|
|
225
|
+
const icons = search ? Object.values(activeCategories).flat().filter((icon) => icon.includes(search.toLowerCase())) : activeCategories[category] || [];
|
|
226
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
|
|
227
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/40 backdrop-blur-sm cursor-pointer", onClick: onClose }),
|
|
228
|
+
/* @__PURE__ */ jsxs(
|
|
229
|
+
"div",
|
|
230
|
+
{
|
|
231
|
+
ref: dialogRef,
|
|
232
|
+
role: "dialog",
|
|
233
|
+
"aria-modal": "true",
|
|
234
|
+
"aria-labelledby": "icon-picker-title",
|
|
235
|
+
tabIndex: -1,
|
|
236
|
+
className: "relative bg-surface rounded-2xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col overflow-hidden focus:outline-none",
|
|
237
|
+
children: [
|
|
238
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-outline-variant/30", children: [
|
|
239
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
240
|
+
/* @__PURE__ */ jsx("h3", { id: "icon-picker-title", className: "text-lg font-bold text-on-surface", children: title }),
|
|
241
|
+
value && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
242
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined text-lg text-primary", children: value }),
|
|
243
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-on-surface-variant", children: value })
|
|
244
|
+
] })
|
|
245
|
+
] }),
|
|
246
|
+
/* @__PURE__ */ jsx(IconButton, { icon: "close", variant: "ghost", size: "sm", onClick: onClose, "aria-label": closeAriaLabel })
|
|
247
|
+
] }),
|
|
248
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-5 py-3 border-b border-outline-variant/30", children: [
|
|
249
|
+
ICON_LIBRARIES.map((lib) => /* @__PURE__ */ jsx(
|
|
250
|
+
"button",
|
|
251
|
+
{
|
|
252
|
+
type: "button",
|
|
253
|
+
onClick: () => setLibrary(lib.id),
|
|
254
|
+
"aria-pressed": library === lib.id,
|
|
255
|
+
className: `px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors ${library === lib.id ? "bg-primary text-on-primary" : "bg-surface-container text-on-surface-variant hover:bg-surface-container-high"}`,
|
|
256
|
+
children: lib.label
|
|
257
|
+
},
|
|
258
|
+
lib.id
|
|
259
|
+
)),
|
|
260
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1 ml-2", children: [
|
|
261
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-on-surface-variant text-lg", children: "search" }),
|
|
262
|
+
/* @__PURE__ */ jsx(
|
|
263
|
+
"input",
|
|
264
|
+
{
|
|
265
|
+
type: "search",
|
|
266
|
+
"aria-label": searchLabel,
|
|
267
|
+
value: search,
|
|
268
|
+
onChange: (e) => setSearch(e.target.value),
|
|
269
|
+
placeholder: searchPlaceholder,
|
|
270
|
+
className: "w-full pl-10 pr-4 py-1.5 bg-surface-container-low border border-outline-variant rounded-lg text-sm outline-none focus:border-primary transition-[border-color]"
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
] })
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
276
|
+
!search && /* @__PURE__ */ jsx("div", { className: "w-40 shrink-0 border-r border-outline-variant/30 overflow-y-auto py-2", children: categoryKeys.map((cat) => /* @__PURE__ */ jsx(
|
|
277
|
+
"button",
|
|
278
|
+
{
|
|
279
|
+
type: "button",
|
|
280
|
+
onClick: () => setCategory(cat),
|
|
281
|
+
className: `w-full text-left px-4 py-2 text-xs font-medium transition-colors ${category === cat ? "text-primary bg-primary/10 font-semibold" : "text-on-surface-variant hover:bg-surface-container-low"}`,
|
|
282
|
+
children: cat
|
|
283
|
+
},
|
|
284
|
+
cat
|
|
285
|
+
)) }),
|
|
286
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children: icons.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-on-surface-variant text-sm", children: emptyText }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 gap-1", children: icons.map((icon) => /* @__PURE__ */ jsxs(
|
|
287
|
+
"button",
|
|
288
|
+
{
|
|
289
|
+
type: "button",
|
|
290
|
+
onClick: () => {
|
|
291
|
+
onChange(icon);
|
|
292
|
+
onClose();
|
|
293
|
+
},
|
|
294
|
+
"aria-label": icon,
|
|
295
|
+
className: `flex flex-col items-center gap-1 p-2 rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${value === icon ? "bg-primary/10 ring-2 ring-primary" : "hover:bg-surface-container"}`,
|
|
296
|
+
children: [
|
|
297
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined text-2xl text-on-surface", children: icon }),
|
|
298
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-[8px] text-on-surface-variant truncate w-full text-center", children: icon })
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
icon
|
|
302
|
+
)) }) })
|
|
303
|
+
] })
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
] });
|
|
308
|
+
}
|
|
309
|
+
export {
|
|
310
|
+
IconPicker
|
|
311
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type AppColor = "primary" | "emerald" | "amber" | "rose" | "red" | "violet" | "blue" | "slate" | "cyan";
|
|
2
|
+
/** @deprecated use AppColor */
|
|
3
|
+
type IconWrapperColor = AppColor;
|
|
4
|
+
interface IconWrapperProps {
|
|
5
|
+
icon: string;
|
|
6
|
+
color?: AppColor;
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
filled?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const appColorMap: Record<AppColor, string>;
|
|
12
|
+
declare const appColorFg: Record<AppColor, string>;
|
|
13
|
+
declare function IconWrapper({ icon, color, size, filled, className, }: IconWrapperProps): React.ReactNode;
|
|
14
|
+
|
|
15
|
+
export { type AppColor, IconWrapper, type IconWrapperColor, appColorFg, appColorMap };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "./chunk-ORMEWXMH.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
const sizeMap = {
|
|
4
|
+
sm: { wrapper: "w-8 h-8 rounded-lg", icon: "text-lg" },
|
|
5
|
+
md: { wrapper: "w-10 h-10 rounded-xl", icon: "text-xl" },
|
|
6
|
+
lg: { wrapper: "w-12 h-12 rounded-2xl", icon: "text-3xl" }
|
|
7
|
+
};
|
|
8
|
+
const appColorMap = {
|
|
9
|
+
primary: "bg-primary/10 text-primary",
|
|
10
|
+
emerald: "bg-emerald-100 text-emerald-600",
|
|
11
|
+
amber: "bg-amber-100 text-amber-600",
|
|
12
|
+
rose: "bg-rose-100 text-rose-600",
|
|
13
|
+
red: "bg-red-100 text-red-600",
|
|
14
|
+
violet: "bg-violet-100 text-violet-600",
|
|
15
|
+
blue: "bg-blue-100 text-blue-600",
|
|
16
|
+
slate: "bg-surface-container text-on-surface-variant",
|
|
17
|
+
cyan: "bg-cyan-100 text-cyan-600"
|
|
18
|
+
};
|
|
19
|
+
const appColorFg = {
|
|
20
|
+
primary: "text-primary",
|
|
21
|
+
emerald: "text-emerald-600",
|
|
22
|
+
amber: "text-amber-600",
|
|
23
|
+
rose: "text-rose-600",
|
|
24
|
+
red: "text-red-600",
|
|
25
|
+
violet: "text-violet-600",
|
|
26
|
+
blue: "text-blue-600",
|
|
27
|
+
slate: "text-on-surface-variant",
|
|
28
|
+
cyan: "text-cyan-600"
|
|
29
|
+
};
|
|
30
|
+
function IconWrapper({
|
|
31
|
+
icon,
|
|
32
|
+
color = "primary",
|
|
33
|
+
size = "md",
|
|
34
|
+
filled = false,
|
|
35
|
+
className = ""
|
|
36
|
+
}) {
|
|
37
|
+
const s = sizeMap[size];
|
|
38
|
+
return /* @__PURE__ */ jsx("div", { className: `${s.wrapper} flex items-center justify-center ${appColorMap[color]} ${className}`, children: /* @__PURE__ */ jsx(
|
|
39
|
+
"span",
|
|
40
|
+
{
|
|
41
|
+
"aria-hidden": "true",
|
|
42
|
+
className: `material-symbols-outlined ${s.icon}`,
|
|
43
|
+
style: filled ? { fontVariationSettings: "'FILL' 1" } : void 0,
|
|
44
|
+
children: icon
|
|
45
|
+
}
|
|
46
|
+
) });
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
IconWrapper,
|
|
50
|
+
appColorFg,
|
|
51
|
+
appColorMap
|
|
52
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface ImageUploadProps {
|
|
2
|
+
value?: string | null;
|
|
3
|
+
onChange?: (url: string | null) => void;
|
|
4
|
+
onUpload: (file: File) => Promise<string>;
|
|
5
|
+
onUploadProgress?: (progress: number) => void;
|
|
6
|
+
onBrowseLibrary?: () => void;
|
|
7
|
+
label?: string;
|
|
8
|
+
hint?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
accept?: string;
|
|
11
|
+
maxSizeMB?: number;
|
|
12
|
+
aspect?: "square" | "video" | "wide";
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
uploadLabel?: string;
|
|
15
|
+
libraryLabel?: string;
|
|
16
|
+
deleteLabel?: string;
|
|
17
|
+
uploadingText?: string;
|
|
18
|
+
invalidTypeText?: string;
|
|
19
|
+
maxSizeErrorText?: (mb: number) => string;
|
|
20
|
+
uploadFailText?: string;
|
|
21
|
+
}
|
|
22
|
+
declare function ImageUpload({ value, onChange, onUpload, onUploadProgress, onBrowseLibrary, label, hint, error: externalError, accept, maxSizeMB, aspect, disabled, uploadLabel, libraryLabel, deleteLabel, uploadingText, invalidTypeText, maxSizeErrorText, uploadFailText, }: ImageUploadProps): React.ReactNode;
|
|
23
|
+
|
|
24
|
+
export { ImageUpload };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./chunk-ORMEWXMH.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useRef, useState } from "react";
|
|
5
|
+
import { Button } from "./button";
|
|
6
|
+
function ImageUpload({
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
onUpload,
|
|
10
|
+
onUploadProgress,
|
|
11
|
+
onBrowseLibrary,
|
|
12
|
+
label,
|
|
13
|
+
hint,
|
|
14
|
+
error: externalError,
|
|
15
|
+
accept = "image/jpeg,image/png,image/webp,image/gif",
|
|
16
|
+
maxSizeMB = 5,
|
|
17
|
+
aspect = "square",
|
|
18
|
+
disabled = false,
|
|
19
|
+
uploadLabel = "\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14",
|
|
20
|
+
libraryLabel = "\u0E04\u0E25\u0E31\u0E07\u0E2A\u0E37\u0E48\u0E2D",
|
|
21
|
+
deleteLabel = "\u0E25\u0E1A",
|
|
22
|
+
uploadingText = "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14...",
|
|
23
|
+
invalidTypeText = "\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E44\u0E1F\u0E25\u0E4C\u0E44\u0E21\u0E48\u0E23\u0E2D\u0E07\u0E23\u0E31\u0E1A",
|
|
24
|
+
maxSizeErrorText = (mb) => `\u0E02\u0E19\u0E32\u0E14\u0E44\u0E1F\u0E25\u0E4C\u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${mb} MB`,
|
|
25
|
+
uploadFailText = "\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14\u0E44\u0E21\u0E48\u0E2A\u0E33\u0E40\u0E23\u0E47\u0E08 \u0E01\u0E23\u0E38\u0E13\u0E32\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48"
|
|
26
|
+
}) {
|
|
27
|
+
const inputRef = useRef(null);
|
|
28
|
+
const inputId = useRef(`image-upload-${Math.random().toString(36).slice(2)}`).current;
|
|
29
|
+
const [uploading, setUploading] = useState(false);
|
|
30
|
+
const [internalError, setInternalError] = useState(null);
|
|
31
|
+
const [hovering, setHovering] = useState(false);
|
|
32
|
+
const error = externalError != null ? externalError : internalError;
|
|
33
|
+
const containerClass = aspect === "square" ? "w-32 h-32" : "aspect-video w-full";
|
|
34
|
+
const handleFileChange = async (e) => {
|
|
35
|
+
var _a;
|
|
36
|
+
const file = (_a = e.target.files) == null ? void 0 : _a[0];
|
|
37
|
+
if (!file) return;
|
|
38
|
+
setInternalError(null);
|
|
39
|
+
const acceptedTypes = accept.split(",").map((t) => t.trim());
|
|
40
|
+
if (!acceptedTypes.includes(file.type)) {
|
|
41
|
+
setInternalError(invalidTypeText);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (file.size > maxSizeMB * 1024 * 1024) {
|
|
45
|
+
setInternalError(maxSizeErrorText(maxSizeMB));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
setUploading(true);
|
|
50
|
+
onUploadProgress == null ? void 0 : onUploadProgress(0);
|
|
51
|
+
const url = await onUpload(file);
|
|
52
|
+
onUploadProgress == null ? void 0 : onUploadProgress(100);
|
|
53
|
+
onChange == null ? void 0 : onChange(url);
|
|
54
|
+
} catch (e2) {
|
|
55
|
+
setInternalError(uploadFailText);
|
|
56
|
+
} finally {
|
|
57
|
+
setUploading(false);
|
|
58
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const handleRemove = () => {
|
|
62
|
+
onChange == null ? void 0 : onChange(null);
|
|
63
|
+
setInternalError(null);
|
|
64
|
+
};
|
|
65
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
66
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "text-sm font-semibold text-on-surface", children: label }),
|
|
67
|
+
/* @__PURE__ */ jsxs(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
className: `relative border-2 border-dashed rounded-2xl overflow-hidden bg-surface transition-colors ${containerClass} ${error ? "border-red-400" : "border-outline-variant/40"} ${disabled ? "opacity-50 cursor-not-allowed" : ""}`,
|
|
71
|
+
onMouseEnter: () => setHovering(true),
|
|
72
|
+
onMouseLeave: () => setHovering(false),
|
|
73
|
+
children: [
|
|
74
|
+
uploading ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 bg-surface", children: [
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined animate-spin text-primary", style: { fontSize: "28px" }, children: "progress_activity" }),
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant font-medium", children: uploadingText })
|
|
77
|
+
] }) : value ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
78
|
+
/* @__PURE__ */ jsx("img", { src: value, alt: "uploaded", className: "w-full h-full object-cover" }),
|
|
79
|
+
hovering && !disabled && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-2 bg-black/40", children: [
|
|
80
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", icon: "upload", onClick: () => {
|
|
81
|
+
var _a;
|
|
82
|
+
return (_a = inputRef.current) == null ? void 0 : _a.click();
|
|
83
|
+
}, children: uploadLabel }),
|
|
84
|
+
onBrowseLibrary && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", icon: "photo_library", onClick: onBrowseLibrary, children: libraryLabel }),
|
|
85
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "danger", icon: "delete", onClick: handleRemove, children: deleteLabel })
|
|
86
|
+
] })
|
|
87
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 p-3 text-center", children: [
|
|
88
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-on-surface-variant", style: { fontSize: "32px" }, children: "add_photo_alternate" }),
|
|
89
|
+
hint && aspect !== "square" && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: hint }),
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
91
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "primary", disabled, onClick: () => {
|
|
92
|
+
var _a;
|
|
93
|
+
return (_a = inputRef.current) == null ? void 0 : _a.click();
|
|
94
|
+
}, children: uploadLabel }),
|
|
95
|
+
onBrowseLibrary && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled, onClick: onBrowseLibrary, children: libraryLabel })
|
|
96
|
+
] })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
"input",
|
|
100
|
+
{
|
|
101
|
+
ref: inputRef,
|
|
102
|
+
id: inputId,
|
|
103
|
+
type: "file",
|
|
104
|
+
accept,
|
|
105
|
+
disabled,
|
|
106
|
+
className: "hidden",
|
|
107
|
+
onChange: handleFileChange
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
hint && aspect === "square" && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: hint }),
|
|
114
|
+
error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 flex items-center gap-1", children: [
|
|
115
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "14px" }, children: "error" }),
|
|
116
|
+
error
|
|
117
|
+
] })
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
ImageUpload
|
|
122
|
+
};
|