najm-kit 2.11.23 → 2.12.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/CHANGELOG.md +21 -12
- package/README.md +28 -1
- package/dist/NIcon-D73-C51g.d.ts +21 -0
- package/dist/{NNotFoundState-BmGDW2Bx.d.ts → NNotFoundState-Z2n2gbhS.d.ts} +1 -16
- package/dist/{NajmUIProvider-BwL5HFIV.d.ts → NajmUIProvider-C9IJW1ya.d.ts} +2 -19
- package/dist/adapters/app.d.ts +6 -4
- package/dist/adapters/app.mjs +9 -6
- package/dist/adapters/next.d.ts +2 -1
- package/dist/adapters/next.mjs +5 -3
- package/dist/chunk-5UY7U2ED.mjs +9 -0
- package/dist/chunk-A34LRQYI.mjs +7 -0
- package/dist/chunk-BFFTC7LD.mjs +66 -0
- package/dist/{chunk-5ZTOCWRA.mjs → chunk-F32O3WRX.mjs} +7 -197
- package/dist/chunk-FZX3DONZ.mjs +200 -0
- package/dist/chunk-GYPQLOQ5.mjs +2202 -0
- package/dist/{chunk-BDQK6ECA.mjs → chunk-LMW4DM5L.mjs} +43 -101
- package/dist/{chunk-DKHIQFXX.mjs → chunk-QDFLHSWO.mjs} +1 -1
- package/dist/{chunk-5JVFMOLI.mjs → chunk-QJ22WAQY.mjs} +2 -28
- package/dist/{chunk-LK3SMYUP.mjs → chunk-TZRTOQCC.mjs} +1 -6
- package/dist/chunk-VZXMMC72.mjs +31 -0
- package/dist/{chunk-M5VGBZ7R.mjs → chunk-WHGRN7PP.mjs} +2 -1
- package/dist/formFill-CQkTA0JS.d.ts +17 -0
- package/dist/index.d.ts +20 -436
- package/dist/index.mjs +338 -1950
- package/dist/json.mjs +5 -3
- package/dist/location/google.d.ts +14 -0
- package/dist/location/google.mjs +184 -0
- package/dist/location/index.d.ts +33 -0
- package/dist/location/index.mjs +9 -0
- package/dist/location/leaflet.d.ts +13 -0
- package/dist/location/leaflet.mjs +168 -0
- package/dist/theme.css +23 -2
- package/dist/types-BQBx0RZd.d.ts +578 -0
- package/package.json +30 -3
|
@@ -0,0 +1,2202 @@
|
|
|
1
|
+
import { NLoadingState } from './chunk-FZX3DONZ.mjs';
|
|
2
|
+
import { NajmScroll } from './chunk-QJ22WAQY.mjs';
|
|
3
|
+
import { inputBorderClasses, Button, surfaceBorderClasses, NIcon } from './chunk-WHGRN7PP.mjs';
|
|
4
|
+
import { useNajmComponentStyle, NajmThemeContainerCtx } from './chunk-TZRTOQCC.mjs';
|
|
5
|
+
import { cn } from './chunk-5UY7U2ED.mjs';
|
|
6
|
+
import { resolveRadiusValue } from './chunk-TFHWLE7N.mjs';
|
|
7
|
+
import * as React12 from 'react';
|
|
8
|
+
import React12__default, { createContext, useContext, useState, useCallback, useMemo } from 'react';
|
|
9
|
+
import { MapPin, CircleAlert, MapPinCheck, X, Search, Plus, Minus, Crosshair, LoaderCircle, Trash2, Check, XIcon, Menu } from 'lucide-react';
|
|
10
|
+
import { cva } from 'class-variance-authority';
|
|
11
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
12
|
+
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
|
13
|
+
import { create } from 'zustand';
|
|
14
|
+
import { FormProvider, Controller, useFormContext } from 'react-hook-form';
|
|
15
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
16
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
17
|
+
|
|
18
|
+
// src/theme/focus.ts
|
|
19
|
+
var focusRingClasses = "outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50";
|
|
20
|
+
var focusRingWithinClasses = "focus-visible:ring-[3px] focus-visible:ring-ring/50 has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-ring/50";
|
|
21
|
+
var NO_SHADE = /* @__PURE__ */ new Set(["black", "white"]);
|
|
22
|
+
var PRESETS = {
|
|
23
|
+
muted: "border-muted-foreground",
|
|
24
|
+
primary: "border-primary",
|
|
25
|
+
accent: "border-accent",
|
|
26
|
+
success: "border-green-600",
|
|
27
|
+
warning: "border-amber-600",
|
|
28
|
+
destructive: "border-red-600"
|
|
29
|
+
};
|
|
30
|
+
var inputVariants = cva(
|
|
31
|
+
"relative w-full flex items-center h-10 bg-card text-foreground overflow-hidden transition-colors",
|
|
32
|
+
{
|
|
33
|
+
variants: {
|
|
34
|
+
variant: {
|
|
35
|
+
default: "p-2 rounded-md",
|
|
36
|
+
rounded: "p-2 rounded-full",
|
|
37
|
+
ghost: "p-0! !bg-transparent !bg-none"
|
|
38
|
+
},
|
|
39
|
+
status: {
|
|
40
|
+
default: "",
|
|
41
|
+
error: ""
|
|
42
|
+
},
|
|
43
|
+
hasIcon: { true: "pl-8", false: "pl-3" },
|
|
44
|
+
disabled: { true: "cursor-not-allowed", false: "" }
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: { variant: "default", status: "default", hasIcon: false, disabled: false }
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
var ERROR_BORDER = "border !border-red-600";
|
|
50
|
+
var ERROR_FOCUS_RING = "focus-visible:ring-destructive/20 has-[:focus-visible]:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:has-[:focus-visible]:ring-destructive/40";
|
|
51
|
+
var FOCUS_INTERACTIVE_BASE = "border focus-within:border-primary";
|
|
52
|
+
var HOVER_INTERACTIVE_BASE = "hover:border-primary";
|
|
53
|
+
var STATIC_BASE = "border";
|
|
54
|
+
var BaseInput = React12__default.forwardRef(
|
|
55
|
+
({ children, variant = "default", status = "default", bordered, borderColor, className, style, disabled = false, onHover, onClick, hasIcon, ...rest }, ref) => {
|
|
56
|
+
const recipe = useNajmComponentStyle("input");
|
|
57
|
+
const isGhost = variant === "ghost";
|
|
58
|
+
const isError = status === "error";
|
|
59
|
+
const hasExplicitColor = !!borderColor;
|
|
60
|
+
const isBordered = bordered !== false;
|
|
61
|
+
const recipeRadius = variant === "default" ? resolveRadiusValue(recipe?.radius) : void 0;
|
|
62
|
+
let colorClass = "";
|
|
63
|
+
let borderClass = "";
|
|
64
|
+
if (!isGhost) {
|
|
65
|
+
if (isError) {
|
|
66
|
+
borderClass = ERROR_BORDER;
|
|
67
|
+
} else if (hasExplicitColor) {
|
|
68
|
+
borderClass = `${STATIC_BASE} ${FOCUS_INTERACTIVE_BASE} ${HOVER_INTERACTIVE_BASE}`;
|
|
69
|
+
colorClass = PRESETS[borderColor] ?? (NO_SHADE.has(borderColor) ? `border-${borderColor}` : `border-${borderColor}-600`);
|
|
70
|
+
} else {
|
|
71
|
+
const restingBorderClass = inputBorderClasses(isBordered);
|
|
72
|
+
borderClass = isBordered ? `${STATIC_BASE} ${restingBorderClass} [&:not(:focus-within):not([data-state=open]):hover]:border-input focus-within:border-ring data-[state=open]:border-ring` : restingBorderClass;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return /* @__PURE__ */ jsx(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
"data-bordered": !isGhost && !isBordered ? "false" : !isGhost ? "true" : void 0,
|
|
80
|
+
style: recipeRadius ? { borderRadius: recipeRadius, ...style } : style,
|
|
81
|
+
className: cn(
|
|
82
|
+
inputVariants({ variant, status, hasIcon, disabled }),
|
|
83
|
+
// The wrapper is what the user reads as "the field": the inner
|
|
84
|
+
// control is stripped of its own ring precisely so this element can
|
|
85
|
+
// carry the border. Until this was added, the border merely changed
|
|
86
|
+
// colour on focus — a 1px hue shift and nothing else — and the number
|
|
87
|
+
// fields and the multi-select trigger came out of the acceptance walk
|
|
88
|
+
// as tab stops with no visible indicator at all.
|
|
89
|
+
!isGhost && focusRingWithinClasses,
|
|
90
|
+
!isGhost && isError && ERROR_FOCUS_RING,
|
|
91
|
+
!isGhost && borderClass,
|
|
92
|
+
!isGhost && colorClass,
|
|
93
|
+
className
|
|
94
|
+
),
|
|
95
|
+
onMouseEnter: disabled ? void 0 : onHover,
|
|
96
|
+
onClick: disabled ? void 0 : onClick,
|
|
97
|
+
...rest,
|
|
98
|
+
children
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
BaseInput.displayName = "BaseInput";
|
|
104
|
+
var variantClasses = {
|
|
105
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
106
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
107
|
+
ghost: "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
108
|
+
destructive: "bg-transparent text-destructive hover:bg-destructive/10"
|
|
109
|
+
};
|
|
110
|
+
var sizeClasses = {
|
|
111
|
+
sm: "h-6 w-6",
|
|
112
|
+
md: "h-8 w-8",
|
|
113
|
+
lg: "h-10 w-10"
|
|
114
|
+
};
|
|
115
|
+
var IconButton = React12.forwardRef(
|
|
116
|
+
({
|
|
117
|
+
className,
|
|
118
|
+
variant = "ghost",
|
|
119
|
+
size = "md",
|
|
120
|
+
active = false,
|
|
121
|
+
type = "button",
|
|
122
|
+
children,
|
|
123
|
+
style,
|
|
124
|
+
...props
|
|
125
|
+
}, ref) => {
|
|
126
|
+
const recipe = useNajmComponentStyle("button");
|
|
127
|
+
const recipeRadius = resolveRadiusValue(recipe?.radius);
|
|
128
|
+
return /* @__PURE__ */ jsx(
|
|
129
|
+
"button",
|
|
130
|
+
{
|
|
131
|
+
ref,
|
|
132
|
+
type,
|
|
133
|
+
"aria-pressed": active || void 0,
|
|
134
|
+
style: recipeRadius ? { borderRadius: recipeRadius, ...style } : style,
|
|
135
|
+
className: cn(
|
|
136
|
+
"inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors",
|
|
137
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
138
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
139
|
+
variantClasses[variant],
|
|
140
|
+
sizeClasses[size],
|
|
141
|
+
active && "bg-accent text-accent-foreground",
|
|
142
|
+
className
|
|
143
|
+
),
|
|
144
|
+
...props,
|
|
145
|
+
children
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
IconButton.displayName = "IconButton";
|
|
151
|
+
var Input = React12.forwardRef(function Input2({ className, type, ...props }, ref) {
|
|
152
|
+
return /* @__PURE__ */ jsx(
|
|
153
|
+
"input",
|
|
154
|
+
{
|
|
155
|
+
ref,
|
|
156
|
+
type,
|
|
157
|
+
"data-slot": "input",
|
|
158
|
+
className: cn(
|
|
159
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-card px-3 py-1 text-base text-foreground shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
160
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
161
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
162
|
+
className
|
|
163
|
+
),
|
|
164
|
+
...props
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// src/location/contracts.ts
|
|
170
|
+
var DEFAULT_LOCATION_VALUE = {
|
|
171
|
+
address: "",
|
|
172
|
+
latitude: null,
|
|
173
|
+
longitude: null
|
|
174
|
+
};
|
|
175
|
+
function isCompleteCoordinatePair(value) {
|
|
176
|
+
const { latitude, longitude } = value;
|
|
177
|
+
return typeof latitude === "number" && Number.isFinite(latitude) && latitude >= -90 && latitude <= 90 && typeof longitude === "number" && Number.isFinite(longitude) && longitude >= -180 && longitude <= 180;
|
|
178
|
+
}
|
|
179
|
+
function normalizeLocationValue(value) {
|
|
180
|
+
const address = typeof value?.address === "string" ? value.address : "";
|
|
181
|
+
if (!isCompleteCoordinatePair({ latitude: value?.latitude ?? null, longitude: value?.longitude ?? null })) {
|
|
182
|
+
return { address, latitude: null, longitude: null };
|
|
183
|
+
}
|
|
184
|
+
return { address, latitude: value.latitude, longitude: value.longitude };
|
|
185
|
+
}
|
|
186
|
+
function locationCoordinates(value) {
|
|
187
|
+
return isCompleteCoordinatePair(value) ? { latitude: value.latitude, longitude: value.longitude } : null;
|
|
188
|
+
}
|
|
189
|
+
function moveCoordinates(value, latitudeDelta, longitudeDelta) {
|
|
190
|
+
return {
|
|
191
|
+
latitude: Math.max(-90, Math.min(90, value.latitude + latitudeDelta)),
|
|
192
|
+
longitude: Math.max(-180, Math.min(180, value.longitude + longitudeDelta))
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function Sheet({ ...props }) {
|
|
196
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
197
|
+
}
|
|
198
|
+
function SheetTrigger({ ...props }) {
|
|
199
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
200
|
+
}
|
|
201
|
+
function SheetClose({ ...props }) {
|
|
202
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", ...props });
|
|
203
|
+
}
|
|
204
|
+
function SheetPortal({ ...props }) {
|
|
205
|
+
const container = React12.useContext(NajmThemeContainerCtx);
|
|
206
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Portal, { "data-slot": "sheet-portal", container: container ?? void 0, ...props });
|
|
207
|
+
}
|
|
208
|
+
function SheetOverlay({ className, ...props }) {
|
|
209
|
+
return /* @__PURE__ */ jsx(
|
|
210
|
+
SheetPrimitive.Overlay,
|
|
211
|
+
{
|
|
212
|
+
"data-slot": "sheet-overlay",
|
|
213
|
+
className: cn(
|
|
214
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
215
|
+
className
|
|
216
|
+
),
|
|
217
|
+
...props
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
var sheetVariants = cva(
|
|
222
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 fixed z-50 gap-4 shadow-lg transition ease-in-out border-border",
|
|
223
|
+
{
|
|
224
|
+
variants: {
|
|
225
|
+
side: {
|
|
226
|
+
top: "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b",
|
|
227
|
+
bottom: "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t",
|
|
228
|
+
left: "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
|
229
|
+
right: "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
defaultVariants: { side: "right" }
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
function SheetContent({ className, portalClassName, side, children, ...props }) {
|
|
236
|
+
return /* @__PURE__ */ jsx(SheetPortal, { children: /* @__PURE__ */ jsxs("div", { className: portalClassName, children: [
|
|
237
|
+
/* @__PURE__ */ jsx(SheetOverlay, {}),
|
|
238
|
+
/* @__PURE__ */ jsxs(
|
|
239
|
+
SheetPrimitive.Content,
|
|
240
|
+
{
|
|
241
|
+
"data-slot": "sheet-content",
|
|
242
|
+
className: cn(sheetVariants({ side }), className),
|
|
243
|
+
...props,
|
|
244
|
+
children: [
|
|
245
|
+
children,
|
|
246
|
+
/* @__PURE__ */ jsxs(SheetPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 end-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [
|
|
247
|
+
/* @__PURE__ */ jsx(X, { className: "size-4" }),
|
|
248
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
249
|
+
] })
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
] }) });
|
|
254
|
+
}
|
|
255
|
+
function SheetHeader({ className, ...props }) {
|
|
256
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-6", className), ...props });
|
|
257
|
+
}
|
|
258
|
+
function SheetFooter({ className, ...props }) {
|
|
259
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "sheet-footer", className: cn("mt-auto flex flex-col gap-2 p-6", className), ...props });
|
|
260
|
+
}
|
|
261
|
+
function SheetTitle({ className, ...props }) {
|
|
262
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Title, { "data-slot": "sheet-title", className: cn("text-foreground font-semibold", className), ...props });
|
|
263
|
+
}
|
|
264
|
+
function SheetDescription({ className, ...props }) {
|
|
265
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Description, { "data-slot": "sheet-description", className: cn("text-muted-foreground text-sm", className), ...props });
|
|
266
|
+
}
|
|
267
|
+
var PortalScopeContext = createContext(void 0);
|
|
268
|
+
function NPortalScopeProvider({ className, children }) {
|
|
269
|
+
return /* @__PURE__ */ jsx(PortalScopeContext.Provider, { value: className, children });
|
|
270
|
+
}
|
|
271
|
+
var useNPortalScope = () => useContext(PortalScopeContext);
|
|
272
|
+
var responsiveSectionPadding = "px-3 py-3 lg:px-3 2xl:px-4";
|
|
273
|
+
function NSheet({
|
|
274
|
+
open,
|
|
275
|
+
onOpenChange,
|
|
276
|
+
icon: Icon,
|
|
277
|
+
title,
|
|
278
|
+
description,
|
|
279
|
+
width = 480,
|
|
280
|
+
side = "right",
|
|
281
|
+
portalClassName,
|
|
282
|
+
classNames,
|
|
283
|
+
bodyClassName,
|
|
284
|
+
contentClassName,
|
|
285
|
+
children,
|
|
286
|
+
footer
|
|
287
|
+
}) {
|
|
288
|
+
const inheritedScope = useNPortalScope();
|
|
289
|
+
const portal = portalClassName ?? inheritedScope;
|
|
290
|
+
const style = {
|
|
291
|
+
width: `min(${width}px, 95vw)`,
|
|
292
|
+
maxWidth: `${width}px`
|
|
293
|
+
};
|
|
294
|
+
return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
295
|
+
SheetContent,
|
|
296
|
+
{
|
|
297
|
+
side,
|
|
298
|
+
portalClassName: portal,
|
|
299
|
+
className: cn(
|
|
300
|
+
"flex h-dvh max-h-dvh flex-col gap-0 overflow-hidden bg-background p-0 text-foreground",
|
|
301
|
+
contentClassName,
|
|
302
|
+
classNames?.content
|
|
303
|
+
),
|
|
304
|
+
style,
|
|
305
|
+
children: [
|
|
306
|
+
/* @__PURE__ */ jsx(
|
|
307
|
+
SheetHeader,
|
|
308
|
+
{
|
|
309
|
+
className: cn(
|
|
310
|
+
"shrink-0 border-b border-border p-0",
|
|
311
|
+
responsiveSectionPadding,
|
|
312
|
+
classNames?.header
|
|
313
|
+
),
|
|
314
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 lg:gap-3 xl:gap-3 2xl:gap-4", children: [
|
|
315
|
+
/* @__PURE__ */ jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-primary/10", children: /* @__PURE__ */ jsx(Icon, { className: "size-4 text-primary" }) }),
|
|
316
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
317
|
+
/* @__PURE__ */ jsx(SheetTitle, { className: "truncate text-sm font-semibold text-foreground", children: title }),
|
|
318
|
+
description && /* @__PURE__ */ jsx(SheetDescription, { className: "truncate text-xs text-muted-foreground", children: description })
|
|
319
|
+
] })
|
|
320
|
+
] })
|
|
321
|
+
}
|
|
322
|
+
),
|
|
323
|
+
/* @__PURE__ */ jsx(NajmScroll, { axis: "y", className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
324
|
+
"div",
|
|
325
|
+
{
|
|
326
|
+
"data-slot": "sheet-body",
|
|
327
|
+
className: cn(responsiveSectionPadding, bodyClassName, classNames?.body),
|
|
328
|
+
children
|
|
329
|
+
}
|
|
330
|
+
) }),
|
|
331
|
+
footer && /* @__PURE__ */ jsx(
|
|
332
|
+
"div",
|
|
333
|
+
{
|
|
334
|
+
"data-slot": "sheet-footer",
|
|
335
|
+
className: cn(
|
|
336
|
+
"shrink-0 border-t border-border",
|
|
337
|
+
responsiveSectionPadding,
|
|
338
|
+
classNames?.footer
|
|
339
|
+
),
|
|
340
|
+
children: footer
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
) });
|
|
346
|
+
}
|
|
347
|
+
function Dialog({ ...props }) {
|
|
348
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
349
|
+
}
|
|
350
|
+
function DialogTrigger({ ...props }) {
|
|
351
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
352
|
+
}
|
|
353
|
+
function DialogPortal({ ...props }) {
|
|
354
|
+
const container = React12.useContext(NajmThemeContainerCtx);
|
|
355
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Portal, { "data-slot": "dialog-portal", container: container ?? void 0, ...props });
|
|
356
|
+
}
|
|
357
|
+
function DialogClose({ ...props }) {
|
|
358
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Close, { "data-slot": "dialog-close cursor-pointer", ...props });
|
|
359
|
+
}
|
|
360
|
+
function DialogOverlay({ className, ...props }) {
|
|
361
|
+
return /* @__PURE__ */ jsx(
|
|
362
|
+
SheetPrimitive.Overlay,
|
|
363
|
+
{
|
|
364
|
+
"data-slot": "dialog-overlay",
|
|
365
|
+
className: cn(
|
|
366
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 backdrop-blur-sm",
|
|
367
|
+
className
|
|
368
|
+
),
|
|
369
|
+
...props
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
var dialogPaddingMap = {
|
|
374
|
+
none: "p-0 gap-0 overflow-hidden",
|
|
375
|
+
sm: "p-3",
|
|
376
|
+
md: "p-6",
|
|
377
|
+
lg: "p-8"
|
|
378
|
+
};
|
|
379
|
+
var MOBILE_PADDING_QUERY = "(max-width: 639.98px)";
|
|
380
|
+
function getDefaultPadding() {
|
|
381
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return "md";
|
|
382
|
+
return window.matchMedia(MOBILE_PADDING_QUERY).matches ? "sm" : "md";
|
|
383
|
+
}
|
|
384
|
+
function useResponsivePadding(value) {
|
|
385
|
+
const [resolved, setResolved] = React12.useState(() => value ?? getDefaultPadding());
|
|
386
|
+
React12.useEffect(() => {
|
|
387
|
+
if (value) {
|
|
388
|
+
setResolved(value);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
392
|
+
const mql = window.matchMedia(MOBILE_PADDING_QUERY);
|
|
393
|
+
const update = () => setResolved(mql.matches ? "sm" : "md");
|
|
394
|
+
update();
|
|
395
|
+
mql.addEventListener("change", update);
|
|
396
|
+
return () => mql.removeEventListener("change", update);
|
|
397
|
+
}, [value]);
|
|
398
|
+
return resolved;
|
|
399
|
+
}
|
|
400
|
+
function DialogContent({
|
|
401
|
+
className,
|
|
402
|
+
children,
|
|
403
|
+
padding,
|
|
404
|
+
hideClose = false,
|
|
405
|
+
onOpenAutoFocus,
|
|
406
|
+
onCloseAutoFocus,
|
|
407
|
+
...props
|
|
408
|
+
}) {
|
|
409
|
+
const resolvedPadding = useResponsivePadding(padding);
|
|
410
|
+
const portalClassName = useNPortalScope();
|
|
411
|
+
const openerRef = React12.useRef(null);
|
|
412
|
+
return /* @__PURE__ */ jsx(DialogPortal, { "data-slot": "dialog-portal", children: /* @__PURE__ */ jsxs("div", { className: portalClassName, children: [
|
|
413
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
414
|
+
/* @__PURE__ */ jsxs(
|
|
415
|
+
SheetPrimitive.Content,
|
|
416
|
+
{
|
|
417
|
+
"data-slot": "dialog-content",
|
|
418
|
+
"data-padding": resolvedPadding,
|
|
419
|
+
onOpenAutoFocus: (event) => {
|
|
420
|
+
const active = document.activeElement;
|
|
421
|
+
openerRef.current = active instanceof HTMLElement && active !== document.body ? active : null;
|
|
422
|
+
onOpenAutoFocus?.(event);
|
|
423
|
+
},
|
|
424
|
+
onCloseAutoFocus: (event) => {
|
|
425
|
+
onCloseAutoFocus?.(event);
|
|
426
|
+
if (event.defaultPrevented) return;
|
|
427
|
+
const opener = openerRef.current;
|
|
428
|
+
setTimeout(() => {
|
|
429
|
+
if (!opener?.isConnected) return;
|
|
430
|
+
const active = document.activeElement;
|
|
431
|
+
const lost = !active || active === document.body || active === document.documentElement || !active.isConnected;
|
|
432
|
+
if (lost) opener.focus();
|
|
433
|
+
}, 0);
|
|
434
|
+
},
|
|
435
|
+
className: cn(
|
|
436
|
+
"bg-card text-card-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-sm translate-x-[-50%] translate-y-[-50%] gap-4 rounded-none lg:rounded-lg najm-border border-border shadow-lg duration-200",
|
|
437
|
+
dialogPaddingMap[resolvedPadding],
|
|
438
|
+
className
|
|
439
|
+
),
|
|
440
|
+
...props,
|
|
441
|
+
children: [
|
|
442
|
+
children,
|
|
443
|
+
!hideClose && /* @__PURE__ */ jsxs(SheetPrimitive.Close, { className: "ring-offset-background cursor-pointer focus:ring-ring absolute top-4 end-4 rounded-xs text-muted-foreground hover:text-foreground transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
|
|
444
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
445
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
446
|
+
] })
|
|
447
|
+
]
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
] }) });
|
|
451
|
+
}
|
|
452
|
+
function DialogHeader({ className, ...props }) {
|
|
453
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2 text-center sm:text-start", className), ...props });
|
|
454
|
+
}
|
|
455
|
+
function DialogFooter({ className, ...props }) {
|
|
456
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "dialog-footer", className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props });
|
|
457
|
+
}
|
|
458
|
+
function DialogTitle({ className, ...props }) {
|
|
459
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Title, { "data-slot": "dialog-title", className: cn("text-lg leading-none font-semibold", className), ...props });
|
|
460
|
+
}
|
|
461
|
+
function DialogDescription({ className, ...props }) {
|
|
462
|
+
return /* @__PURE__ */ jsx(SheetPrimitive.Description, { "data-slot": "dialog-description", className: cn("text-muted-foreground text-sm", className), ...props });
|
|
463
|
+
}
|
|
464
|
+
var NSidebarContext = createContext(null);
|
|
465
|
+
function useNSidebar() {
|
|
466
|
+
return useContext(NSidebarContext);
|
|
467
|
+
}
|
|
468
|
+
function NSidebarProvider({
|
|
469
|
+
children,
|
|
470
|
+
defaultCollapsed = false,
|
|
471
|
+
mobileBreakpoint = "md"
|
|
472
|
+
}) {
|
|
473
|
+
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
474
|
+
const [mobileOpen, setMobileOpen] = useState(false);
|
|
475
|
+
const openMobile = useCallback(() => setMobileOpen(true), []);
|
|
476
|
+
const closeMobile = useCallback(() => setMobileOpen(false), []);
|
|
477
|
+
const toggleCollapsed = useCallback(() => setCollapsed((prev) => !prev), []);
|
|
478
|
+
const value = useMemo(
|
|
479
|
+
() => ({
|
|
480
|
+
collapsed,
|
|
481
|
+
mobileOpen,
|
|
482
|
+
openMobile,
|
|
483
|
+
closeMobile,
|
|
484
|
+
setMobileOpen,
|
|
485
|
+
setCollapsed,
|
|
486
|
+
toggleCollapsed,
|
|
487
|
+
mobileBreakpoint
|
|
488
|
+
}),
|
|
489
|
+
[collapsed, mobileOpen, openMobile, closeMobile, toggleCollapsed, mobileBreakpoint]
|
|
490
|
+
);
|
|
491
|
+
return /* @__PURE__ */ jsx(NSidebarContext.Provider, { value, children });
|
|
492
|
+
}
|
|
493
|
+
function NPageHeaderActions({ children, className }) {
|
|
494
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex shrink-0 items-center gap-0 lg:gap-1 xl:gap-2 2xl:gap-2", className), children });
|
|
495
|
+
}
|
|
496
|
+
NPageHeaderActions.displayName = "NPageHeaderActions";
|
|
497
|
+
function NPageHeaderCompactActions({ children, className }) {
|
|
498
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex shrink-0 items-center", className), children });
|
|
499
|
+
}
|
|
500
|
+
NPageHeaderCompactActions.displayName = "NPageHeaderCompactActions";
|
|
501
|
+
function NPageHeaderFilters({ children, className }) {
|
|
502
|
+
return /* @__PURE__ */ jsx("div", { className: cn("py-3 lg:px-3 xl:px-4 2xl:px-5", className), children });
|
|
503
|
+
}
|
|
504
|
+
NPageHeaderFilters.displayName = "NPageHeaderFilters";
|
|
505
|
+
function NPageHeaderTop({ children, className }) {
|
|
506
|
+
return /* @__PURE__ */ jsx("div", { className, children });
|
|
507
|
+
}
|
|
508
|
+
NPageHeaderTop.displayName = "NPageHeaderTop";
|
|
509
|
+
function isPageHeaderSlot(child, slot) {
|
|
510
|
+
return React12__default.isValidElement(child) && child.type === slot;
|
|
511
|
+
}
|
|
512
|
+
function getSlotElements(children, slot) {
|
|
513
|
+
const matches = React12__default.Children.toArray(children).filter((child) => isPageHeaderSlot(child, slot));
|
|
514
|
+
return matches.length > 0 ? matches : void 0;
|
|
515
|
+
}
|
|
516
|
+
var responsiveClasses = {
|
|
517
|
+
sm: {
|
|
518
|
+
main: "sm:flex sm:justify-between",
|
|
519
|
+
identity: "sm:col-start-auto sm:row-start-auto sm:justify-self-auto",
|
|
520
|
+
controls: "sm:col-start-auto sm:row-start-auto sm:justify-self-auto",
|
|
521
|
+
fullActions: "sm:flex",
|
|
522
|
+
compactActions: "sm:hidden",
|
|
523
|
+
sidebarTrigger: "sm:hidden",
|
|
524
|
+
icon: "sm:flex",
|
|
525
|
+
subtitle: "sm:block",
|
|
526
|
+
minH: "sm:min-h-14"
|
|
527
|
+
},
|
|
528
|
+
md: {
|
|
529
|
+
main: "md:flex md:justify-between",
|
|
530
|
+
identity: "md:col-start-auto md:row-start-auto md:justify-self-auto",
|
|
531
|
+
controls: "md:col-start-auto md:row-start-auto md:justify-self-auto",
|
|
532
|
+
fullActions: "md:flex",
|
|
533
|
+
compactActions: "md:hidden",
|
|
534
|
+
sidebarTrigger: "md:hidden",
|
|
535
|
+
icon: "md:flex",
|
|
536
|
+
subtitle: "md:block",
|
|
537
|
+
minH: "md:min-h-14"
|
|
538
|
+
},
|
|
539
|
+
lg: {
|
|
540
|
+
main: "lg:flex lg:justify-between lg:px-3 2xl:px-4",
|
|
541
|
+
identity: "lg:col-start-auto lg:row-start-auto lg:justify-self-auto",
|
|
542
|
+
controls: "lg:col-start-auto lg:row-start-auto lg:justify-self-auto",
|
|
543
|
+
fullActions: "lg:flex",
|
|
544
|
+
compactActions: "lg:hidden",
|
|
545
|
+
sidebarTrigger: "lg:hidden",
|
|
546
|
+
icon: "lg:flex",
|
|
547
|
+
subtitle: "lg:block",
|
|
548
|
+
minH: "lg:min-h-14"
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
function NPageHeader({
|
|
552
|
+
icon: Icon,
|
|
553
|
+
title,
|
|
554
|
+
subtitle,
|
|
555
|
+
actions,
|
|
556
|
+
compactActions,
|
|
557
|
+
filters,
|
|
558
|
+
top,
|
|
559
|
+
mobileBreakpoint: mobileBreakpointProp,
|
|
560
|
+
onSidebarOpen,
|
|
561
|
+
sidebarTriggerLabel = "Open sidebar",
|
|
562
|
+
sidebarTriggerClassName,
|
|
563
|
+
search,
|
|
564
|
+
children,
|
|
565
|
+
className,
|
|
566
|
+
headerClassName,
|
|
567
|
+
card,
|
|
568
|
+
bordered
|
|
569
|
+
}) {
|
|
570
|
+
const [internalSearch, setInternalSearch] = useState("");
|
|
571
|
+
const recipe = useNajmComponentStyle("pageHeader");
|
|
572
|
+
const sidebar = useNSidebar();
|
|
573
|
+
const resolvedOnSidebarOpen = onSidebarOpen ?? sidebar?.openMobile;
|
|
574
|
+
const mobileBreakpoint = mobileBreakpointProp ?? sidebar?.mobileBreakpoint ?? "md";
|
|
575
|
+
const searchValue = search?.value ?? internalSearch;
|
|
576
|
+
const handleSearchChange = useCallback((e) => {
|
|
577
|
+
setInternalSearch(e.currentTarget.value);
|
|
578
|
+
search?.onChange?.(e);
|
|
579
|
+
}, [search?.onChange]);
|
|
580
|
+
const slottedActions = getSlotElements(children, NPageHeaderActions);
|
|
581
|
+
const slottedCompactActions = getSlotElements(children, NPageHeaderCompactActions);
|
|
582
|
+
const slottedFilters = getSlotElements(children, NPageHeaderFilters);
|
|
583
|
+
const slottedTop = getSlotElements(children, NPageHeaderTop);
|
|
584
|
+
const resolvedActions = slottedActions ?? actions;
|
|
585
|
+
const resolvedCompactActions = slottedCompactActions ?? compactActions;
|
|
586
|
+
const resolvedFilters = slottedFilters ?? filters;
|
|
587
|
+
const resolvedTop = slottedTop ?? top;
|
|
588
|
+
const hasCompactActions = resolvedCompactActions !== void 0 && resolvedCompactActions !== null && resolvedCompactActions !== false;
|
|
589
|
+
const breakpointClasses = responsiveClasses[mobileBreakpoint];
|
|
590
|
+
const isCard = card ?? recipe?.card ?? bordered === true;
|
|
591
|
+
const recipeRadius = resolveRadiusValue(recipe?.radius);
|
|
592
|
+
const bleedStyle = isCard ? void 0 : {
|
|
593
|
+
marginTop: "calc(var(--najm-section-gap, 0px) * -1)",
|
|
594
|
+
marginInline: "calc(var(--najm-page-gutter, 0px) * -1)"
|
|
595
|
+
};
|
|
596
|
+
const recipeStyle = recipeRadius || recipe?.borderWidth || bleedStyle ? {
|
|
597
|
+
...bleedStyle,
|
|
598
|
+
...recipeRadius ? { borderRadius: recipeRadius } : {},
|
|
599
|
+
...recipe?.borderWidth ? { borderWidth: recipe.borderWidth } : {}
|
|
600
|
+
} : void 0;
|
|
601
|
+
return /* @__PURE__ */ jsxs(
|
|
602
|
+
"div",
|
|
603
|
+
{
|
|
604
|
+
"data-slot": "page-header",
|
|
605
|
+
"data-card": isCard ? "true" : void 0,
|
|
606
|
+
"data-bordered": bordered === false ? "false" : bordered ? "true" : void 0,
|
|
607
|
+
style: recipeStyle,
|
|
608
|
+
className: cn(
|
|
609
|
+
isCard ? cn("rounded-xl bg-card text-card-foreground shadow-none", surfaceBorderClasses(true)) : cn(
|
|
610
|
+
"border-b bg-background text-foreground",
|
|
611
|
+
surfaceBorderClasses(true, "bottom").replace("najm-border-b", "najm-border-b")
|
|
612
|
+
),
|
|
613
|
+
className
|
|
614
|
+
),
|
|
615
|
+
children: [
|
|
616
|
+
resolvedTop,
|
|
617
|
+
/* @__PURE__ */ jsxs(
|
|
618
|
+
"div",
|
|
619
|
+
{
|
|
620
|
+
"data-slot": "page-header-main",
|
|
621
|
+
className: cn(
|
|
622
|
+
// min-h-14 matches NSidebarHeader so the two bottom rules line up.
|
|
623
|
+
"relative grid min-h-14 grid-cols-[minmax(2.75rem,1fr)_minmax(0,auto)_minmax(2.75rem,1fr)] items-center gap-2 px-2 lg:px-3 2xl:px-4",
|
|
624
|
+
breakpointClasses.main,
|
|
625
|
+
breakpointClasses.minH,
|
|
626
|
+
headerClassName
|
|
627
|
+
),
|
|
628
|
+
children: [
|
|
629
|
+
resolvedOnSidebarOpen && /* @__PURE__ */ jsx(
|
|
630
|
+
Button,
|
|
631
|
+
{
|
|
632
|
+
type: "button",
|
|
633
|
+
variant: "ghost",
|
|
634
|
+
size: "icon",
|
|
635
|
+
onClick: resolvedOnSidebarOpen,
|
|
636
|
+
"aria-label": sidebarTriggerLabel,
|
|
637
|
+
"data-slot": "page-header-sidebar-trigger",
|
|
638
|
+
className: cn(
|
|
639
|
+
"col-start-1 row-start-1 justify-self-start",
|
|
640
|
+
breakpointClasses.sidebarTrigger,
|
|
641
|
+
sidebarTriggerClassName
|
|
642
|
+
),
|
|
643
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
644
|
+
}
|
|
645
|
+
),
|
|
646
|
+
/* @__PURE__ */ jsxs(
|
|
647
|
+
"div",
|
|
648
|
+
{
|
|
649
|
+
"data-slot": "page-header-identity",
|
|
650
|
+
className: cn(
|
|
651
|
+
"col-start-2 row-start-1 flex min-w-0 max-w-full items-center justify-self-center gap-2 lg:gap-3 xl:gap-3 2xl:gap-4",
|
|
652
|
+
breakpointClasses.identity
|
|
653
|
+
),
|
|
654
|
+
children: [
|
|
655
|
+
/* @__PURE__ */ jsx("div", { className: cn("hidden h-8 w-8 shrink-0 rounded-lg bg-primary/10 items-center justify-center", breakpointClasses.icon), children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-primary" }) }),
|
|
656
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
657
|
+
/* @__PURE__ */ jsx("h2", { className: "truncate text-sm font-semibold text-foreground", children: title }),
|
|
658
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn("hidden truncate text-xs leading-relaxed text-muted-foreground sm:text-sm sm:leading-normal", breakpointClasses.subtitle), children: subtitle })
|
|
659
|
+
] })
|
|
660
|
+
]
|
|
661
|
+
}
|
|
662
|
+
),
|
|
663
|
+
/* @__PURE__ */ jsxs(
|
|
664
|
+
"div",
|
|
665
|
+
{
|
|
666
|
+
"data-slot": "page-header-controls",
|
|
667
|
+
className: cn(
|
|
668
|
+
"col-start-3 row-start-1 flex min-w-0 shrink-0 items-center justify-self-end gap-3",
|
|
669
|
+
breakpointClasses.controls
|
|
670
|
+
),
|
|
671
|
+
children: [
|
|
672
|
+
hasCompactActions && /* @__PURE__ */ jsx(
|
|
673
|
+
"div",
|
|
674
|
+
{
|
|
675
|
+
"data-slot": "page-header-compact-actions",
|
|
676
|
+
className: cn("flex items-center", breakpointClasses.compactActions),
|
|
677
|
+
children: slottedCompactActions ? resolvedCompactActions : /* @__PURE__ */ jsx(NPageHeaderCompactActions, { children: resolvedCompactActions })
|
|
678
|
+
}
|
|
679
|
+
),
|
|
680
|
+
/* @__PURE__ */ jsxs(
|
|
681
|
+
"div",
|
|
682
|
+
{
|
|
683
|
+
"data-slot": "page-header-full-actions",
|
|
684
|
+
className: cn(
|
|
685
|
+
"shrink-0 items-center gap-3",
|
|
686
|
+
hasCompactActions ? "hidden" : "flex",
|
|
687
|
+
hasCompactActions && breakpointClasses.fullActions
|
|
688
|
+
),
|
|
689
|
+
children: [
|
|
690
|
+
search && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
691
|
+
/* @__PURE__ */ jsx(Search, { size: 14, className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none" }),
|
|
692
|
+
/* @__PURE__ */ jsx(
|
|
693
|
+
"input",
|
|
694
|
+
{
|
|
695
|
+
ref: search.ref,
|
|
696
|
+
type: "text",
|
|
697
|
+
value: searchValue,
|
|
698
|
+
placeholder: search.placeholder ?? "Search...",
|
|
699
|
+
className: cn(
|
|
700
|
+
"h-9 w-full max-w-[280px] rounded-lg bg-background pl-9 pr-3 text-sm text-foreground placeholder:text-muted-foreground outline-none border border-border focus:border-primary focus:ring-1 focus:ring-primary/20 transition-colors",
|
|
701
|
+
search.className
|
|
702
|
+
),
|
|
703
|
+
onChange: handleSearchChange
|
|
704
|
+
}
|
|
705
|
+
)
|
|
706
|
+
] }),
|
|
707
|
+
resolvedActions && (slottedActions ? resolvedActions : /* @__PURE__ */ jsx(NPageHeaderActions, { children: resolvedActions }))
|
|
708
|
+
]
|
|
709
|
+
}
|
|
710
|
+
)
|
|
711
|
+
]
|
|
712
|
+
}
|
|
713
|
+
)
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
resolvedFilters && (slottedFilters ? resolvedFilters : /* @__PURE__ */ jsx(NPageHeaderFilters, { children: resolvedFilters }))
|
|
718
|
+
]
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
var generateId = () => `dialog-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
723
|
+
var buildButtonConfig = (partial, defaults) => {
|
|
724
|
+
return { ...defaults, ...partial };
|
|
725
|
+
};
|
|
726
|
+
function createDialogStore() {
|
|
727
|
+
return create((set, get) => ({
|
|
728
|
+
dialogs: [],
|
|
729
|
+
pushDialog: (config) => {
|
|
730
|
+
return new Promise((resolve, reject) => {
|
|
731
|
+
const id = generateId();
|
|
732
|
+
const primaryButtonConfig = buildButtonConfig(config.primaryButton, {
|
|
733
|
+
text: "Confirm",
|
|
734
|
+
variant: "default",
|
|
735
|
+
loading: false,
|
|
736
|
+
disabled: false,
|
|
737
|
+
loadingText: "Processing..."
|
|
738
|
+
});
|
|
739
|
+
const secondaryButtonConfig = buildButtonConfig(config.secondaryButton, {
|
|
740
|
+
text: "Cancel",
|
|
741
|
+
variant: "outline",
|
|
742
|
+
loading: false,
|
|
743
|
+
disabled: false,
|
|
744
|
+
loadingText: "Processing..."
|
|
745
|
+
});
|
|
746
|
+
const dialogConfig = {
|
|
747
|
+
id,
|
|
748
|
+
title: config.title,
|
|
749
|
+
description: config.description,
|
|
750
|
+
pageHeader: config.pageHeader,
|
|
751
|
+
padding: config.padding,
|
|
752
|
+
children: config.children,
|
|
753
|
+
primaryButton: primaryButtonConfig,
|
|
754
|
+
secondaryButton: secondaryButtonConfig,
|
|
755
|
+
showButtons: config.showButtons ?? true,
|
|
756
|
+
size: config.size,
|
|
757
|
+
width: config.width,
|
|
758
|
+
height: config.height,
|
|
759
|
+
className: config.className,
|
|
760
|
+
actionMode: config.actionMode ?? "auto",
|
|
761
|
+
render: config.render,
|
|
762
|
+
resolve,
|
|
763
|
+
reject
|
|
764
|
+
};
|
|
765
|
+
set((state) => ({ dialogs: [...state.dialogs, dialogConfig] }));
|
|
766
|
+
});
|
|
767
|
+
},
|
|
768
|
+
popDialog: async (result) => {
|
|
769
|
+
const { dialogs } = get();
|
|
770
|
+
if (dialogs.length === 0) return;
|
|
771
|
+
const dialog = dialogs[dialogs.length - 1];
|
|
772
|
+
const closeCurrentDialog = () => {
|
|
773
|
+
dialog.resolve?.(result);
|
|
774
|
+
set((state) => ({ dialogs: state.dialogs.slice(0, -1) }));
|
|
775
|
+
};
|
|
776
|
+
const handler = dialog.primaryButton?.onClick ?? dialog.primaryButton?.onConfirm;
|
|
777
|
+
if (handler && result != null) {
|
|
778
|
+
get().setPrimaryLoading(true);
|
|
779
|
+
try {
|
|
780
|
+
await handler(result);
|
|
781
|
+
closeCurrentDialog();
|
|
782
|
+
} finally {
|
|
783
|
+
get().setPrimaryLoading(false);
|
|
784
|
+
}
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
dialog.resolve?.(result);
|
|
788
|
+
set((state) => ({ dialogs: state.dialogs.slice(0, -1) }));
|
|
789
|
+
},
|
|
790
|
+
closeDialog: (id, result) => {
|
|
791
|
+
const { dialogs } = get();
|
|
792
|
+
const dialog = dialogs.find((d) => d.id === id);
|
|
793
|
+
if (!dialog) return;
|
|
794
|
+
dialog.resolve?.(result);
|
|
795
|
+
set((state) => ({ dialogs: state.dialogs.filter((d) => d.id !== id) }));
|
|
796
|
+
},
|
|
797
|
+
closeAllDialogs: () => {
|
|
798
|
+
const { dialogs } = get();
|
|
799
|
+
dialogs.forEach((d) => d.reject?.(new Error("All dialogs closed")));
|
|
800
|
+
set({ dialogs: [] });
|
|
801
|
+
},
|
|
802
|
+
getCurrentDialog: () => {
|
|
803
|
+
const { dialogs } = get();
|
|
804
|
+
return dialogs[dialogs.length - 1];
|
|
805
|
+
},
|
|
806
|
+
setPrimaryLoading: (loading) => {
|
|
807
|
+
set((state) => {
|
|
808
|
+
const newDialogs = [...state.dialogs];
|
|
809
|
+
const idx = newDialogs.length - 1;
|
|
810
|
+
if (idx < 0 || !newDialogs[idx].primaryButton) return state;
|
|
811
|
+
newDialogs[idx] = {
|
|
812
|
+
...newDialogs[idx],
|
|
813
|
+
primaryButton: { ...newDialogs[idx].primaryButton, loading }
|
|
814
|
+
};
|
|
815
|
+
return { dialogs: newDialogs };
|
|
816
|
+
});
|
|
817
|
+
},
|
|
818
|
+
setSecondaryLoading: (loading) => {
|
|
819
|
+
set((state) => {
|
|
820
|
+
const newDialogs = [...state.dialogs];
|
|
821
|
+
const idx = newDialogs.length - 1;
|
|
822
|
+
if (idx < 0 || !newDialogs[idx].secondaryButton) return state;
|
|
823
|
+
newDialogs[idx] = {
|
|
824
|
+
...newDialogs[idx],
|
|
825
|
+
secondaryButton: { ...newDialogs[idx].secondaryButton, loading }
|
|
826
|
+
};
|
|
827
|
+
return { dialogs: newDialogs };
|
|
828
|
+
});
|
|
829
|
+
},
|
|
830
|
+
updatePrimaryButton: (config, dialogId) => {
|
|
831
|
+
set((state) => {
|
|
832
|
+
const newDialogs = [...state.dialogs];
|
|
833
|
+
const idx = dialogId ? newDialogs.findIndex((d) => d.id === dialogId) : newDialogs.length - 1;
|
|
834
|
+
if (idx === -1 || !newDialogs[idx].primaryButton) return state;
|
|
835
|
+
newDialogs[idx] = {
|
|
836
|
+
...newDialogs[idx],
|
|
837
|
+
primaryButton: { ...newDialogs[idx].primaryButton, ...config }
|
|
838
|
+
};
|
|
839
|
+
return { dialogs: newDialogs };
|
|
840
|
+
});
|
|
841
|
+
},
|
|
842
|
+
updateSecondaryButton: (config, dialogId) => {
|
|
843
|
+
set((state) => {
|
|
844
|
+
const newDialogs = [...state.dialogs];
|
|
845
|
+
const idx = dialogId ? newDialogs.findIndex((d) => d.id === dialogId) : newDialogs.length - 1;
|
|
846
|
+
if (idx === -1 || !newDialogs[idx].secondaryButton) return state;
|
|
847
|
+
newDialogs[idx] = {
|
|
848
|
+
...newDialogs[idx],
|
|
849
|
+
secondaryButton: { ...newDialogs[idx].secondaryButton, ...config }
|
|
850
|
+
};
|
|
851
|
+
return { dialogs: newDialogs };
|
|
852
|
+
});
|
|
853
|
+
},
|
|
854
|
+
updateShowButtons: (show, dialogId) => {
|
|
855
|
+
set((state) => {
|
|
856
|
+
const newDialogs = [...state.dialogs];
|
|
857
|
+
const idx = dialogId ? newDialogs.findIndex((d) => d.id === dialogId) : newDialogs.length - 1;
|
|
858
|
+
if (idx === -1) return state;
|
|
859
|
+
newDialogs[idx] = { ...newDialogs[idx], showButtons: show };
|
|
860
|
+
return { dialogs: newDialogs };
|
|
861
|
+
});
|
|
862
|
+
},
|
|
863
|
+
handlePrimaryClick: async (dialogId, data) => {
|
|
864
|
+
const { dialogs, setPrimaryLoading, closeDialog } = get();
|
|
865
|
+
const dialog = dialogs.find((d) => d.id === dialogId);
|
|
866
|
+
if (!dialog?.primaryButton) return;
|
|
867
|
+
const handler = dialog.primaryButton.onClick ?? dialog.primaryButton.onConfirm;
|
|
868
|
+
if (handler) {
|
|
869
|
+
setPrimaryLoading(true);
|
|
870
|
+
try {
|
|
871
|
+
await handler(data);
|
|
872
|
+
closeDialog(dialogId, data);
|
|
873
|
+
} finally {
|
|
874
|
+
setPrimaryLoading(false);
|
|
875
|
+
}
|
|
876
|
+
} else {
|
|
877
|
+
closeDialog(dialogId, data);
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
handleSecondaryClick: async (dialogId, data) => {
|
|
881
|
+
const { dialogs, closeDialog } = get();
|
|
882
|
+
const dialog = dialogs.find((d) => d.id === dialogId);
|
|
883
|
+
if (!dialog?.secondaryButton) {
|
|
884
|
+
closeDialog(dialogId, null);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const handler = dialog.secondaryButton.onClick ?? dialog.secondaryButton.onConfirm;
|
|
888
|
+
if (handler) {
|
|
889
|
+
await handler(data);
|
|
890
|
+
}
|
|
891
|
+
closeDialog(dialogId, null);
|
|
892
|
+
},
|
|
893
|
+
handleConfirm: async (data) => {
|
|
894
|
+
const { dialogs, handlePrimaryClick } = get();
|
|
895
|
+
const current = dialogs[dialogs.length - 1];
|
|
896
|
+
if (!current) return;
|
|
897
|
+
await handlePrimaryClick(current.id, data);
|
|
898
|
+
},
|
|
899
|
+
handleOpenChange: (dialogId, open) => {
|
|
900
|
+
if (!open) {
|
|
901
|
+
const { closeDialog } = get();
|
|
902
|
+
closeDialog(dialogId, null);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}));
|
|
906
|
+
}
|
|
907
|
+
var defaultStore = createDialogStore();
|
|
908
|
+
function useDialogStore() {
|
|
909
|
+
return defaultStore;
|
|
910
|
+
}
|
|
911
|
+
function NDialogHeader(_props) {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
function NDialogDescription(_props) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
function NDialogPrimaryButton(_props) {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
function NDialogSecondaryButton(_props) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
function parseDirectDialogSlots(children) {
|
|
924
|
+
const body = [];
|
|
925
|
+
const parsed = {};
|
|
926
|
+
React12__default.Children.forEach(children, (child) => {
|
|
927
|
+
if (!React12__default.isValidElement(child)) {
|
|
928
|
+
body.push(child);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (child.type === NDialogHeader) {
|
|
932
|
+
parsed.header = child.props;
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (child.type === NDialogDescription) {
|
|
936
|
+
parsed.description = child.props;
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (child.type === NDialogPrimaryButton || child.type === NDialogSecondaryButton) {
|
|
940
|
+
const { children: text, label, ...config } = child.props;
|
|
941
|
+
const button = { ...config, text: label ?? text ?? (child.type === NDialogPrimaryButton ? "Confirm" : "Cancel") };
|
|
942
|
+
if (child.type === NDialogPrimaryButton) parsed.primaryButton = button;
|
|
943
|
+
else parsed.secondaryButton = button;
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
body.push(child);
|
|
947
|
+
});
|
|
948
|
+
return { ...parsed, body };
|
|
949
|
+
}
|
|
950
|
+
var dialogVariants = cva(
|
|
951
|
+
"flex flex-col w-full max-w-full h-full max-h-screen",
|
|
952
|
+
{
|
|
953
|
+
variants: {
|
|
954
|
+
variant: {
|
|
955
|
+
default: "",
|
|
956
|
+
window: "rounded-none lg:rounded-sm"
|
|
957
|
+
},
|
|
958
|
+
size: {
|
|
959
|
+
sm: "lg:max-w-md",
|
|
960
|
+
md: "lg:max-w-lg",
|
|
961
|
+
lg: "lg:max-w-xl",
|
|
962
|
+
xl: "lg:max-w-2xl",
|
|
963
|
+
xxl: "lg:max-w-3xl",
|
|
964
|
+
full: "lg:max-w-4xl"
|
|
965
|
+
},
|
|
966
|
+
width: {
|
|
967
|
+
sm: "lg:max-w-sm",
|
|
968
|
+
md: "lg:max-w-md",
|
|
969
|
+
lg: "lg:max-w-lg",
|
|
970
|
+
xl: "lg:max-w-xl",
|
|
971
|
+
xxl: "lg:max-w-2xl",
|
|
972
|
+
"3xl": "lg:max-w-3xl",
|
|
973
|
+
"4xl": "lg:max-w-4xl",
|
|
974
|
+
"5xl": "lg:max-w-5xl",
|
|
975
|
+
"6xl": "lg:max-w-6xl",
|
|
976
|
+
"7xl": "lg:max-w-7xl",
|
|
977
|
+
full: "lg:max-w-5/6",
|
|
978
|
+
auto: "lg:w-auto"
|
|
979
|
+
},
|
|
980
|
+
height: {
|
|
981
|
+
auto: "lg:h-auto lg:max-h-[95vh]",
|
|
982
|
+
sm: "lg:h-[40vh]",
|
|
983
|
+
md: "lg:h-[60vh]",
|
|
984
|
+
lg: "lg:h-[70vh]",
|
|
985
|
+
xl: "lg:h-[80vh]",
|
|
986
|
+
xxl: "lg:h-[95vh]",
|
|
987
|
+
full: "lg:h-[95vh]"
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
defaultVariants: {
|
|
991
|
+
variant: "default",
|
|
992
|
+
size: "xl",
|
|
993
|
+
width: "3xl",
|
|
994
|
+
height: "auto"
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
);
|
|
998
|
+
var CONTENT_ACTIONS_SELECTOR = '[data-najm-dialog-actions="content"], [data-najm-wizard-form="true"]';
|
|
999
|
+
function useContentOwnsActions() {
|
|
1000
|
+
const [contentElement, setContentElement] = React12__default.useState(null);
|
|
1001
|
+
const [contentOwnsActions, setContentOwnsActions] = React12__default.useState(false);
|
|
1002
|
+
const contentElementRef = React12__default.useRef(null);
|
|
1003
|
+
const handleContentElementRef = React12__default.useCallback((node) => {
|
|
1004
|
+
if (contentElementRef.current === node) return;
|
|
1005
|
+
contentElementRef.current = node;
|
|
1006
|
+
setContentElement(node);
|
|
1007
|
+
}, []);
|
|
1008
|
+
React12__default.useLayoutEffect(() => {
|
|
1009
|
+
if (!contentElement) {
|
|
1010
|
+
setContentOwnsActions((current) => current ? false : current);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const update = () => {
|
|
1014
|
+
const next = !!contentElement.querySelector(CONTENT_ACTIONS_SELECTOR);
|
|
1015
|
+
setContentOwnsActions((current) => current === next ? current : next);
|
|
1016
|
+
};
|
|
1017
|
+
update();
|
|
1018
|
+
const observer = new MutationObserver(update);
|
|
1019
|
+
observer.observe(contentElement, {
|
|
1020
|
+
childList: true,
|
|
1021
|
+
subtree: true,
|
|
1022
|
+
attributes: true,
|
|
1023
|
+
attributeFilter: ["data-najm-dialog-actions", "data-najm-wizard-form"]
|
|
1024
|
+
});
|
|
1025
|
+
return () => observer.disconnect();
|
|
1026
|
+
}, [contentElement]);
|
|
1027
|
+
return { contentOwnsActions, setContentElement: handleContentElementRef };
|
|
1028
|
+
}
|
|
1029
|
+
function shouldRenderDialogButtons(showButtons, actionMode = "auto", contentOwnsActions) {
|
|
1030
|
+
if (!showButtons || actionMode === "content") return false;
|
|
1031
|
+
if (actionMode === "auto" && contentOwnsActions) return false;
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
var windowHeaderSpacing = {
|
|
1035
|
+
none: "px-3 py-2",
|
|
1036
|
+
sm: "-mx-3 -mt-3 px-3 py-2",
|
|
1037
|
+
md: "-mx-6 -mt-6 px-3 py-2",
|
|
1038
|
+
lg: "-mx-8 -mt-8 px-3 py-2"
|
|
1039
|
+
};
|
|
1040
|
+
function DialogChrome({
|
|
1041
|
+
pageHeader,
|
|
1042
|
+
title,
|
|
1043
|
+
description,
|
|
1044
|
+
padding,
|
|
1045
|
+
variant = "default",
|
|
1046
|
+
headerClassName,
|
|
1047
|
+
titleClassName,
|
|
1048
|
+
descriptionClassName,
|
|
1049
|
+
closeButtonClassName,
|
|
1050
|
+
contentRef,
|
|
1051
|
+
children
|
|
1052
|
+
}) {
|
|
1053
|
+
const resolvedPadding = useResponsivePadding(padding);
|
|
1054
|
+
const noTitle = !title || title.trim() === "";
|
|
1055
|
+
const noDescription = !description || description.trim() === "";
|
|
1056
|
+
const noHeader = noTitle && noDescription;
|
|
1057
|
+
const flush = resolvedPadding === "none";
|
|
1058
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1059
|
+
pageHeader ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1060
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: cn("sr-only", titleClassName), children: pageHeader.title || title || "Dialog" }),
|
|
1061
|
+
(pageHeader.subtitle || description) && /* @__PURE__ */ jsx(DialogDescription, { className: cn("sr-only", descriptionClassName), children: pageHeader.subtitle || description }),
|
|
1062
|
+
/* @__PURE__ */ jsx("div", { className: cn("shrink-0", headerClassName), children: /* @__PURE__ */ jsx(
|
|
1063
|
+
NPageHeader,
|
|
1064
|
+
{
|
|
1065
|
+
...pageHeader,
|
|
1066
|
+
card: false,
|
|
1067
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1068
|
+
pageHeader.actions,
|
|
1069
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { "aria-label": "Close", variant: "ghost", className: closeButtonClassName, children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) }) })
|
|
1070
|
+
] })
|
|
1071
|
+
}
|
|
1072
|
+
) })
|
|
1073
|
+
] }) : variant === "window" ? /* @__PURE__ */ jsxs(
|
|
1074
|
+
DialogHeader,
|
|
1075
|
+
{
|
|
1076
|
+
className: cn(
|
|
1077
|
+
"flex-row items-center justify-between gap-3 border-b border-border bg-secondary text-start text-secondary-foreground",
|
|
1078
|
+
windowHeaderSpacing[resolvedPadding],
|
|
1079
|
+
headerClassName
|
|
1080
|
+
),
|
|
1081
|
+
children: [
|
|
1082
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
1083
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: cn("truncate text-sm", noTitle && "sr-only", titleClassName), children: title || "Dialog" }),
|
|
1084
|
+
/* @__PURE__ */ jsx(DialogDescription, { className: cn("text-xs", noDescription && "sr-only", descriptionClassName), children: description })
|
|
1085
|
+
] }),
|
|
1086
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1087
|
+
IconButton,
|
|
1088
|
+
{
|
|
1089
|
+
"aria-label": "Close",
|
|
1090
|
+
variant: "ghost",
|
|
1091
|
+
size: "sm",
|
|
1092
|
+
className: cn("border border-border bg-background text-foreground hover:bg-accent", closeButtonClassName),
|
|
1093
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
1094
|
+
}
|
|
1095
|
+
) })
|
|
1096
|
+
]
|
|
1097
|
+
}
|
|
1098
|
+
) : /* @__PURE__ */ jsxs(DialogHeader, { className: cn(noHeader && "sr-only", headerClassName), children: [
|
|
1099
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: cn(noTitle && "sr-only", titleClassName), children: title }),
|
|
1100
|
+
/* @__PURE__ */ jsx(DialogDescription, { className: cn(noDescription && "sr-only", descriptionClassName), children: description })
|
|
1101
|
+
] }),
|
|
1102
|
+
flush ? (
|
|
1103
|
+
// Full-bleed content owns its own layout/scrolling; no outer scroll wrapper.
|
|
1104
|
+
/* @__PURE__ */ jsx("div", { ref: contentRef, className: "flex min-h-0 flex-1 flex-col", children })
|
|
1105
|
+
) : /* @__PURE__ */ jsx(NajmScroll, { axis: "y", className: "flex-1 px-1 -mx-1", children: /* @__PURE__ */ jsx("div", { ref: contentRef, style: { display: "contents" }, children }) })
|
|
1106
|
+
] });
|
|
1107
|
+
}
|
|
1108
|
+
function DialogItem({ dialog, index, store }) {
|
|
1109
|
+
if (dialog.render) {
|
|
1110
|
+
return /* @__PURE__ */ jsx(Fragment, { children: dialog.render({
|
|
1111
|
+
dialog,
|
|
1112
|
+
index,
|
|
1113
|
+
zIndex: 9990 + index,
|
|
1114
|
+
close: (result) => store.getState().closeDialog(dialog.id, result),
|
|
1115
|
+
confirm: (data) => store.getState().handlePrimaryClick(dialog.id, data),
|
|
1116
|
+
cancel: (data) => store.getState().handleSecondaryClick(dialog.id, data),
|
|
1117
|
+
onOpenChange: (open) => store.getState().handleOpenChange(dialog.id, open)
|
|
1118
|
+
}) });
|
|
1119
|
+
}
|
|
1120
|
+
const {
|
|
1121
|
+
id,
|
|
1122
|
+
title,
|
|
1123
|
+
description,
|
|
1124
|
+
pageHeader,
|
|
1125
|
+
padding,
|
|
1126
|
+
children,
|
|
1127
|
+
primaryButton,
|
|
1128
|
+
secondaryButton,
|
|
1129
|
+
showButtons,
|
|
1130
|
+
size,
|
|
1131
|
+
width,
|
|
1132
|
+
height,
|
|
1133
|
+
className,
|
|
1134
|
+
headerClassName,
|
|
1135
|
+
titleClassName,
|
|
1136
|
+
descriptionClassName,
|
|
1137
|
+
closeButtonClassName,
|
|
1138
|
+
variant = "default",
|
|
1139
|
+
actionMode = "auto"
|
|
1140
|
+
} = dialog;
|
|
1141
|
+
const { contentOwnsActions, setContentElement } = useContentOwnsActions();
|
|
1142
|
+
const renderDialogButtons = shouldRenderDialogButtons(showButtons, actionMode, contentOwnsActions);
|
|
1143
|
+
const handlePrimary = async (e) => {
|
|
1144
|
+
const hasForm = !!primaryButton?.form;
|
|
1145
|
+
if (!hasForm && e) e.preventDefault();
|
|
1146
|
+
await store.getState().handlePrimaryClick(id);
|
|
1147
|
+
};
|
|
1148
|
+
const handleSecondary = async (e) => {
|
|
1149
|
+
const hasForm = !!secondaryButton?.form;
|
|
1150
|
+
if (!hasForm && e) e.preventDefault();
|
|
1151
|
+
await store.getState().handleSecondaryClick(id);
|
|
1152
|
+
};
|
|
1153
|
+
return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (open) => store.getState().handleOpenChange(id, open), children: /* @__PURE__ */ jsxs(
|
|
1154
|
+
DialogContent,
|
|
1155
|
+
{
|
|
1156
|
+
"data-dialog-id": id,
|
|
1157
|
+
"data-variant": variant,
|
|
1158
|
+
padding,
|
|
1159
|
+
hideClose: !!pageHeader || variant === "window",
|
|
1160
|
+
className: cn(dialogVariants({ variant, size, width, height }), className),
|
|
1161
|
+
style: { zIndex: 9990 + index },
|
|
1162
|
+
children: [
|
|
1163
|
+
/* @__PURE__ */ jsx(
|
|
1164
|
+
DialogChrome,
|
|
1165
|
+
{
|
|
1166
|
+
pageHeader,
|
|
1167
|
+
title,
|
|
1168
|
+
description,
|
|
1169
|
+
padding,
|
|
1170
|
+
variant,
|
|
1171
|
+
headerClassName,
|
|
1172
|
+
titleClassName,
|
|
1173
|
+
descriptionClassName,
|
|
1174
|
+
closeButtonClassName,
|
|
1175
|
+
contentRef: setContentElement,
|
|
1176
|
+
children
|
|
1177
|
+
}
|
|
1178
|
+
),
|
|
1179
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: cn(!renderDialogButtons && "sr-only"), children: [
|
|
1180
|
+
renderDialogButtons && secondaryButton && /* @__PURE__ */ jsx(
|
|
1181
|
+
Button,
|
|
1182
|
+
{
|
|
1183
|
+
type: secondaryButton.form ? "submit" : "button",
|
|
1184
|
+
"data-button-type": "secondary",
|
|
1185
|
+
onClick: secondaryButton.form ? void 0 : handleSecondary,
|
|
1186
|
+
disabled: secondaryButton.disabled || secondaryButton.loading || false,
|
|
1187
|
+
variant: secondaryButton.variant || "outline",
|
|
1188
|
+
leftIcon: secondaryButton.icon,
|
|
1189
|
+
loading: secondaryButton.loading,
|
|
1190
|
+
loadingText: secondaryButton.loadingText,
|
|
1191
|
+
form: secondaryButton.form,
|
|
1192
|
+
className: cn("w-auto", secondaryButton.className),
|
|
1193
|
+
children: secondaryButton.text
|
|
1194
|
+
}
|
|
1195
|
+
),
|
|
1196
|
+
renderDialogButtons && primaryButton && /* @__PURE__ */ jsx(
|
|
1197
|
+
Button,
|
|
1198
|
+
{
|
|
1199
|
+
type: primaryButton.form ? "submit" : "button",
|
|
1200
|
+
"data-button-type": "primary",
|
|
1201
|
+
onClick: primaryButton.form ? void 0 : handlePrimary,
|
|
1202
|
+
disabled: primaryButton.disabled || primaryButton.loading || false,
|
|
1203
|
+
variant: primaryButton.variant || "default",
|
|
1204
|
+
leftIcon: primaryButton.icon,
|
|
1205
|
+
loading: primaryButton.loading,
|
|
1206
|
+
loadingText: primaryButton.loadingText,
|
|
1207
|
+
form: primaryButton.form,
|
|
1208
|
+
className: cn("w-auto", primaryButton.className),
|
|
1209
|
+
children: primaryButton.text
|
|
1210
|
+
}
|
|
1211
|
+
)
|
|
1212
|
+
] })
|
|
1213
|
+
]
|
|
1214
|
+
}
|
|
1215
|
+
) }, id);
|
|
1216
|
+
}
|
|
1217
|
+
function NMultiDialog({ store: storeProp }) {
|
|
1218
|
+
const defaultStore2 = useDialogStore();
|
|
1219
|
+
const store = storeProp ?? defaultStore2;
|
|
1220
|
+
const dialogs = store((s) => s.dialogs);
|
|
1221
|
+
return /* @__PURE__ */ jsx(Fragment, { children: dialogs.map((dialog, index) => /* @__PURE__ */ jsx(DialogItem, { dialog, index, store }, dialog.id)) });
|
|
1222
|
+
}
|
|
1223
|
+
var buildButtonConfig2 = (partial, defaults) => {
|
|
1224
|
+
return { ...defaults, ...partial };
|
|
1225
|
+
};
|
|
1226
|
+
var isDirectDialogProps = (props) => {
|
|
1227
|
+
return "trigger" in props || "open" in props || "defaultOpen" in props || "onOpenChange" in props || "title" in props || "description" in props || "children" in props || "primaryButton" in props || "secondaryButton" in props || "showButtons" in props || "size" in props || "width" in props || "height" in props || "className" in props || "headerClassName" in props || "titleClassName" in props || "descriptionClassName" in props || "closeButtonClassName" in props || "variant" in props || "pageHeader" in props || "padding" in props;
|
|
1228
|
+
};
|
|
1229
|
+
function NDirectDialog({
|
|
1230
|
+
trigger,
|
|
1231
|
+
open,
|
|
1232
|
+
defaultOpen = false,
|
|
1233
|
+
onOpenChange,
|
|
1234
|
+
title,
|
|
1235
|
+
description,
|
|
1236
|
+
pageHeader,
|
|
1237
|
+
padding,
|
|
1238
|
+
children,
|
|
1239
|
+
primaryButton,
|
|
1240
|
+
secondaryButton,
|
|
1241
|
+
showButtons = true,
|
|
1242
|
+
size,
|
|
1243
|
+
width,
|
|
1244
|
+
height,
|
|
1245
|
+
className,
|
|
1246
|
+
headerClassName,
|
|
1247
|
+
titleClassName,
|
|
1248
|
+
descriptionClassName,
|
|
1249
|
+
closeButtonClassName,
|
|
1250
|
+
variant = "default",
|
|
1251
|
+
actionMode = "auto",
|
|
1252
|
+
closeOnPrimary = true,
|
|
1253
|
+
closeOnSecondary = true
|
|
1254
|
+
}) {
|
|
1255
|
+
const slots = parseDirectDialogSlots(children);
|
|
1256
|
+
const resolvedTitle = title ?? slots.header?.label ?? slots.header?.children;
|
|
1257
|
+
const resolvedDescription = description ?? slots.description?.label ?? slots.description?.children;
|
|
1258
|
+
const resolvedHeaderClassName = cn(slots.header?.className, headerClassName);
|
|
1259
|
+
const resolvedTitleClassName = cn(slots.header?.titleClassName, titleClassName);
|
|
1260
|
+
const resolvedDescriptionClassName = cn(slots.description?.className, descriptionClassName);
|
|
1261
|
+
const [internalOpen, setInternalOpen] = React12__default.useState(defaultOpen);
|
|
1262
|
+
const [primaryLoading, setPrimaryLoading] = React12__default.useState(false);
|
|
1263
|
+
const isControlled = open !== void 0;
|
|
1264
|
+
const currentOpen = isControlled ? open : internalOpen;
|
|
1265
|
+
const setOpen = (nextOpen) => {
|
|
1266
|
+
if (!isControlled) {
|
|
1267
|
+
setInternalOpen(nextOpen);
|
|
1268
|
+
}
|
|
1269
|
+
onOpenChange?.(nextOpen);
|
|
1270
|
+
};
|
|
1271
|
+
const resolvedPrimaryButton = buildButtonConfig2(primaryButton ?? slots.primaryButton, {
|
|
1272
|
+
text: "Confirm",
|
|
1273
|
+
variant: "default",
|
|
1274
|
+
loading: false,
|
|
1275
|
+
disabled: false,
|
|
1276
|
+
loadingText: "Processing..."
|
|
1277
|
+
});
|
|
1278
|
+
const resolvedSecondaryButton = buildButtonConfig2(secondaryButton ?? slots.secondaryButton, {
|
|
1279
|
+
text: "Cancel",
|
|
1280
|
+
variant: "outline",
|
|
1281
|
+
loading: false,
|
|
1282
|
+
disabled: false,
|
|
1283
|
+
loadingText: "Processing..."
|
|
1284
|
+
});
|
|
1285
|
+
const isPrimaryLoading = primaryLoading || !!resolvedPrimaryButton.loading;
|
|
1286
|
+
const { contentOwnsActions, setContentElement } = useContentOwnsActions();
|
|
1287
|
+
const renderDialogButtons = shouldRenderDialogButtons(showButtons, actionMode, contentOwnsActions);
|
|
1288
|
+
const handlePrimary = async (e) => {
|
|
1289
|
+
const hasForm = !!resolvedPrimaryButton.form;
|
|
1290
|
+
if (!hasForm && e) e.preventDefault();
|
|
1291
|
+
if (resolvedPrimaryButton.onClick) {
|
|
1292
|
+
setPrimaryLoading(true);
|
|
1293
|
+
try {
|
|
1294
|
+
await resolvedPrimaryButton.onClick();
|
|
1295
|
+
} finally {
|
|
1296
|
+
setPrimaryLoading(false);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
if (closeOnPrimary) {
|
|
1300
|
+
setOpen(false);
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
const handleSecondary = async (e) => {
|
|
1304
|
+
const hasForm = !!resolvedSecondaryButton.form;
|
|
1305
|
+
if (!hasForm && e) e.preventDefault();
|
|
1306
|
+
if (resolvedSecondaryButton.onClick) {
|
|
1307
|
+
await resolvedSecondaryButton.onClick();
|
|
1308
|
+
}
|
|
1309
|
+
if (closeOnSecondary) {
|
|
1310
|
+
setOpen(false);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
return /* @__PURE__ */ jsxs(Dialog, { open: currentOpen, onOpenChange: setOpen, children: [
|
|
1314
|
+
trigger ? /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: trigger }) : null,
|
|
1315
|
+
/* @__PURE__ */ jsxs(
|
|
1316
|
+
DialogContent,
|
|
1317
|
+
{
|
|
1318
|
+
"data-variant": variant,
|
|
1319
|
+
padding,
|
|
1320
|
+
hideClose: !!pageHeader || variant === "window",
|
|
1321
|
+
className: cn(dialogVariants({ variant, size, width, height }), className),
|
|
1322
|
+
children: [
|
|
1323
|
+
/* @__PURE__ */ jsx(
|
|
1324
|
+
DialogChrome,
|
|
1325
|
+
{
|
|
1326
|
+
pageHeader,
|
|
1327
|
+
title: resolvedTitle,
|
|
1328
|
+
description: resolvedDescription,
|
|
1329
|
+
padding,
|
|
1330
|
+
variant,
|
|
1331
|
+
headerClassName: resolvedHeaderClassName,
|
|
1332
|
+
titleClassName: resolvedTitleClassName,
|
|
1333
|
+
descriptionClassName: resolvedDescriptionClassName,
|
|
1334
|
+
closeButtonClassName,
|
|
1335
|
+
contentRef: setContentElement,
|
|
1336
|
+
children: slots.body
|
|
1337
|
+
}
|
|
1338
|
+
),
|
|
1339
|
+
/* @__PURE__ */ jsx(DialogFooter, { className: cn(!renderDialogButtons && "sr-only"), children: renderDialogButtons && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1340
|
+
/* @__PURE__ */ jsx(
|
|
1341
|
+
Button,
|
|
1342
|
+
{
|
|
1343
|
+
type: resolvedSecondaryButton.form ? "submit" : "button",
|
|
1344
|
+
"data-button-type": "secondary",
|
|
1345
|
+
onClick: resolvedSecondaryButton.form ? void 0 : handleSecondary,
|
|
1346
|
+
disabled: resolvedSecondaryButton.disabled || resolvedSecondaryButton.loading || false,
|
|
1347
|
+
variant: resolvedSecondaryButton.variant || "outline",
|
|
1348
|
+
leftIcon: resolvedSecondaryButton.icon,
|
|
1349
|
+
loading: resolvedSecondaryButton.loading,
|
|
1350
|
+
loadingText: resolvedSecondaryButton.loadingText,
|
|
1351
|
+
form: resolvedSecondaryButton.form,
|
|
1352
|
+
className: cn("w-auto", resolvedSecondaryButton.className),
|
|
1353
|
+
children: resolvedSecondaryButton.text
|
|
1354
|
+
}
|
|
1355
|
+
),
|
|
1356
|
+
/* @__PURE__ */ jsx(
|
|
1357
|
+
Button,
|
|
1358
|
+
{
|
|
1359
|
+
type: resolvedPrimaryButton.form ? "submit" : "button",
|
|
1360
|
+
"data-button-type": "primary",
|
|
1361
|
+
onClick: resolvedPrimaryButton.form ? void 0 : handlePrimary,
|
|
1362
|
+
disabled: resolvedPrimaryButton.disabled || isPrimaryLoading || false,
|
|
1363
|
+
variant: resolvedPrimaryButton.variant || "default",
|
|
1364
|
+
leftIcon: resolvedPrimaryButton.icon,
|
|
1365
|
+
loading: isPrimaryLoading,
|
|
1366
|
+
loadingText: resolvedPrimaryButton.loadingText,
|
|
1367
|
+
form: resolvedPrimaryButton.form,
|
|
1368
|
+
className: cn("w-auto", resolvedPrimaryButton.className),
|
|
1369
|
+
children: resolvedPrimaryButton.text
|
|
1370
|
+
}
|
|
1371
|
+
)
|
|
1372
|
+
] }) })
|
|
1373
|
+
]
|
|
1374
|
+
}
|
|
1375
|
+
)
|
|
1376
|
+
] });
|
|
1377
|
+
}
|
|
1378
|
+
function NDialog(props) {
|
|
1379
|
+
if (isDirectDialogProps(props)) {
|
|
1380
|
+
return /* @__PURE__ */ jsx(NDirectDialog, { ...props });
|
|
1381
|
+
}
|
|
1382
|
+
return /* @__PURE__ */ jsx(NMultiDialog, { ...props });
|
|
1383
|
+
}
|
|
1384
|
+
function NConfirmDialog({
|
|
1385
|
+
open,
|
|
1386
|
+
onOpenChange,
|
|
1387
|
+
title = "Are you sure?",
|
|
1388
|
+
description,
|
|
1389
|
+
confirmLabel = "Confirm",
|
|
1390
|
+
cancelLabel = "Cancel",
|
|
1391
|
+
onConfirm,
|
|
1392
|
+
variant = "default",
|
|
1393
|
+
loading = false,
|
|
1394
|
+
children,
|
|
1395
|
+
icon
|
|
1396
|
+
}) {
|
|
1397
|
+
const isDestructive = variant === "destructive";
|
|
1398
|
+
const resolvedIcon = icon ?? (isDestructive ? "alert-triangle" : "circle-help");
|
|
1399
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (value) => {
|
|
1400
|
+
if (!value && loading) return;
|
|
1401
|
+
onOpenChange(value);
|
|
1402
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
1403
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 text-center pt-1", children: [
|
|
1404
|
+
/* @__PURE__ */ jsx(
|
|
1405
|
+
"div",
|
|
1406
|
+
{
|
|
1407
|
+
className: cn(
|
|
1408
|
+
"flex size-16 items-center justify-center rounded-full",
|
|
1409
|
+
isDestructive ? "bg-destructive/10 text-destructive" : "bg-primary/10 text-primary"
|
|
1410
|
+
),
|
|
1411
|
+
children: /* @__PURE__ */ jsx(NIcon, { icon: resolvedIcon, className: "size-8" })
|
|
1412
|
+
}
|
|
1413
|
+
),
|
|
1414
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "text-center sm:text-center", children: [
|
|
1415
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: isDestructive ? "text-destructive" : "", children: title }),
|
|
1416
|
+
description && /* @__PURE__ */ jsx(DialogDescription, { children: description })
|
|
1417
|
+
] })
|
|
1418
|
+
] }),
|
|
1419
|
+
children,
|
|
1420
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
1421
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: loading, children: cancelLabel }),
|
|
1422
|
+
/* @__PURE__ */ jsx(Button, { variant: isDestructive ? "destructive" : "default", onClick: onConfirm, disabled: loading, children: loading ? "Processing..." : confirmLabel })
|
|
1423
|
+
] })
|
|
1424
|
+
] }) });
|
|
1425
|
+
}
|
|
1426
|
+
function NDeleteDialogContent({
|
|
1427
|
+
title = "Delete",
|
|
1428
|
+
itemName,
|
|
1429
|
+
itemType,
|
|
1430
|
+
icon: Icon = "trash-2",
|
|
1431
|
+
warningText = "Are you sure you want to delete?",
|
|
1432
|
+
className
|
|
1433
|
+
}) {
|
|
1434
|
+
return /* @__PURE__ */ jsx("form", { id: "najm-delete-form", onSubmit: (e) => e.preventDefault(), children: /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center text-center", className), children: [
|
|
1435
|
+
/* @__PURE__ */ jsx("div", { className: "mb-6 flex size-16 items-center justify-center rounded-[20px] bg-destructive/10 text-destructive", children: /* @__PURE__ */ jsx(NIcon, { icon: Icon, className: "size-7" }) }),
|
|
1436
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1437
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold leading-none text-card-foreground", children: title }),
|
|
1438
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1439
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-5 text-muted-foreground", children: warningText }),
|
|
1440
|
+
/* @__PURE__ */ jsxs("p", { className: "mx-auto max-w-[280px] break-words text-center text-sm font-bold leading-5 text-card-foreground", children: [
|
|
1441
|
+
'"',
|
|
1442
|
+
itemName,
|
|
1443
|
+
'"',
|
|
1444
|
+
itemType ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1445
|
+
" (",
|
|
1446
|
+
itemType,
|
|
1447
|
+
")"
|
|
1448
|
+
] }) : null
|
|
1449
|
+
] })
|
|
1450
|
+
] })
|
|
1451
|
+
] })
|
|
1452
|
+
] }) });
|
|
1453
|
+
}
|
|
1454
|
+
function NDeleteDialog({
|
|
1455
|
+
open = true,
|
|
1456
|
+
title = "Delete",
|
|
1457
|
+
description,
|
|
1458
|
+
itemName,
|
|
1459
|
+
itemType,
|
|
1460
|
+
icon,
|
|
1461
|
+
warningText,
|
|
1462
|
+
confirmText = "Confirm",
|
|
1463
|
+
cancelText = "Cancel",
|
|
1464
|
+
loading,
|
|
1465
|
+
zIndex,
|
|
1466
|
+
className,
|
|
1467
|
+
contentClassName,
|
|
1468
|
+
onOpenChange,
|
|
1469
|
+
onConfirm,
|
|
1470
|
+
onCancel
|
|
1471
|
+
}) {
|
|
1472
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
1473
|
+
DialogContent,
|
|
1474
|
+
{
|
|
1475
|
+
className: cn(
|
|
1476
|
+
"h-auto max-w-[380px] gap-0 rounded-[18px] border border-border bg-card p-6 pt-8 shadow-2xl",
|
|
1477
|
+
"[&>button]:end-4 [&>button]:top-4 [&>button]:flex [&>button]:size-6 [&>button]:items-center [&>button]:justify-center",
|
|
1478
|
+
"[&>button]:rounded-full [&>button]:bg-muted [&>button]:text-muted-foreground [&>button]:opacity-100 [&>button]:ring-offset-0",
|
|
1479
|
+
"[&>button]:hover:bg-secondary [&>button]:hover:text-foreground",
|
|
1480
|
+
className
|
|
1481
|
+
),
|
|
1482
|
+
style: { zIndex },
|
|
1483
|
+
children: [
|
|
1484
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "sr-only", children: [
|
|
1485
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: title }),
|
|
1486
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: description })
|
|
1487
|
+
] }),
|
|
1488
|
+
/* @__PURE__ */ jsx(
|
|
1489
|
+
NDeleteDialogContent,
|
|
1490
|
+
{
|
|
1491
|
+
title,
|
|
1492
|
+
itemName,
|
|
1493
|
+
itemType,
|
|
1494
|
+
icon,
|
|
1495
|
+
warningText,
|
|
1496
|
+
className: contentClassName
|
|
1497
|
+
}
|
|
1498
|
+
),
|
|
1499
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: "mt-6 grid grid-cols-2 gap-3 sm:grid-cols-2", children: [
|
|
1500
|
+
/* @__PURE__ */ jsx(
|
|
1501
|
+
Button,
|
|
1502
|
+
{
|
|
1503
|
+
type: "button",
|
|
1504
|
+
"data-button-type": "secondary",
|
|
1505
|
+
variant: "outline",
|
|
1506
|
+
onClick: onCancel,
|
|
1507
|
+
disabled: loading,
|
|
1508
|
+
className: "h-10 w-full rounded-[10px] border border-destructive bg-transparent text-sm font-bold text-card-foreground shadow-none hover:bg-transparent hover:text-card-foreground",
|
|
1509
|
+
children: cancelText
|
|
1510
|
+
}
|
|
1511
|
+
),
|
|
1512
|
+
/* @__PURE__ */ jsx(
|
|
1513
|
+
Button,
|
|
1514
|
+
{
|
|
1515
|
+
type: "button",
|
|
1516
|
+
"data-button-type": "primary",
|
|
1517
|
+
variant: "destructive",
|
|
1518
|
+
onClick: onConfirm,
|
|
1519
|
+
loading,
|
|
1520
|
+
className: "h-10 w-full rounded-[10px] bg-destructive text-sm font-bold text-destructive-foreground shadow-none hover:bg-destructive/90 focus-visible:ring-destructive/40",
|
|
1521
|
+
children: confirmText
|
|
1522
|
+
}
|
|
1523
|
+
)
|
|
1524
|
+
] })
|
|
1525
|
+
]
|
|
1526
|
+
}
|
|
1527
|
+
) });
|
|
1528
|
+
}
|
|
1529
|
+
function useDialog(store) {
|
|
1530
|
+
const resolvedStore = store ?? useDialogStore();
|
|
1531
|
+
const custom = async (options) => {
|
|
1532
|
+
try {
|
|
1533
|
+
return await resolvedStore.getState().pushDialog(options);
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
console.error("[useDialog] Failed to push dialog:", error);
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
const confirmDelete = async (options) => {
|
|
1540
|
+
try {
|
|
1541
|
+
return await resolvedStore.getState().pushDialog({
|
|
1542
|
+
title: options.title || "Delete",
|
|
1543
|
+
description: options.description,
|
|
1544
|
+
size: options.size || "sm",
|
|
1545
|
+
className: options.className,
|
|
1546
|
+
render: ({ dialog, zIndex, confirm, cancel, onOpenChange }) => React12__default.createElement(NDeleteDialog, {
|
|
1547
|
+
itemName: options.itemName,
|
|
1548
|
+
itemType: options.itemType,
|
|
1549
|
+
icon: options.icon,
|
|
1550
|
+
warningText: options.warningText,
|
|
1551
|
+
title: dialog.title,
|
|
1552
|
+
description: dialog.description,
|
|
1553
|
+
confirmText: dialog.primaryButton?.text,
|
|
1554
|
+
cancelText: dialog.secondaryButton?.text,
|
|
1555
|
+
loading: dialog.primaryButton?.loading,
|
|
1556
|
+
className: dialog.className,
|
|
1557
|
+
zIndex,
|
|
1558
|
+
onOpenChange,
|
|
1559
|
+
onConfirm: () => confirm(),
|
|
1560
|
+
onCancel: () => cancel()
|
|
1561
|
+
}),
|
|
1562
|
+
primaryButton: {
|
|
1563
|
+
text: options.confirmText || "Confirm",
|
|
1564
|
+
variant: "destructive",
|
|
1565
|
+
loading: options.loading,
|
|
1566
|
+
onClick: options.onConfirm
|
|
1567
|
+
},
|
|
1568
|
+
secondaryButton: {
|
|
1569
|
+
text: options.cancelText || "Cancel",
|
|
1570
|
+
variant: "outline"
|
|
1571
|
+
},
|
|
1572
|
+
showButtons: true
|
|
1573
|
+
});
|
|
1574
|
+
} catch (error) {
|
|
1575
|
+
console.error("[useDialog] Failed to show confirmDelete:", error);
|
|
1576
|
+
return null;
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
custom,
|
|
1581
|
+
openDialog: custom,
|
|
1582
|
+
confirmDelete,
|
|
1583
|
+
closeAll: () => resolvedStore.getState().closeAllDialogs(),
|
|
1584
|
+
push: (opts) => resolvedStore.getState().pushDialog(opts),
|
|
1585
|
+
pop: (result) => resolvedStore.getState().popDialog(result)
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/location/labels.ts
|
|
1590
|
+
var DEFAULT_LOCATION_LABELS = {
|
|
1591
|
+
dialogTitle: "Pick an address",
|
|
1592
|
+
dialogDescription: "Search or select a location on the map.",
|
|
1593
|
+
openMap: "Select location on map",
|
|
1594
|
+
close: "Close",
|
|
1595
|
+
cancel: "Cancel",
|
|
1596
|
+
confirm: "Confirm address",
|
|
1597
|
+
clearPin: "Clear pin",
|
|
1598
|
+
searchPlaceholder: "Search by street, city, or postal code",
|
|
1599
|
+
search: "Search",
|
|
1600
|
+
searching: "Searching\u2026",
|
|
1601
|
+
searchEmpty: "No locations found.",
|
|
1602
|
+
searchError: "Location search is unavailable.",
|
|
1603
|
+
findingAddress: "Finding the address...",
|
|
1604
|
+
selected: "Delivery location selected",
|
|
1605
|
+
notSelected: "Delivery location not selected",
|
|
1606
|
+
changedAfterPin: "Address changed after the pin was selected",
|
|
1607
|
+
loading: "Loading map\u2026",
|
|
1608
|
+
unavailable: "Map selection is unavailable",
|
|
1609
|
+
retry: "Retry",
|
|
1610
|
+
currentLocation: "Use current location",
|
|
1611
|
+
zoomIn: "Zoom in",
|
|
1612
|
+
zoomOut: "Zoom out",
|
|
1613
|
+
mapInstructions: "Map. Use arrow keys to move the selected pin. Hold Shift for a larger step.",
|
|
1614
|
+
readyAnnouncement: "Map is ready.",
|
|
1615
|
+
selectedAnnouncement: "Location selected.",
|
|
1616
|
+
addressUpdatedAnnouncement: "Address updated for the selected location.",
|
|
1617
|
+
clearedAnnouncement: "Location pin cleared.",
|
|
1618
|
+
geolocationUnsupported: "Current location is not supported by this browser.",
|
|
1619
|
+
geolocationDenied: "Location permission was denied.",
|
|
1620
|
+
geolocationTimeout: "Current location timed out.",
|
|
1621
|
+
geolocationUnavailable: "Current location is unavailable.",
|
|
1622
|
+
providerError: "The map could not be loaded.",
|
|
1623
|
+
resultsAnnouncement: (count) => `${count} location result${count === 1 ? "" : "s"} available.`
|
|
1624
|
+
};
|
|
1625
|
+
var DEFAULT_CENTER = { latitude: 33.5731, longitude: -7.5898 };
|
|
1626
|
+
var NLocationContext = React12__default.createContext(null);
|
|
1627
|
+
function NLocationProvider({
|
|
1628
|
+
children,
|
|
1629
|
+
adapter = null,
|
|
1630
|
+
geocoder = null,
|
|
1631
|
+
defaultCenter = DEFAULT_CENTER,
|
|
1632
|
+
defaultZoom = 12,
|
|
1633
|
+
labels,
|
|
1634
|
+
searchMode = "submit",
|
|
1635
|
+
unavailableReason
|
|
1636
|
+
}) {
|
|
1637
|
+
const value = React12__default.useMemo(() => ({
|
|
1638
|
+
adapter,
|
|
1639
|
+
geocoder,
|
|
1640
|
+
defaultCenter,
|
|
1641
|
+
defaultZoom,
|
|
1642
|
+
labels: { ...DEFAULT_LOCATION_LABELS, ...labels },
|
|
1643
|
+
searchMode,
|
|
1644
|
+
unavailableReason
|
|
1645
|
+
}), [adapter, defaultCenter, defaultZoom, geocoder, labels, searchMode, unavailableReason]);
|
|
1646
|
+
return /* @__PURE__ */ jsx(NLocationContext.Provider, { value, children });
|
|
1647
|
+
}
|
|
1648
|
+
function useNLocationProvider() {
|
|
1649
|
+
const value = React12__default.useContext(NLocationContext);
|
|
1650
|
+
if (!value) {
|
|
1651
|
+
return {
|
|
1652
|
+
adapter: null,
|
|
1653
|
+
geocoder: null,
|
|
1654
|
+
defaultCenter: DEFAULT_CENTER,
|
|
1655
|
+
defaultZoom: 12,
|
|
1656
|
+
labels: DEFAULT_LOCATION_LABELS,
|
|
1657
|
+
searchMode: "submit",
|
|
1658
|
+
unavailableReason: DEFAULT_LOCATION_LABELS.unavailable
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
return value;
|
|
1662
|
+
}
|
|
1663
|
+
function useLocationDraft(committed, open) {
|
|
1664
|
+
const [draft, setDraft] = React12__default.useState(() => normalizeLocationValue(committed));
|
|
1665
|
+
React12__default.useEffect(() => {
|
|
1666
|
+
if (open) setDraft(normalizeLocationValue(committed));
|
|
1667
|
+
}, [committed, open]);
|
|
1668
|
+
const discard = React12__default.useCallback(() => setDraft(normalizeLocationValue(committed)), [committed]);
|
|
1669
|
+
return { draft, setDraft, discard };
|
|
1670
|
+
}
|
|
1671
|
+
var SEARCH_DELAY_MS = 350;
|
|
1672
|
+
function geolocationMessage(error, labels) {
|
|
1673
|
+
if (error.code === error.PERMISSION_DENIED) return labels.geolocationDenied;
|
|
1674
|
+
if (error.code === error.TIMEOUT) return labels.geolocationTimeout;
|
|
1675
|
+
return labels.geolocationUnavailable;
|
|
1676
|
+
}
|
|
1677
|
+
function NLocationDialog({ open, value, onOpenChange, onConfirm, labels: labelOverrides, classNames }) {
|
|
1678
|
+
const provider = useNLocationProvider();
|
|
1679
|
+
const labels = React12__default.useMemo(() => ({ ...provider.labels, ...labelOverrides }), [labelOverrides, provider.labels]);
|
|
1680
|
+
const { draft, setDraft, discard } = useLocationDraft(value, open);
|
|
1681
|
+
const [announcement, setAnnouncement] = React12__default.useState("");
|
|
1682
|
+
const [providerLoading, setProviderLoading] = React12__default.useState(false);
|
|
1683
|
+
const [providerError, setProviderError] = React12__default.useState(null);
|
|
1684
|
+
const [providerAttempt, setProviderAttempt] = React12__default.useState(0);
|
|
1685
|
+
const [mapControls, setMapControls] = React12__default.useState(null);
|
|
1686
|
+
const [query, setQuery] = React12__default.useState("");
|
|
1687
|
+
const [results, setResults] = React12__default.useState([]);
|
|
1688
|
+
const [searchPerformed, setSearchPerformed] = React12__default.useState(false);
|
|
1689
|
+
const [searchLoading, setSearchLoading] = React12__default.useState(false);
|
|
1690
|
+
const [searchError, setSearchError] = React12__default.useState(false);
|
|
1691
|
+
const [reverseLoading, setReverseLoading] = React12__default.useState(false);
|
|
1692
|
+
const searchAbort = React12__default.useRef(null);
|
|
1693
|
+
const reverseAbort = React12__default.useRef(null);
|
|
1694
|
+
const reverseRequest = React12__default.useRef(0);
|
|
1695
|
+
const geoRequest = React12__default.useRef(0);
|
|
1696
|
+
const searchInputRef = React12__default.useRef(null);
|
|
1697
|
+
const mapInstructionRef = React12__default.useRef(null);
|
|
1698
|
+
const coordinates = locationCoordinates(draft);
|
|
1699
|
+
const coordinatesRef = React12__default.useRef(coordinates);
|
|
1700
|
+
coordinatesRef.current = coordinates;
|
|
1701
|
+
const AdapterMap = provider.adapter?.Map;
|
|
1702
|
+
React12__default.useEffect(() => {
|
|
1703
|
+
if (!open) {
|
|
1704
|
+
searchAbort.current?.abort();
|
|
1705
|
+
reverseAbort.current?.abort();
|
|
1706
|
+
reverseRequest.current += 1;
|
|
1707
|
+
provider.geocoder?.resetSession?.();
|
|
1708
|
+
geoRequest.current += 1;
|
|
1709
|
+
setProviderLoading(false);
|
|
1710
|
+
setSearchLoading(false);
|
|
1711
|
+
setReverseLoading(false);
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
setAnnouncement("");
|
|
1715
|
+
setProviderError(null);
|
|
1716
|
+
setResults([]);
|
|
1717
|
+
setQuery("");
|
|
1718
|
+
setSearchError(false);
|
|
1719
|
+
setSearchPerformed(false);
|
|
1720
|
+
setSearchLoading(false);
|
|
1721
|
+
setReverseLoading(false);
|
|
1722
|
+
}, [open, provider.geocoder]);
|
|
1723
|
+
const close = React12__default.useCallback(() => {
|
|
1724
|
+
discard();
|
|
1725
|
+
onOpenChange(false);
|
|
1726
|
+
}, [discard, onOpenChange]);
|
|
1727
|
+
const updateCoordinates = React12__default.useCallback((next) => {
|
|
1728
|
+
setDraft((current) => ({ ...current, ...next }));
|
|
1729
|
+
setAnnouncement(labels.selectedAnnouncement);
|
|
1730
|
+
if (!provider.geocoder?.reverse) return;
|
|
1731
|
+
reverseAbort.current?.abort();
|
|
1732
|
+
const controller = new AbortController();
|
|
1733
|
+
const request = ++reverseRequest.current;
|
|
1734
|
+
reverseAbort.current = controller;
|
|
1735
|
+
setReverseLoading(true);
|
|
1736
|
+
void provider.geocoder.reverse(next, controller.signal).then((candidate) => {
|
|
1737
|
+
if (controller.signal.aborted || request !== reverseRequest.current || !candidate?.label) return;
|
|
1738
|
+
setDraft((current) => ({ ...current, address: candidate.label }));
|
|
1739
|
+
setAnnouncement(labels.addressUpdatedAnnouncement);
|
|
1740
|
+
}).catch(() => {
|
|
1741
|
+
}).finally(() => {
|
|
1742
|
+
if (request === reverseRequest.current) setReverseLoading(false);
|
|
1743
|
+
});
|
|
1744
|
+
}, [labels.addressUpdatedAnnouncement, labels.selectedAnnouncement, provider.geocoder, setDraft]);
|
|
1745
|
+
const performSearch = React12__default.useCallback(async (rawQuery) => {
|
|
1746
|
+
const nextQuery = rawQuery.trim();
|
|
1747
|
+
if (!provider.geocoder || nextQuery.length < 2) {
|
|
1748
|
+
setResults([]);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
searchAbort.current?.abort();
|
|
1752
|
+
const controller = new AbortController();
|
|
1753
|
+
searchAbort.current = controller;
|
|
1754
|
+
setSearchLoading(true);
|
|
1755
|
+
setSearchError(false);
|
|
1756
|
+
setSearchPerformed(false);
|
|
1757
|
+
try {
|
|
1758
|
+
const found = await provider.geocoder.search(nextQuery, {
|
|
1759
|
+
signal: controller.signal,
|
|
1760
|
+
center: coordinatesRef.current ?? provider.defaultCenter,
|
|
1761
|
+
language: document.documentElement.lang || void 0
|
|
1762
|
+
});
|
|
1763
|
+
if (controller.signal.aborted) return;
|
|
1764
|
+
setResults(found);
|
|
1765
|
+
setSearchPerformed(true);
|
|
1766
|
+
setAnnouncement(labels.resultsAnnouncement(found.length));
|
|
1767
|
+
} catch {
|
|
1768
|
+
if (controller.signal.aborted) return;
|
|
1769
|
+
setResults([]);
|
|
1770
|
+
setSearchError(true);
|
|
1771
|
+
setSearchPerformed(true);
|
|
1772
|
+
setAnnouncement(labels.searchError);
|
|
1773
|
+
} finally {
|
|
1774
|
+
if (!controller.signal.aborted) setSearchLoading(false);
|
|
1775
|
+
}
|
|
1776
|
+
}, [labels, provider.defaultCenter, provider.geocoder]);
|
|
1777
|
+
React12__default.useEffect(() => {
|
|
1778
|
+
if (!open || provider.searchMode !== "autocomplete" || !provider.geocoder || query.trim().length < 2) return;
|
|
1779
|
+
const timeout = setTimeout(() => void performSearch(query), SEARCH_DELAY_MS);
|
|
1780
|
+
return () => clearTimeout(timeout);
|
|
1781
|
+
}, [open, performSearch, provider.geocoder, provider.searchMode, query]);
|
|
1782
|
+
const selectCandidate = async (candidate) => {
|
|
1783
|
+
if (!provider.geocoder) return;
|
|
1784
|
+
reverseAbort.current?.abort();
|
|
1785
|
+
reverseRequest.current += 1;
|
|
1786
|
+
setReverseLoading(false);
|
|
1787
|
+
searchAbort.current?.abort();
|
|
1788
|
+
const controller = new AbortController();
|
|
1789
|
+
searchAbort.current = controller;
|
|
1790
|
+
setSearchLoading(true);
|
|
1791
|
+
try {
|
|
1792
|
+
const resolved = candidate.coordinates ? candidate : provider.geocoder.resolve ? await provider.geocoder.resolve(candidate, controller.signal) : candidate;
|
|
1793
|
+
if (controller.signal.aborted || !resolved.coordinates) return;
|
|
1794
|
+
setDraft((current) => ({
|
|
1795
|
+
...current,
|
|
1796
|
+
address: resolved.label || current.address,
|
|
1797
|
+
latitude: resolved.coordinates.latitude,
|
|
1798
|
+
longitude: resolved.coordinates.longitude
|
|
1799
|
+
}));
|
|
1800
|
+
mapControls?.recenter(resolved.coordinates);
|
|
1801
|
+
setQuery("");
|
|
1802
|
+
setResults([]);
|
|
1803
|
+
setSearchPerformed(false);
|
|
1804
|
+
setAnnouncement(labels.selectedAnnouncement);
|
|
1805
|
+
} catch {
|
|
1806
|
+
if (!controller.signal.aborted) setSearchError(true);
|
|
1807
|
+
} finally {
|
|
1808
|
+
if (!controller.signal.aborted) setSearchLoading(false);
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
const useCurrentLocation = () => {
|
|
1812
|
+
if (!navigator.geolocation) {
|
|
1813
|
+
setAnnouncement(labels.geolocationUnsupported);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const request = ++geoRequest.current;
|
|
1817
|
+
setProviderLoading(true);
|
|
1818
|
+
navigator.geolocation.getCurrentPosition(
|
|
1819
|
+
(position) => {
|
|
1820
|
+
if (request !== geoRequest.current || !open) return;
|
|
1821
|
+
const next = { latitude: position.coords.latitude, longitude: position.coords.longitude };
|
|
1822
|
+
updateCoordinates(next);
|
|
1823
|
+
mapControls?.recenter(next);
|
|
1824
|
+
setProviderLoading(false);
|
|
1825
|
+
},
|
|
1826
|
+
(error) => {
|
|
1827
|
+
if (request !== geoRequest.current || !open) return;
|
|
1828
|
+
setProviderLoading(false);
|
|
1829
|
+
setAnnouncement(geolocationMessage(error, labels));
|
|
1830
|
+
},
|
|
1831
|
+
{ enableHighAccuracy: false, timeout: 1e4, maximumAge: 6e4 }
|
|
1832
|
+
);
|
|
1833
|
+
};
|
|
1834
|
+
const handleMapKeyDown = (event) => {
|
|
1835
|
+
if (!coordinates || !["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
1836
|
+
event.preventDefault();
|
|
1837
|
+
const step = event.shiftKey ? 1e-3 : 1e-4;
|
|
1838
|
+
const latitudeDelta = event.key === "ArrowUp" ? step : event.key === "ArrowDown" ? -step : 0;
|
|
1839
|
+
const longitudeDelta = event.key === "ArrowRight" ? step : event.key === "ArrowLeft" ? -step : 0;
|
|
1840
|
+
updateCoordinates(moveCoordinates(coordinates, latitudeDelta, longitudeDelta));
|
|
1841
|
+
};
|
|
1842
|
+
const clearPin = () => {
|
|
1843
|
+
reverseAbort.current?.abort();
|
|
1844
|
+
reverseRequest.current += 1;
|
|
1845
|
+
setReverseLoading(false);
|
|
1846
|
+
setDraft((current) => ({ ...current, latitude: null, longitude: null }));
|
|
1847
|
+
setAnnouncement(labels.clearedAnnouncement);
|
|
1848
|
+
};
|
|
1849
|
+
const confirm = () => {
|
|
1850
|
+
const normalized = normalizeLocationValue(draft);
|
|
1851
|
+
if ((draft.latitude !== null || draft.longitude !== null) && !isCompleteCoordinatePair(draft)) return;
|
|
1852
|
+
onConfirm(normalized);
|
|
1853
|
+
onOpenChange(false);
|
|
1854
|
+
};
|
|
1855
|
+
const pending = providerLoading || searchLoading || reverseLoading;
|
|
1856
|
+
const invalidPair = (draft.latitude !== null || draft.longitude !== null) && !isCompleteCoordinatePair(draft);
|
|
1857
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => next ? onOpenChange(true) : close(), children: /* @__PURE__ */ jsxs(
|
|
1858
|
+
DialogContent,
|
|
1859
|
+
{
|
|
1860
|
+
padding: "none",
|
|
1861
|
+
hideClose: true,
|
|
1862
|
+
className: cn(
|
|
1863
|
+
"nlocation-dialog !flex flex-col gap-0 inset-0 top-0 left-0 h-dvh max-h-dvh max-w-none translate-x-0 translate-y-0 rounded-none sm:inset-auto sm:top-1/2 sm:left-1/2 sm:h-[min(46rem,calc(100dvh-2rem))] sm:max-h-[calc(100dvh-2rem)] sm:max-w-4xl sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-lg",
|
|
1864
|
+
classNames?.dialog
|
|
1865
|
+
),
|
|
1866
|
+
onOpenAutoFocus: (event) => {
|
|
1867
|
+
event.preventDefault();
|
|
1868
|
+
queueMicrotask(() => (provider.geocoder ? searchInputRef.current : mapInstructionRef.current)?.focus());
|
|
1869
|
+
},
|
|
1870
|
+
children: [
|
|
1871
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "shrink-0 flex-row items-start justify-between gap-4 border-b border-border px-4 py-4 text-start sm:px-6", children: [
|
|
1872
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
1873
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: labels.dialogTitle }),
|
|
1874
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: labels.dialogDescription })
|
|
1875
|
+
] }),
|
|
1876
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { "aria-label": labels.close, variant: "ghost", onClick: discard, children: /* @__PURE__ */ jsx(X, {}) }) })
|
|
1877
|
+
] }),
|
|
1878
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto px-4 py-3 sm:px-6", children: [
|
|
1879
|
+
provider.geocoder && /* @__PURE__ */ jsxs("form", { className: cn("relative shrink-0", classNames?.search), onSubmit: (event) => {
|
|
1880
|
+
event.preventDefault();
|
|
1881
|
+
void performSearch(query);
|
|
1882
|
+
}, children: [
|
|
1883
|
+
/* @__PURE__ */ jsx(
|
|
1884
|
+
Input,
|
|
1885
|
+
{
|
|
1886
|
+
ref: searchInputRef,
|
|
1887
|
+
value: query,
|
|
1888
|
+
onChange: (event) => {
|
|
1889
|
+
searchAbort.current?.abort();
|
|
1890
|
+
searchAbort.current = null;
|
|
1891
|
+
setSearchLoading(false);
|
|
1892
|
+
setQuery(event.target.value);
|
|
1893
|
+
setResults([]);
|
|
1894
|
+
setSearchError(false);
|
|
1895
|
+
setSearchPerformed(false);
|
|
1896
|
+
},
|
|
1897
|
+
placeholder: labels.searchPlaceholder,
|
|
1898
|
+
className: "pe-24",
|
|
1899
|
+
autoComplete: "off"
|
|
1900
|
+
}
|
|
1901
|
+
),
|
|
1902
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", size: "sm", variant: "ghost", className: "absolute end-1 top-1 h-7", disabled: searchLoading || query.trim().length < 2, leftIcon: Search, children: searchLoading ? labels.searching : labels.search }),
|
|
1903
|
+
(results.length > 0 || searchError || searchPerformed) && /* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 top-full z-[60] mt-1 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md", children: searchError ? /* @__PURE__ */ jsx("p", { className: "p-3 text-sm text-destructive", children: labels.searchError }) : results.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-3 text-sm text-muted-foreground", children: labels.searchEmpty }) : /* @__PURE__ */ jsx("div", { role: "listbox", "aria-label": labels.searchPlaceholder, className: "max-h-52 overflow-y-auto p-1", children: results.map((candidate) => /* @__PURE__ */ jsxs("button", { type: "button", role: "option", "aria-selected": "false", className: "flex w-full flex-col rounded-sm px-3 py-2 text-start text-sm hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", onClick: () => void selectCandidate(candidate), children: [
|
|
1904
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: candidate.label }),
|
|
1905
|
+
candidate.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: candidate.description })
|
|
1906
|
+
] }, candidate.id)) }) })
|
|
1907
|
+
] }),
|
|
1908
|
+
/* @__PURE__ */ jsxs(
|
|
1909
|
+
"div",
|
|
1910
|
+
{
|
|
1911
|
+
ref: mapInstructionRef,
|
|
1912
|
+
tabIndex: 0,
|
|
1913
|
+
role: "application",
|
|
1914
|
+
"aria-label": labels.mapInstructions,
|
|
1915
|
+
onKeyDown: handleMapKeyDown,
|
|
1916
|
+
className: cn("relative min-h-72 flex-1 overflow-hidden rounded-lg border border-border bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", classNames?.map),
|
|
1917
|
+
children: [
|
|
1918
|
+
AdapterMap ? /* @__PURE__ */ jsx(React12__default.Suspense, { fallback: /* @__PURE__ */ jsx(NLoadingState, { surface: "panel", label: labels.loading, className: "h-full min-h-72" }), children: /* @__PURE__ */ jsx(
|
|
1919
|
+
AdapterMap,
|
|
1920
|
+
{
|
|
1921
|
+
value: coordinates,
|
|
1922
|
+
initialCenter: provider.defaultCenter,
|
|
1923
|
+
initialZoom: provider.defaultZoom,
|
|
1924
|
+
onChange: updateCoordinates,
|
|
1925
|
+
onReady: () => {
|
|
1926
|
+
setProviderLoading(false);
|
|
1927
|
+
setProviderError(null);
|
|
1928
|
+
setAnnouncement(labels.readyAnnouncement);
|
|
1929
|
+
},
|
|
1930
|
+
onLoadingChange: setProviderLoading,
|
|
1931
|
+
onError: () => {
|
|
1932
|
+
setProviderLoading(false);
|
|
1933
|
+
setProviderError(labels.providerError);
|
|
1934
|
+
setAnnouncement(labels.providerError);
|
|
1935
|
+
},
|
|
1936
|
+
onControlsReady: setMapControls,
|
|
1937
|
+
reducedMotion: typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches,
|
|
1938
|
+
className: "h-full min-h-72 w-full"
|
|
1939
|
+
},
|
|
1940
|
+
providerAttempt
|
|
1941
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-72 items-center justify-center p-6 text-center text-sm text-muted-foreground", children: provider.unavailableReason || labels.unavailable }),
|
|
1942
|
+
providerError && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-20 flex flex-col items-center justify-center gap-3 bg-background/90 p-6 text-center", children: [
|
|
1943
|
+
/* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: providerError }),
|
|
1944
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => {
|
|
1945
|
+
setProviderError(null);
|
|
1946
|
+
setProviderAttempt((n) => n + 1);
|
|
1947
|
+
}, children: labels.retry })
|
|
1948
|
+
] }),
|
|
1949
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 end-3 z-10 flex flex-col gap-1 rounded-md border border-border bg-card/95 p-1 shadow-sm", children: [
|
|
1950
|
+
/* @__PURE__ */ jsx(IconButton, { "aria-label": labels.zoomIn, onClick: () => mapControls?.zoomIn(), disabled: !mapControls, children: /* @__PURE__ */ jsx(Plus, {}) }),
|
|
1951
|
+
/* @__PURE__ */ jsx(IconButton, { "aria-label": labels.zoomOut, onClick: () => mapControls?.zoomOut(), disabled: !mapControls, children: /* @__PURE__ */ jsx(Minus, {}) }),
|
|
1952
|
+
/* @__PURE__ */ jsx(IconButton, { "aria-label": labels.currentLocation, onClick: useCurrentLocation, disabled: !AdapterMap || pending, children: /* @__PURE__ */ jsx(Crosshair, {}) })
|
|
1953
|
+
] })
|
|
1954
|
+
]
|
|
1955
|
+
}
|
|
1956
|
+
),
|
|
1957
|
+
/* @__PURE__ */ jsxs("label", { className: "grid shrink-0 gap-1 text-sm font-medium", children: [
|
|
1958
|
+
labels.dialogTitle,
|
|
1959
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1960
|
+
/* @__PURE__ */ jsx(
|
|
1961
|
+
Input,
|
|
1962
|
+
{
|
|
1963
|
+
value: draft.address,
|
|
1964
|
+
"aria-busy": reverseLoading,
|
|
1965
|
+
className: reverseLoading ? "pe-10" : void 0,
|
|
1966
|
+
onChange: (event) => {
|
|
1967
|
+
reverseAbort.current?.abort();
|
|
1968
|
+
reverseRequest.current += 1;
|
|
1969
|
+
setReverseLoading(false);
|
|
1970
|
+
setDraft((current) => ({ ...current, address: event.target.value }));
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
),
|
|
1974
|
+
reverseLoading && /* @__PURE__ */ jsx("span", { role: "status", "aria-label": labels.findingAddress, className: "pointer-events-none absolute inset-y-0 end-3 flex items-center text-muted-foreground", children: /* @__PURE__ */ jsx(LoaderCircle, { "aria-hidden": "true", className: "h-4 w-4 animate-spin motion-reduce:animate-none" }) })
|
|
1975
|
+
] })
|
|
1976
|
+
] })
|
|
1977
|
+
] }),
|
|
1978
|
+
/* @__PURE__ */ jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", children: announcement }),
|
|
1979
|
+
/* @__PURE__ */ jsxs(DialogFooter, { className: cn("shrink-0 border-t border-border bg-card px-4 py-3 pb-[max(.75rem,env(safe-area-inset-bottom))] sm:px-6", classNames?.footer), children: [
|
|
1980
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", className: "sm:me-auto", onClick: clearPin, disabled: !coordinates || pending, leftIcon: Trash2, children: labels.clearPin }),
|
|
1981
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: close, children: labels.cancel }),
|
|
1982
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: confirm, disabled: pending || invalidPair, leftIcon: Check, children: labels.confirm })
|
|
1983
|
+
] })
|
|
1984
|
+
]
|
|
1985
|
+
}
|
|
1986
|
+
) });
|
|
1987
|
+
}
|
|
1988
|
+
var NLocationInput = React12__default.forwardRef(function NLocationInput2({
|
|
1989
|
+
value,
|
|
1990
|
+
onChange,
|
|
1991
|
+
labels: labelOverrides,
|
|
1992
|
+
classNames,
|
|
1993
|
+
status = "default",
|
|
1994
|
+
bordered,
|
|
1995
|
+
className,
|
|
1996
|
+
disabled = false,
|
|
1997
|
+
readOnly = false,
|
|
1998
|
+
...inputProps
|
|
1999
|
+
}, ref) {
|
|
2000
|
+
const provider = useNLocationProvider();
|
|
2001
|
+
const labels = React12__default.useMemo(() => ({ ...provider.labels, ...labelOverrides }), [labelOverrides, provider.labels]);
|
|
2002
|
+
const normalized = normalizeLocationValue(value);
|
|
2003
|
+
const hasPin = isCompleteCoordinatePair(normalized);
|
|
2004
|
+
const [open, setOpen] = React12__default.useState(false);
|
|
2005
|
+
const [pinnedAddress, setPinnedAddress] = React12__default.useState(() => hasPin ? normalized.address : "");
|
|
2006
|
+
const coordinateKey = hasPin ? `${normalized.latitude}:${normalized.longitude}` : "";
|
|
2007
|
+
const pinnedCoordinateKey = React12__default.useRef(coordinateKey);
|
|
2008
|
+
React12__default.useEffect(() => {
|
|
2009
|
+
if (coordinateKey === pinnedCoordinateKey.current) return;
|
|
2010
|
+
pinnedCoordinateKey.current = coordinateKey;
|
|
2011
|
+
setPinnedAddress(hasPin ? normalized.address : "");
|
|
2012
|
+
}, [coordinateKey, hasPin, normalized.address]);
|
|
2013
|
+
const changedAfterPin = hasPin && pinnedAddress !== "" && pinnedAddress !== normalized.address;
|
|
2014
|
+
const mapAvailable = Boolean(provider.adapter);
|
|
2015
|
+
const stateLabel = !mapAvailable ? provider.unavailableReason || labels.unavailable : changedAfterPin ? labels.changedAfterPin : hasPin ? labels.selected : labels.notSelected;
|
|
2016
|
+
const StateIcon = !mapAvailable || changedAfterPin ? CircleAlert : hasPin ? MapPinCheck : MapPin;
|
|
2017
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("grid gap-1.5", classNames?.root), children: [
|
|
2018
|
+
/* @__PURE__ */ jsxs(BaseInput, { status, bordered, disabled, className: cn("gap-2", classNames?.input, className), children: [
|
|
2019
|
+
/* @__PURE__ */ jsx(
|
|
2020
|
+
Input,
|
|
2021
|
+
{
|
|
2022
|
+
...inputProps,
|
|
2023
|
+
ref,
|
|
2024
|
+
value: normalized.address,
|
|
2025
|
+
onChange: (event) => onChange({ ...normalized, address: event.target.value }),
|
|
2026
|
+
disabled,
|
|
2027
|
+
readOnly,
|
|
2028
|
+
className: "h-auto flex-1 border-0 bg-transparent p-0 shadow-none focus-visible:ring-0"
|
|
2029
|
+
}
|
|
2030
|
+
),
|
|
2031
|
+
/* @__PURE__ */ jsx(
|
|
2032
|
+
IconButton,
|
|
2033
|
+
{
|
|
2034
|
+
"aria-label": labels.openMap,
|
|
2035
|
+
variant: "ghost",
|
|
2036
|
+
size: "md",
|
|
2037
|
+
disabled: disabled || readOnly || !mapAvailable,
|
|
2038
|
+
onClick: () => setOpen(true),
|
|
2039
|
+
children: /* @__PURE__ */ jsx(MapPin, {})
|
|
2040
|
+
}
|
|
2041
|
+
)
|
|
2042
|
+
] }),
|
|
2043
|
+
/* @__PURE__ */ jsxs("p", { className: cn("flex items-center gap-1.5 text-xs text-muted-foreground", changedAfterPin && "text-warning", classNames?.status), children: [
|
|
2044
|
+
/* @__PURE__ */ jsx(StateIcon, { "aria-hidden": "true", className: "h-3.5 w-3.5 shrink-0" }),
|
|
2045
|
+
stateLabel
|
|
2046
|
+
] }),
|
|
2047
|
+
/* @__PURE__ */ jsx(
|
|
2048
|
+
NLocationDialog,
|
|
2049
|
+
{
|
|
2050
|
+
open,
|
|
2051
|
+
value: normalized,
|
|
2052
|
+
onOpenChange: setOpen,
|
|
2053
|
+
onConfirm: (next) => {
|
|
2054
|
+
setPinnedAddress(next.latitude === null ? "" : next.address);
|
|
2055
|
+
onChange(next);
|
|
2056
|
+
},
|
|
2057
|
+
labels: labelOverrides,
|
|
2058
|
+
classNames
|
|
2059
|
+
}
|
|
2060
|
+
)
|
|
2061
|
+
] });
|
|
2062
|
+
});
|
|
2063
|
+
function Label({ className, ...props }) {
|
|
2064
|
+
return /* @__PURE__ */ jsx(
|
|
2065
|
+
LabelPrimitive.Root,
|
|
2066
|
+
{
|
|
2067
|
+
"data-slot": "label",
|
|
2068
|
+
className: cn("flex items-center gap-2 text-sm leading-none font-medium text-foreground select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
|
|
2069
|
+
...props
|
|
2070
|
+
}
|
|
2071
|
+
);
|
|
2072
|
+
}
|
|
2073
|
+
var Form = FormProvider;
|
|
2074
|
+
var FormFieldContext = React12.createContext({});
|
|
2075
|
+
var FormField = ({ ...props }) => /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
|
|
2076
|
+
var useFormField = () => {
|
|
2077
|
+
const fieldContext = React12.useContext(FormFieldContext);
|
|
2078
|
+
const itemContext = React12.useContext(FormItemContext);
|
|
2079
|
+
const { getFieldState, formState } = useFormContext();
|
|
2080
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
2081
|
+
if (!fieldContext) throw new Error("useFormField should be used within <FormField>");
|
|
2082
|
+
const { id } = itemContext;
|
|
2083
|
+
return { id, name: fieldContext.name, formItemId: `${id}-form-item`, formDescriptionId: `${id}-form-item-description`, formMessageId: `${id}-form-item-message`, ...fieldState };
|
|
2084
|
+
};
|
|
2085
|
+
var FormItemContext = React12.createContext({});
|
|
2086
|
+
function FormItem({ className, ...props }) {
|
|
2087
|
+
const id = React12.useId();
|
|
2088
|
+
return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx("div", { "data-slot": "form-item", className: cn("grid gap-2", className), ...props }) });
|
|
2089
|
+
}
|
|
2090
|
+
function FormLabel({ className, ...props }) {
|
|
2091
|
+
const { error, formItemId } = useFormField();
|
|
2092
|
+
return /* @__PURE__ */ jsx(Label, { "data-slot": "form-label", "data-error": !!error, className: cn("data-[error=true]:text-destructive", className), htmlFor: formItemId, ...props });
|
|
2093
|
+
}
|
|
2094
|
+
function FormControl({ ...props }) {
|
|
2095
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
2096
|
+
return /* @__PURE__ */ jsx(Slot, { "data-slot": "form-control", id: formItemId, "aria-describedby": !error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`, "aria-invalid": !!error, ...props });
|
|
2097
|
+
}
|
|
2098
|
+
function FormDescription({ className, ...props }) {
|
|
2099
|
+
const { formDescriptionId } = useFormField();
|
|
2100
|
+
return /* @__PURE__ */ jsx("p", { "data-slot": "form-description", id: formDescriptionId, className: cn("text-muted-foreground text-[0.8rem]", className), ...props });
|
|
2101
|
+
}
|
|
2102
|
+
function FormMessage({ className, children, ...props }) {
|
|
2103
|
+
const { error, formMessageId } = useFormField();
|
|
2104
|
+
const findMessage = (value) => {
|
|
2105
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2106
|
+
if ("message" in value && typeof value.message === "string") return value.message;
|
|
2107
|
+
for (const child of Object.values(value)) {
|
|
2108
|
+
const nested = findMessage(child);
|
|
2109
|
+
if (nested) return nested;
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
const body = error ? findMessage(error) : children;
|
|
2113
|
+
if (!body) return null;
|
|
2114
|
+
return /* @__PURE__ */ jsx("p", { "data-slot": "form-message", id: formMessageId, className: cn("text-destructive text-[0.8rem] font-medium", className), ...props, children: body });
|
|
2115
|
+
}
|
|
2116
|
+
var VARIANT_PRESETS = {
|
|
2117
|
+
default: {
|
|
2118
|
+
item: "flex flex-col w-full",
|
|
2119
|
+
label: "text-foreground flex items-center gap-2",
|
|
2120
|
+
input: "",
|
|
2121
|
+
description: "",
|
|
2122
|
+
error: ""
|
|
2123
|
+
},
|
|
2124
|
+
studio: {
|
|
2125
|
+
item: "space-y-1.5 w-full",
|
|
2126
|
+
label: "text-[11px] uppercase tracking-[0.16em] text-muted-foreground flex items-center gap-2",
|
|
2127
|
+
input: "h-10 bg-card",
|
|
2128
|
+
description: "text-xs text-muted-foreground",
|
|
2129
|
+
error: "text-xs text-destructive"
|
|
2130
|
+
},
|
|
2131
|
+
compact: {
|
|
2132
|
+
item: "flex flex-col w-full gap-1",
|
|
2133
|
+
label: "text-xs text-foreground flex items-center gap-2",
|
|
2134
|
+
input: "h-8 text-sm",
|
|
2135
|
+
description: "text-xs",
|
|
2136
|
+
error: "text-xs"
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
var VariantContext = createContext({ variant: "default" });
|
|
2140
|
+
var VariantProvider = ({ variant = "default", bordered, children }) => /* @__PURE__ */ jsx(VariantContext.Provider, { value: { variant, bordered }, children });
|
|
2141
|
+
var useVariant = () => useContext(VariantContext).variant;
|
|
2142
|
+
var useBordered = () => useContext(VariantContext).bordered;
|
|
2143
|
+
var useVariantPreset = () => VARIANT_PRESETS[useContext(VariantContext).variant];
|
|
2144
|
+
var PrefixContext = createContext(void 0);
|
|
2145
|
+
var PrefixProvider = ({ prefix, children }) => /* @__PURE__ */ jsx(PrefixContext.Provider, { value: prefix, children });
|
|
2146
|
+
var usePrefix = () => useContext(PrefixContext);
|
|
2147
|
+
var backgrounds = {
|
|
2148
|
+
card: "!bg-card",
|
|
2149
|
+
background: "!bg-background",
|
|
2150
|
+
secondary: "!bg-secondary",
|
|
2151
|
+
muted: "!bg-muted",
|
|
2152
|
+
transparent: "!bg-transparent"
|
|
2153
|
+
};
|
|
2154
|
+
function nestedMessage(error) {
|
|
2155
|
+
if (!error || typeof error !== "object") return void 0;
|
|
2156
|
+
if ("message" in error && typeof error.message === "string") return error.message;
|
|
2157
|
+
for (const child of Object.values(error)) {
|
|
2158
|
+
const message = nestedMessage(child);
|
|
2159
|
+
if (message) return message;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
function FormLocationInput({ name, formLabel, formDescription, required = false, disabled = false, readOnly = false, hidden = false, background, classNames, onChange: consumerOnChange, ...props }) {
|
|
2163
|
+
const { control } = useFormContext();
|
|
2164
|
+
const prefix = usePrefix();
|
|
2165
|
+
const preset = useVariantPreset();
|
|
2166
|
+
const bordered = useBordered();
|
|
2167
|
+
const fieldName = prefix ? `${prefix}.${name}` : name;
|
|
2168
|
+
return /* @__PURE__ */ jsx(FormField, { control, name: fieldName, render: ({ field, fieldState }) => {
|
|
2169
|
+
const message = nestedMessage(fieldState.error);
|
|
2170
|
+
const handleChange = (next) => {
|
|
2171
|
+
field.onChange(next);
|
|
2172
|
+
consumerOnChange?.(next);
|
|
2173
|
+
};
|
|
2174
|
+
return /* @__PURE__ */ jsxs(FormItem, { className: cn(preset.item, classNames?.item, hidden && "hidden"), children: [
|
|
2175
|
+
formLabel && /* @__PURE__ */ jsxs(FormLabel, { className: cn(preset.label, classNames?.label), children: [
|
|
2176
|
+
formLabel,
|
|
2177
|
+
required && !disabled && !readOnly && /* @__PURE__ */ jsx("span", { className: "ms-1 text-destructive", children: "*" })
|
|
2178
|
+
] }),
|
|
2179
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
2180
|
+
NLocationInput,
|
|
2181
|
+
{
|
|
2182
|
+
...props,
|
|
2183
|
+
name: field.name,
|
|
2184
|
+
ref: field.ref,
|
|
2185
|
+
value: field.value ?? DEFAULT_LOCATION_VALUE,
|
|
2186
|
+
onChange: handleChange,
|
|
2187
|
+
onBlur: field.onBlur,
|
|
2188
|
+
disabled,
|
|
2189
|
+
readOnly,
|
|
2190
|
+
bordered,
|
|
2191
|
+
status: fieldState.error ? "error" : "default",
|
|
2192
|
+
className: cn(background && backgrounds[background], classNames?.input),
|
|
2193
|
+
classNames
|
|
2194
|
+
}
|
|
2195
|
+
) }),
|
|
2196
|
+
!fieldState.error && !disabled && !readOnly && formDescription && /* @__PURE__ */ jsx(FormDescription, { className: cn(preset.description, classNames?.description), children: formDescription }),
|
|
2197
|
+
message ? /* @__PURE__ */ jsx(FormMessage, { className: cn(preset.error, classNames?.error), children: message }) : null
|
|
2198
|
+
] });
|
|
2199
|
+
} });
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
export { BaseInput, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormLocationInput, FormMessage, IconButton, Input, Label, NConfirmDialog, NDeleteDialog, NDeleteDialogContent, NDialog, NDialogDescription, NDialogHeader, NDialogPrimaryButton, NDialogSecondaryButton, NLocationDialog, NLocationInput, NLocationProvider, NMultiDialog, NPageHeader, NPageHeaderActions, NPageHeaderCompactActions, NPageHeaderFilters, NPageHeaderTop, NPortalScopeProvider, NSheet, NSidebarProvider, PrefixProvider, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, VariantProvider, createDialogStore, dialogVariants, focusRingClasses, focusRingWithinClasses, isCompleteCoordinatePair, normalizeLocationValue, useBordered, useDialog, useDialogStore, useFormField, useNLocationProvider, useNPortalScope, useNSidebar, usePrefix, useVariant, useVariantPreset };
|