@mmmmzxe/react-360-view 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4716 @@
1
+ "use client";
2
+
3
+ // src/feature/Viewer360.tsx
4
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
5
+
6
+ // src/constants/viewer360Labels.ts
7
+ var defaultViewer360Labels = {
8
+ loading: "Loading images\u2026",
9
+ dragHint: "Drag to rotate \u2022 Scroll to zoom",
10
+ frameIndicator: ({ current, total, label }) => label ? `${label} \xB7 ${current} / ${total}` : `${current} / ${total}`,
11
+ zoom: (percent) => `${percent}%`,
12
+ hotspotModeActive: "Click on the image to place a hotspot",
13
+ addHotspot: "Add hotspot",
14
+ zoomIn: "Zoom in",
15
+ zoomOut: "Zoom out",
16
+ resetView: "Reset view",
17
+ deleteMarker: "Remove marker"
18
+ };
19
+
20
+ // src/constants/viewer360ClassNames.ts
21
+ var viewer360ClassNames = {
22
+ root: "w-full max-w-full min-w-0 overflow-hidden rounded-lg border bg-card text-card-foreground",
23
+ viewport: "relative aspect-[16/10] w-full max-w-full min-w-0 touch-none select-none bg-muted",
24
+ canvas: "absolute inset-0 size-full",
25
+ overlay: "pointer-events-none absolute inset-0 overflow-hidden",
26
+ loading: "absolute inset-0 flex items-center justify-center bg-muted/80",
27
+ loadingText: "text-sm text-muted-foreground",
28
+ frameIndicator: "pointer-events-none absolute bottom-4 start-1/2 z-20 -translate-x-1/2 rounded-full border bg-background px-4 py-1.5 text-xs font-medium shadow-sm whitespace-nowrap",
29
+ hotspotModeBanner: "pointer-events-none absolute top-4 start-1/2 z-20 -translate-x-1/2 rounded-full border border-amber-200 bg-amber-50 px-4 py-1.5 text-xs font-medium text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-400",
30
+ toolbar: "flex flex-wrap items-center justify-between gap-2 border-t px-4 py-3",
31
+ dragHint: "hidden text-xs text-muted-foreground sm:block",
32
+ controls: "ms-auto flex items-center gap-1.5",
33
+ controlButton: "",
34
+ controlButtonActive: "",
35
+ controlButtonDisabled: "",
36
+ zoomDisplay: "flex min-w-[3rem] items-center justify-center gap-1 rounded-md border bg-background px-2 py-1 text-xs font-medium",
37
+ divider: "mx-1 h-6 w-px bg-border"
38
+ };
39
+ var viewer360MarkerPinClassNames = {
40
+ root: "pointer-events-auto absolute z-30 -translate-x-1/2 -translate-y-1/2",
41
+ ping: "pointer-events-none absolute inset-0 inline-flex animate-ping rounded-full bg-destructive opacity-75",
42
+ dot: "relative z-10 inline-flex size-4 min-h-4 min-w-4 shrink-0 rounded-full border-2 border-background bg-destructive p-0 shadow-md transition-transform duration-200 hover:scale-125 focus:outline-none",
43
+ tooltip: "absolute bottom-6 left-1/2 z-40 w-64 -translate-x-1/2 rounded-lg border bg-popover p-3 text-popover-foreground shadow-md",
44
+ tooltipHeader: "flex items-start justify-between gap-2",
45
+ tooltipBody: "flex min-w-0 flex-col gap-1",
46
+ tooltipTitle: "text-sm font-medium",
47
+ tooltipDescription: "mt-2 line-clamp-3 text-xs text-muted-foreground",
48
+ deleteButton: ""
49
+ };
50
+ var defaultViewer360ClassNames = viewer360ClassNames;
51
+ var defaultViewer360MarkerPinClassNames = viewer360MarkerPinClassNames;
52
+
53
+ // node_modules/clsx/dist/clsx.mjs
54
+ function r(e) {
55
+ var t, f, n = "";
56
+ if ("string" == typeof e || "number" == typeof e) n += e;
57
+ else if ("object" == typeof e) if (Array.isArray(e)) {
58
+ var o = e.length;
59
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
60
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
61
+ return n;
62
+ }
63
+ function clsx() {
64
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
65
+ return n;
66
+ }
67
+
68
+ // node_modules/tailwind-merge/dist/bundle-mjs.mjs
69
+ var concatArrays = (array1, array2) => {
70
+ const combinedArray = new Array(array1.length + array2.length);
71
+ for (let i = 0; i < array1.length; i++) {
72
+ combinedArray[i] = array1[i];
73
+ }
74
+ for (let i = 0; i < array2.length; i++) {
75
+ combinedArray[array1.length + i] = array2[i];
76
+ }
77
+ return combinedArray;
78
+ };
79
+ var createClassValidatorObject = (classGroupId, validator) => ({
80
+ classGroupId,
81
+ validator
82
+ });
83
+ var createClassPartObject = (nextPart = /* @__PURE__ */ new Map(), validators = null, classGroupId) => ({
84
+ nextPart,
85
+ validators,
86
+ classGroupId
87
+ });
88
+ var CLASS_PART_SEPARATOR = "-";
89
+ var EMPTY_CONFLICTS = [];
90
+ var ARBITRARY_PROPERTY_PREFIX = "arbitrary..";
91
+ var createClassGroupUtils = (config) => {
92
+ const classMap = createClassMap(config);
93
+ const {
94
+ conflictingClassGroups,
95
+ conflictingClassGroupModifiers
96
+ } = config;
97
+ const getClassGroupId = (className) => {
98
+ if (className.startsWith("[") && className.endsWith("]")) {
99
+ return getGroupIdForArbitraryProperty(className);
100
+ }
101
+ const classParts = className.split(CLASS_PART_SEPARATOR);
102
+ const startIndex = classParts[0] === "" && classParts.length > 1 ? 1 : 0;
103
+ return getGroupRecursive(classParts, startIndex, classMap);
104
+ };
105
+ const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
106
+ if (hasPostfixModifier) {
107
+ const modifierConflicts = conflictingClassGroupModifiers[classGroupId];
108
+ const baseConflicts = conflictingClassGroups[classGroupId];
109
+ if (modifierConflicts) {
110
+ if (baseConflicts) {
111
+ return concatArrays(baseConflicts, modifierConflicts);
112
+ }
113
+ return modifierConflicts;
114
+ }
115
+ return baseConflicts || EMPTY_CONFLICTS;
116
+ }
117
+ return conflictingClassGroups[classGroupId] || EMPTY_CONFLICTS;
118
+ };
119
+ return {
120
+ getClassGroupId,
121
+ getConflictingClassGroupIds
122
+ };
123
+ };
124
+ var getGroupRecursive = (classParts, startIndex, classPartObject) => {
125
+ const classPathsLength = classParts.length - startIndex;
126
+ if (classPathsLength === 0) {
127
+ return classPartObject.classGroupId;
128
+ }
129
+ const currentClassPart = classParts[startIndex];
130
+ const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
131
+ if (nextClassPartObject) {
132
+ const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject);
133
+ if (result) return result;
134
+ }
135
+ const validators = classPartObject.validators;
136
+ if (validators === null) {
137
+ return void 0;
138
+ }
139
+ const classRest = startIndex === 0 ? classParts.join(CLASS_PART_SEPARATOR) : classParts.slice(startIndex).join(CLASS_PART_SEPARATOR);
140
+ const validatorsLength = validators.length;
141
+ for (let i = 0; i < validatorsLength; i++) {
142
+ const validatorObj = validators[i];
143
+ if (validatorObj.validator(classRest)) {
144
+ return validatorObj.classGroupId;
145
+ }
146
+ }
147
+ return void 0;
148
+ };
149
+ var getGroupIdForArbitraryProperty = (className) => className.slice(1, -1).indexOf(":") === -1 ? void 0 : (() => {
150
+ const content = className.slice(1, -1);
151
+ const colonIndex = content.indexOf(":");
152
+ const property = content.slice(0, colonIndex);
153
+ return property ? ARBITRARY_PROPERTY_PREFIX + property : void 0;
154
+ })();
155
+ var createClassMap = (config) => {
156
+ const {
157
+ theme,
158
+ classGroups
159
+ } = config;
160
+ return processClassGroups(classGroups, theme);
161
+ };
162
+ var processClassGroups = (classGroups, theme) => {
163
+ const classMap = createClassPartObject();
164
+ for (const classGroupId in classGroups) {
165
+ const group = classGroups[classGroupId];
166
+ processClassesRecursively(group, classMap, classGroupId, theme);
167
+ }
168
+ return classMap;
169
+ };
170
+ var processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {
171
+ const len = classGroup.length;
172
+ for (let i = 0; i < len; i++) {
173
+ const classDefinition = classGroup[i];
174
+ processClassDefinition(classDefinition, classPartObject, classGroupId, theme);
175
+ }
176
+ };
177
+ var processClassDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
178
+ if (typeof classDefinition === "string") {
179
+ processStringDefinition(classDefinition, classPartObject, classGroupId);
180
+ return;
181
+ }
182
+ if (typeof classDefinition === "function") {
183
+ processFunctionDefinition(classDefinition, classPartObject, classGroupId, theme);
184
+ return;
185
+ }
186
+ processObjectDefinition(classDefinition, classPartObject, classGroupId, theme);
187
+ };
188
+ var processStringDefinition = (classDefinition, classPartObject, classGroupId) => {
189
+ const classPartObjectToEdit = classDefinition === "" ? classPartObject : getPart(classPartObject, classDefinition);
190
+ classPartObjectToEdit.classGroupId = classGroupId;
191
+ };
192
+ var processFunctionDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
193
+ if (isThemeGetter(classDefinition)) {
194
+ processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);
195
+ return;
196
+ }
197
+ if (classPartObject.validators === null) {
198
+ classPartObject.validators = [];
199
+ }
200
+ classPartObject.validators.push(createClassValidatorObject(classGroupId, classDefinition));
201
+ };
202
+ var processObjectDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
203
+ const entries = Object.entries(classDefinition);
204
+ const len = entries.length;
205
+ for (let i = 0; i < len; i++) {
206
+ const [key, value] = entries[i];
207
+ processClassesRecursively(value, getPart(classPartObject, key), classGroupId, theme);
208
+ }
209
+ };
210
+ var getPart = (classPartObject, path) => {
211
+ let current = classPartObject;
212
+ const parts = path.split(CLASS_PART_SEPARATOR);
213
+ const len = parts.length;
214
+ for (let i = 0; i < len; i++) {
215
+ const part = parts[i];
216
+ let next = current.nextPart.get(part);
217
+ if (!next) {
218
+ next = createClassPartObject();
219
+ current.nextPart.set(part, next);
220
+ }
221
+ current = next;
222
+ }
223
+ return current;
224
+ };
225
+ var isThemeGetter = (func) => "isThemeGetter" in func && func.isThemeGetter === true;
226
+ var createLruCache = (maxCacheSize) => {
227
+ if (maxCacheSize < 1) {
228
+ return {
229
+ get: () => void 0,
230
+ set: () => {
231
+ }
232
+ };
233
+ }
234
+ let cacheSize = 0;
235
+ let cache = /* @__PURE__ */ Object.create(null);
236
+ let previousCache = /* @__PURE__ */ Object.create(null);
237
+ const update = (key, value) => {
238
+ cache[key] = value;
239
+ cacheSize++;
240
+ if (cacheSize > maxCacheSize) {
241
+ cacheSize = 0;
242
+ previousCache = cache;
243
+ cache = /* @__PURE__ */ Object.create(null);
244
+ }
245
+ };
246
+ return {
247
+ get(key) {
248
+ let value = cache[key];
249
+ if (value !== void 0) {
250
+ return value;
251
+ }
252
+ if ((value = previousCache[key]) !== void 0) {
253
+ update(key, value);
254
+ return value;
255
+ }
256
+ },
257
+ set(key, value) {
258
+ if (key in cache) {
259
+ cache[key] = value;
260
+ } else {
261
+ update(key, value);
262
+ }
263
+ }
264
+ };
265
+ };
266
+ var IMPORTANT_MODIFIER = "!";
267
+ var MODIFIER_SEPARATOR = ":";
268
+ var EMPTY_MODIFIERS = [];
269
+ var createResultObject = (modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition, isExternal) => ({
270
+ modifiers,
271
+ hasImportantModifier,
272
+ baseClassName,
273
+ maybePostfixModifierPosition,
274
+ isExternal
275
+ });
276
+ var createParseClassName = (config) => {
277
+ const {
278
+ prefix,
279
+ experimentalParseClassName
280
+ } = config;
281
+ let parseClassName = (className) => {
282
+ const modifiers = [];
283
+ let bracketDepth = 0;
284
+ let parenDepth = 0;
285
+ let modifierStart = 0;
286
+ let postfixModifierPosition;
287
+ const len = className.length;
288
+ for (let index = 0; index < len; index++) {
289
+ const currentCharacter = className[index];
290
+ if (bracketDepth === 0 && parenDepth === 0) {
291
+ if (currentCharacter === MODIFIER_SEPARATOR) {
292
+ modifiers.push(className.slice(modifierStart, index));
293
+ modifierStart = index + 1;
294
+ continue;
295
+ }
296
+ if (currentCharacter === "/") {
297
+ postfixModifierPosition = index;
298
+ continue;
299
+ }
300
+ }
301
+ if (currentCharacter === "[") bracketDepth++;
302
+ else if (currentCharacter === "]") bracketDepth--;
303
+ else if (currentCharacter === "(") parenDepth++;
304
+ else if (currentCharacter === ")") parenDepth--;
305
+ }
306
+ const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.slice(modifierStart);
307
+ let baseClassName = baseClassNameWithImportantModifier;
308
+ let hasImportantModifier = false;
309
+ if (baseClassNameWithImportantModifier.endsWith(IMPORTANT_MODIFIER)) {
310
+ baseClassName = baseClassNameWithImportantModifier.slice(0, -1);
311
+ hasImportantModifier = true;
312
+ } else if (
313
+ /**
314
+ * In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.
315
+ * @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864
316
+ */
317
+ baseClassNameWithImportantModifier.startsWith(IMPORTANT_MODIFIER)
318
+ ) {
319
+ baseClassName = baseClassNameWithImportantModifier.slice(1);
320
+ hasImportantModifier = true;
321
+ }
322
+ const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : void 0;
323
+ return createResultObject(modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition);
324
+ };
325
+ if (prefix) {
326
+ const fullPrefix = prefix + MODIFIER_SEPARATOR;
327
+ const parseClassNameOriginal = parseClassName;
328
+ parseClassName = (className) => className.startsWith(fullPrefix) ? parseClassNameOriginal(className.slice(fullPrefix.length)) : createResultObject(EMPTY_MODIFIERS, false, className, void 0, true);
329
+ }
330
+ if (experimentalParseClassName) {
331
+ const parseClassNameOriginal = parseClassName;
332
+ parseClassName = (className) => experimentalParseClassName({
333
+ className,
334
+ parseClassName: parseClassNameOriginal
335
+ });
336
+ }
337
+ return parseClassName;
338
+ };
339
+ var createSortModifiers = (config) => {
340
+ const modifierWeights = /* @__PURE__ */ new Map();
341
+ config.orderSensitiveModifiers.forEach((mod, index) => {
342
+ modifierWeights.set(mod, 1e6 + index);
343
+ });
344
+ return (modifiers) => {
345
+ const result = [];
346
+ let currentSegment = [];
347
+ for (let i = 0; i < modifiers.length; i++) {
348
+ const modifier = modifiers[i];
349
+ const isArbitrary = modifier[0] === "[";
350
+ const isOrderSensitive = modifierWeights.has(modifier);
351
+ if (isArbitrary || isOrderSensitive) {
352
+ if (currentSegment.length > 0) {
353
+ currentSegment.sort();
354
+ result.push(...currentSegment);
355
+ currentSegment = [];
356
+ }
357
+ result.push(modifier);
358
+ } else {
359
+ currentSegment.push(modifier);
360
+ }
361
+ }
362
+ if (currentSegment.length > 0) {
363
+ currentSegment.sort();
364
+ result.push(...currentSegment);
365
+ }
366
+ return result;
367
+ };
368
+ };
369
+ var createConfigUtils = (config) => ({
370
+ cache: createLruCache(config.cacheSize),
371
+ parseClassName: createParseClassName(config),
372
+ sortModifiers: createSortModifiers(config),
373
+ postfixLookupClassGroupIds: createPostfixLookupClassGroupIds(config),
374
+ ...createClassGroupUtils(config)
375
+ });
376
+ var createPostfixLookupClassGroupIds = (config) => {
377
+ const lookup = /* @__PURE__ */ Object.create(null);
378
+ const classGroupIds = config.postfixLookupClassGroups;
379
+ if (classGroupIds) {
380
+ for (let i = 0; i < classGroupIds.length; i++) {
381
+ lookup[classGroupIds[i]] = true;
382
+ }
383
+ }
384
+ return lookup;
385
+ };
386
+ var SPLIT_CLASSES_REGEX = /\s+/;
387
+ var mergeClassList = (classList, configUtils) => {
388
+ const {
389
+ parseClassName,
390
+ getClassGroupId,
391
+ getConflictingClassGroupIds,
392
+ sortModifiers,
393
+ postfixLookupClassGroupIds
394
+ } = configUtils;
395
+ const classGroupsInConflict = [];
396
+ const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);
397
+ let result = "";
398
+ for (let index = classNames.length - 1; index >= 0; index -= 1) {
399
+ const originalClassName = classNames[index];
400
+ const {
401
+ isExternal,
402
+ modifiers,
403
+ hasImportantModifier,
404
+ baseClassName,
405
+ maybePostfixModifierPosition
406
+ } = parseClassName(originalClassName);
407
+ if (isExternal) {
408
+ result = originalClassName + (result.length > 0 ? " " + result : result);
409
+ continue;
410
+ }
411
+ let hasPostfixModifier = !!maybePostfixModifierPosition;
412
+ let classGroupId;
413
+ if (hasPostfixModifier) {
414
+ const baseClassNameWithoutPostfix = baseClassName.substring(0, maybePostfixModifierPosition);
415
+ classGroupId = getClassGroupId(baseClassNameWithoutPostfix);
416
+ const classGroupIdWithPostfix = classGroupId && postfixLookupClassGroupIds[classGroupId] ? getClassGroupId(baseClassName) : void 0;
417
+ if (classGroupIdWithPostfix && classGroupIdWithPostfix !== classGroupId) {
418
+ classGroupId = classGroupIdWithPostfix;
419
+ hasPostfixModifier = false;
420
+ }
421
+ } else {
422
+ classGroupId = getClassGroupId(baseClassName);
423
+ }
424
+ if (!classGroupId) {
425
+ if (!hasPostfixModifier) {
426
+ result = originalClassName + (result.length > 0 ? " " + result : result);
427
+ continue;
428
+ }
429
+ classGroupId = getClassGroupId(baseClassName);
430
+ if (!classGroupId) {
431
+ result = originalClassName + (result.length > 0 ? " " + result : result);
432
+ continue;
433
+ }
434
+ hasPostfixModifier = false;
435
+ }
436
+ const variantModifier = modifiers.length === 0 ? "" : modifiers.length === 1 ? modifiers[0] : sortModifiers(modifiers).join(":");
437
+ const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;
438
+ const classId = modifierId + classGroupId;
439
+ if (classGroupsInConflict.indexOf(classId) > -1) {
440
+ continue;
441
+ }
442
+ classGroupsInConflict.push(classId);
443
+ const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);
444
+ for (let i = 0; i < conflictGroups.length; ++i) {
445
+ const group = conflictGroups[i];
446
+ classGroupsInConflict.push(modifierId + group);
447
+ }
448
+ result = originalClassName + (result.length > 0 ? " " + result : result);
449
+ }
450
+ return result;
451
+ };
452
+ var twJoin = (...classLists) => {
453
+ let index = 0;
454
+ let argument;
455
+ let resolvedValue;
456
+ let string = "";
457
+ while (index < classLists.length) {
458
+ if (argument = classLists[index++]) {
459
+ if (resolvedValue = toValue(argument)) {
460
+ string && (string += " ");
461
+ string += resolvedValue;
462
+ }
463
+ }
464
+ }
465
+ return string;
466
+ };
467
+ var toValue = (mix) => {
468
+ if (typeof mix === "string") {
469
+ return mix;
470
+ }
471
+ let resolvedValue;
472
+ let string = "";
473
+ for (let k = 0; k < mix.length; k++) {
474
+ if (mix[k]) {
475
+ if (resolvedValue = toValue(mix[k])) {
476
+ string && (string += " ");
477
+ string += resolvedValue;
478
+ }
479
+ }
480
+ }
481
+ return string;
482
+ };
483
+ var createTailwindMerge = (createConfigFirst, ...createConfigRest) => {
484
+ let configUtils;
485
+ let cacheGet;
486
+ let cacheSet;
487
+ let functionToCall;
488
+ const initTailwindMerge = (classList) => {
489
+ const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());
490
+ configUtils = createConfigUtils(config);
491
+ cacheGet = configUtils.cache.get;
492
+ cacheSet = configUtils.cache.set;
493
+ functionToCall = tailwindMerge;
494
+ return tailwindMerge(classList);
495
+ };
496
+ const tailwindMerge = (classList) => {
497
+ const cachedResult = cacheGet(classList);
498
+ if (cachedResult) {
499
+ return cachedResult;
500
+ }
501
+ const result = mergeClassList(classList, configUtils);
502
+ cacheSet(classList, result);
503
+ return result;
504
+ };
505
+ functionToCall = initTailwindMerge;
506
+ return (...args) => functionToCall(twJoin(...args));
507
+ };
508
+ var fallbackThemeArr = [];
509
+ var fromTheme = (key) => {
510
+ const themeGetter = (theme) => theme[key] || fallbackThemeArr;
511
+ themeGetter.isThemeGetter = true;
512
+ return themeGetter;
513
+ };
514
+ var arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i;
515
+ var arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i;
516
+ var fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/;
517
+ var tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/;
518
+ var lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/;
519
+ var colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/;
520
+ var shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;
521
+ var imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;
522
+ var isFraction = (value) => fractionRegex.test(value);
523
+ var isNumber = (value) => !!value && !Number.isNaN(Number(value));
524
+ var isInteger = (value) => !!value && Number.isInteger(Number(value));
525
+ var isPercent = (value) => value.endsWith("%") && isNumber(value.slice(0, -1));
526
+ var isTshirtSize = (value) => tshirtUnitRegex.test(value);
527
+ var isAny = () => true;
528
+ var isLengthOnly = (value) => (
529
+ // `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
530
+ // For example, `hsl(0 0% 0%)` would be classified as a length without this check.
531
+ // I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
532
+ lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)
533
+ );
534
+ var isNever = () => false;
535
+ var isShadow = (value) => shadowRegex.test(value);
536
+ var isImage = (value) => imageRegex.test(value);
537
+ var isAnyNonArbitrary = (value) => !isArbitraryValue(value) && !isArbitraryVariable(value);
538
+ var isNamedContainerQuery = (value) => value.startsWith("@container") && (value[10] === "/" && value[11] !== void 0 || value[11] === "s" && value[16] !== void 0 && value.startsWith("-size/", 10) || value[11] === "n" && value[18] !== void 0 && value.startsWith("-normal/", 10));
539
+ var isArbitrarySize = (value) => getIsArbitraryValue(value, isLabelSize, isNever);
540
+ var isArbitraryValue = (value) => arbitraryValueRegex.test(value);
541
+ var isArbitraryLength = (value) => getIsArbitraryValue(value, isLabelLength, isLengthOnly);
542
+ var isArbitraryNumber = (value) => getIsArbitraryValue(value, isLabelNumber, isNumber);
543
+ var isArbitraryWeight = (value) => getIsArbitraryValue(value, isLabelWeight, isAny);
544
+ var isArbitraryFamilyName = (value) => getIsArbitraryValue(value, isLabelFamilyName, isNever);
545
+ var isArbitraryPosition = (value) => getIsArbitraryValue(value, isLabelPosition, isNever);
546
+ var isArbitraryImage = (value) => getIsArbitraryValue(value, isLabelImage, isImage);
547
+ var isArbitraryShadow = (value) => getIsArbitraryValue(value, isLabelShadow, isShadow);
548
+ var isArbitraryVariable = (value) => arbitraryVariableRegex.test(value);
549
+ var isArbitraryVariableLength = (value) => getIsArbitraryVariable(value, isLabelLength);
550
+ var isArbitraryVariableFamilyName = (value) => getIsArbitraryVariable(value, isLabelFamilyName);
551
+ var isArbitraryVariablePosition = (value) => getIsArbitraryVariable(value, isLabelPosition);
552
+ var isArbitraryVariableSize = (value) => getIsArbitraryVariable(value, isLabelSize);
553
+ var isArbitraryVariableImage = (value) => getIsArbitraryVariable(value, isLabelImage);
554
+ var isArbitraryVariableShadow = (value) => getIsArbitraryVariable(value, isLabelShadow, true);
555
+ var isArbitraryVariableWeight = (value) => getIsArbitraryVariable(value, isLabelWeight, true);
556
+ var getIsArbitraryValue = (value, testLabel, testValue) => {
557
+ const result = arbitraryValueRegex.exec(value);
558
+ if (result) {
559
+ if (result[1]) {
560
+ return testLabel(result[1]);
561
+ }
562
+ return testValue(result[2]);
563
+ }
564
+ return false;
565
+ };
566
+ var getIsArbitraryVariable = (value, testLabel, shouldMatchNoLabel = false) => {
567
+ const result = arbitraryVariableRegex.exec(value);
568
+ if (result) {
569
+ if (result[1]) {
570
+ return testLabel(result[1]);
571
+ }
572
+ return shouldMatchNoLabel;
573
+ }
574
+ return false;
575
+ };
576
+ var isLabelPosition = (label) => label === "position" || label === "percentage";
577
+ var isLabelImage = (label) => label === "image" || label === "url";
578
+ var isLabelSize = (label) => label === "length" || label === "size" || label === "bg-size";
579
+ var isLabelLength = (label) => label === "length";
580
+ var isLabelNumber = (label) => label === "number";
581
+ var isLabelFamilyName = (label) => label === "family-name";
582
+ var isLabelWeight = (label) => label === "number" || label === "weight";
583
+ var isLabelShadow = (label) => label === "shadow";
584
+ var getDefaultConfig = () => {
585
+ const themeColor = fromTheme("color");
586
+ const themeFont = fromTheme("font");
587
+ const themeText = fromTheme("text");
588
+ const themeFontWeight = fromTheme("font-weight");
589
+ const themeTracking = fromTheme("tracking");
590
+ const themeLeading = fromTheme("leading");
591
+ const themeBreakpoint = fromTheme("breakpoint");
592
+ const themeContainer = fromTheme("container");
593
+ const themeSpacing = fromTheme("spacing");
594
+ const themeRadius = fromTheme("radius");
595
+ const themeShadow = fromTheme("shadow");
596
+ const themeInsetShadow = fromTheme("inset-shadow");
597
+ const themeTextShadow = fromTheme("text-shadow");
598
+ const themeDropShadow = fromTheme("drop-shadow");
599
+ const themeBlur = fromTheme("blur");
600
+ const themePerspective = fromTheme("perspective");
601
+ const themeAspect = fromTheme("aspect");
602
+ const themeEase = fromTheme("ease");
603
+ const themeAnimate = fromTheme("animate");
604
+ const scaleBreak = () => ["auto", "avoid", "all", "avoid-page", "page", "left", "right", "column"];
605
+ const scalePosition = () => [
606
+ "center",
607
+ "top",
608
+ "bottom",
609
+ "left",
610
+ "right",
611
+ "top-left",
612
+ // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378
613
+ "left-top",
614
+ "top-right",
615
+ // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378
616
+ "right-top",
617
+ "bottom-right",
618
+ // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378
619
+ "right-bottom",
620
+ "bottom-left",
621
+ // Deprecated since Tailwind CSS v4.1.0, see https://github.com/tailwindlabs/tailwindcss/pull/17378
622
+ "left-bottom"
623
+ ];
624
+ const scalePositionWithArbitrary = () => [...scalePosition(), isArbitraryVariable, isArbitraryValue];
625
+ const scaleOverflow = () => ["auto", "hidden", "clip", "visible", "scroll"];
626
+ const scaleOverscroll = () => ["auto", "contain", "none"];
627
+ const scaleUnambiguousSpacing = () => [isArbitraryVariable, isArbitraryValue, themeSpacing];
628
+ const scaleInset = () => [isFraction, "full", "auto", ...scaleUnambiguousSpacing()];
629
+ const scaleGridTemplateColsRows = () => [isInteger, "none", "subgrid", isArbitraryVariable, isArbitraryValue];
630
+ const scaleGridColRowStartAndEnd = () => ["auto", {
631
+ span: ["full", isInteger, isArbitraryVariable, isArbitraryValue]
632
+ }, isInteger, isArbitraryVariable, isArbitraryValue];
633
+ const scaleGridColRowStartOrEnd = () => [isInteger, "auto", isArbitraryVariable, isArbitraryValue];
634
+ const scaleGridAutoColsRows = () => ["auto", "min", "max", "fr", isArbitraryVariable, isArbitraryValue];
635
+ const scaleAlignPrimaryAxis = () => ["start", "end", "center", "between", "around", "evenly", "stretch", "baseline", "center-safe", "end-safe"];
636
+ const scaleAlignSecondaryAxis = () => ["start", "end", "center", "stretch", "center-safe", "end-safe"];
637
+ const scaleMargin = () => ["auto", ...scaleUnambiguousSpacing()];
638
+ const scaleSizing = () => [isFraction, "auto", "full", "dvw", "dvh", "lvw", "lvh", "svw", "svh", "min", "max", "fit", ...scaleUnambiguousSpacing()];
639
+ const scaleSizingInline = () => [isFraction, "screen", "full", "dvw", "lvw", "svw", "min", "max", "fit", ...scaleUnambiguousSpacing()];
640
+ const scaleSizingBlock = () => [isFraction, "screen", "full", "lh", "dvh", "lvh", "svh", "min", "max", "fit", ...scaleUnambiguousSpacing()];
641
+ const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];
642
+ const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {
643
+ position: [isArbitraryVariable, isArbitraryValue]
644
+ }];
645
+ const scaleBgRepeat = () => ["no-repeat", {
646
+ repeat: ["", "x", "y", "space", "round"]
647
+ }];
648
+ const scaleBgSize = () => ["auto", "cover", "contain", isArbitraryVariableSize, isArbitrarySize, {
649
+ size: [isArbitraryVariable, isArbitraryValue]
650
+ }];
651
+ const scaleGradientStopPosition = () => [isPercent, isArbitraryVariableLength, isArbitraryLength];
652
+ const scaleRadius = () => [
653
+ // Deprecated since Tailwind CSS v4.0.0
654
+ "",
655
+ "none",
656
+ "full",
657
+ themeRadius,
658
+ isArbitraryVariable,
659
+ isArbitraryValue
660
+ ];
661
+ const scaleBorderWidth = () => ["", isNumber, isArbitraryVariableLength, isArbitraryLength];
662
+ const scaleLineStyle = () => ["solid", "dashed", "dotted", "double"];
663
+ const scaleBlendMode = () => ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"];
664
+ const scaleMaskImagePosition = () => [isNumber, isPercent, isArbitraryVariablePosition, isArbitraryPosition];
665
+ const scaleBlur = () => [
666
+ // Deprecated since Tailwind CSS v4.0.0
667
+ "",
668
+ "none",
669
+ themeBlur,
670
+ isArbitraryVariable,
671
+ isArbitraryValue
672
+ ];
673
+ const scaleRotate = () => ["none", isNumber, isArbitraryVariable, isArbitraryValue];
674
+ const scaleScale = () => ["none", isNumber, isArbitraryVariable, isArbitraryValue];
675
+ const scaleSkew = () => [isNumber, isArbitraryVariable, isArbitraryValue];
676
+ const scaleTranslate = () => [isFraction, "full", ...scaleUnambiguousSpacing()];
677
+ return {
678
+ cacheSize: 500,
679
+ theme: {
680
+ animate: ["spin", "ping", "pulse", "bounce"],
681
+ aspect: ["video"],
682
+ blur: [isTshirtSize],
683
+ breakpoint: [isTshirtSize],
684
+ color: [isAny],
685
+ container: [isTshirtSize],
686
+ "drop-shadow": [isTshirtSize],
687
+ ease: ["in", "out", "in-out"],
688
+ font: [isAnyNonArbitrary],
689
+ "font-weight": ["thin", "extralight", "light", "normal", "medium", "semibold", "bold", "extrabold", "black"],
690
+ "inset-shadow": [isTshirtSize],
691
+ leading: ["none", "tight", "snug", "normal", "relaxed", "loose"],
692
+ perspective: ["dramatic", "near", "normal", "midrange", "distant", "none"],
693
+ radius: [isTshirtSize],
694
+ shadow: [isTshirtSize],
695
+ spacing: ["px", isNumber],
696
+ text: [isTshirtSize],
697
+ "text-shadow": [isTshirtSize],
698
+ tracking: ["tighter", "tight", "normal", "wide", "wider", "widest"]
699
+ },
700
+ classGroups: {
701
+ // --------------
702
+ // --- Layout ---
703
+ // --------------
704
+ /**
705
+ * Aspect Ratio
706
+ * @see https://tailwindcss.com/docs/aspect-ratio
707
+ */
708
+ aspect: [{
709
+ aspect: ["auto", "square", isFraction, isArbitraryValue, isArbitraryVariable, themeAspect]
710
+ }],
711
+ /**
712
+ * Container
713
+ * @see https://tailwindcss.com/docs/container
714
+ * @deprecated since Tailwind CSS v4.0.0
715
+ */
716
+ container: ["container"],
717
+ /**
718
+ * Container Type
719
+ * @see https://tailwindcss.com/docs/responsive-design#container-queries
720
+ */
721
+ "container-type": [{
722
+ "@container": ["", "normal", "size", isArbitraryVariable, isArbitraryValue]
723
+ }],
724
+ /**
725
+ * Container Name
726
+ * @see https://tailwindcss.com/docs/responsive-design#named-containers
727
+ */
728
+ "container-named": [isNamedContainerQuery],
729
+ /**
730
+ * Columns
731
+ * @see https://tailwindcss.com/docs/columns
732
+ */
733
+ columns: [{
734
+ columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]
735
+ }],
736
+ /**
737
+ * Break After
738
+ * @see https://tailwindcss.com/docs/break-after
739
+ */
740
+ "break-after": [{
741
+ "break-after": scaleBreak()
742
+ }],
743
+ /**
744
+ * Break Before
745
+ * @see https://tailwindcss.com/docs/break-before
746
+ */
747
+ "break-before": [{
748
+ "break-before": scaleBreak()
749
+ }],
750
+ /**
751
+ * Break Inside
752
+ * @see https://tailwindcss.com/docs/break-inside
753
+ */
754
+ "break-inside": [{
755
+ "break-inside": ["auto", "avoid", "avoid-page", "avoid-column"]
756
+ }],
757
+ /**
758
+ * Box Decoration Break
759
+ * @see https://tailwindcss.com/docs/box-decoration-break
760
+ */
761
+ "box-decoration": [{
762
+ "box-decoration": ["slice", "clone"]
763
+ }],
764
+ /**
765
+ * Box Sizing
766
+ * @see https://tailwindcss.com/docs/box-sizing
767
+ */
768
+ box: [{
769
+ box: ["border", "content"]
770
+ }],
771
+ /**
772
+ * Display
773
+ * @see https://tailwindcss.com/docs/display
774
+ */
775
+ display: ["block", "inline-block", "inline", "flex", "inline-flex", "table", "inline-table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row-group", "table-row", "flow-root", "grid", "inline-grid", "contents", "list-item", "hidden"],
776
+ /**
777
+ * Screen Reader Only
778
+ * @see https://tailwindcss.com/docs/display#screen-reader-only
779
+ */
780
+ sr: ["sr-only", "not-sr-only"],
781
+ /**
782
+ * Floats
783
+ * @see https://tailwindcss.com/docs/float
784
+ */
785
+ float: [{
786
+ float: ["right", "left", "none", "start", "end"]
787
+ }],
788
+ /**
789
+ * Clear
790
+ * @see https://tailwindcss.com/docs/clear
791
+ */
792
+ clear: [{
793
+ clear: ["left", "right", "both", "none", "start", "end"]
794
+ }],
795
+ /**
796
+ * Isolation
797
+ * @see https://tailwindcss.com/docs/isolation
798
+ */
799
+ isolation: ["isolate", "isolation-auto"],
800
+ /**
801
+ * Object Fit
802
+ * @see https://tailwindcss.com/docs/object-fit
803
+ */
804
+ "object-fit": [{
805
+ object: ["contain", "cover", "fill", "none", "scale-down"]
806
+ }],
807
+ /**
808
+ * Object Position
809
+ * @see https://tailwindcss.com/docs/object-position
810
+ */
811
+ "object-position": [{
812
+ object: scalePositionWithArbitrary()
813
+ }],
814
+ /**
815
+ * Overflow
816
+ * @see https://tailwindcss.com/docs/overflow
817
+ */
818
+ overflow: [{
819
+ overflow: scaleOverflow()
820
+ }],
821
+ /**
822
+ * Overflow X
823
+ * @see https://tailwindcss.com/docs/overflow
824
+ */
825
+ "overflow-x": [{
826
+ "overflow-x": scaleOverflow()
827
+ }],
828
+ /**
829
+ * Overflow Y
830
+ * @see https://tailwindcss.com/docs/overflow
831
+ */
832
+ "overflow-y": [{
833
+ "overflow-y": scaleOverflow()
834
+ }],
835
+ /**
836
+ * Overscroll Behavior
837
+ * @see https://tailwindcss.com/docs/overscroll-behavior
838
+ */
839
+ overscroll: [{
840
+ overscroll: scaleOverscroll()
841
+ }],
842
+ /**
843
+ * Overscroll Behavior X
844
+ * @see https://tailwindcss.com/docs/overscroll-behavior
845
+ */
846
+ "overscroll-x": [{
847
+ "overscroll-x": scaleOverscroll()
848
+ }],
849
+ /**
850
+ * Overscroll Behavior Y
851
+ * @see https://tailwindcss.com/docs/overscroll-behavior
852
+ */
853
+ "overscroll-y": [{
854
+ "overscroll-y": scaleOverscroll()
855
+ }],
856
+ /**
857
+ * Position
858
+ * @see https://tailwindcss.com/docs/position
859
+ */
860
+ position: ["static", "fixed", "absolute", "relative", "sticky"],
861
+ /**
862
+ * Inset
863
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
864
+ */
865
+ inset: [{
866
+ inset: scaleInset()
867
+ }],
868
+ /**
869
+ * Inset Inline
870
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
871
+ */
872
+ "inset-x": [{
873
+ "inset-x": scaleInset()
874
+ }],
875
+ /**
876
+ * Inset Block
877
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
878
+ */
879
+ "inset-y": [{
880
+ "inset-y": scaleInset()
881
+ }],
882
+ /**
883
+ * Inset Inline Start
884
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
885
+ * @todo class group will be renamed to `inset-s` in next major release
886
+ */
887
+ start: [{
888
+ "inset-s": scaleInset(),
889
+ /**
890
+ * @deprecated since Tailwind CSS v4.2.0 in favor of `inset-s-*` utilities.
891
+ * @see https://github.com/tailwindlabs/tailwindcss/pull/19613
892
+ */
893
+ start: scaleInset()
894
+ }],
895
+ /**
896
+ * Inset Inline End
897
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
898
+ * @todo class group will be renamed to `inset-e` in next major release
899
+ */
900
+ end: [{
901
+ "inset-e": scaleInset(),
902
+ /**
903
+ * @deprecated since Tailwind CSS v4.2.0 in favor of `inset-e-*` utilities.
904
+ * @see https://github.com/tailwindlabs/tailwindcss/pull/19613
905
+ */
906
+ end: scaleInset()
907
+ }],
908
+ /**
909
+ * Inset Block Start
910
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
911
+ */
912
+ "inset-bs": [{
913
+ "inset-bs": scaleInset()
914
+ }],
915
+ /**
916
+ * Inset Block End
917
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
918
+ */
919
+ "inset-be": [{
920
+ "inset-be": scaleInset()
921
+ }],
922
+ /**
923
+ * Top
924
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
925
+ */
926
+ top: [{
927
+ top: scaleInset()
928
+ }],
929
+ /**
930
+ * Right
931
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
932
+ */
933
+ right: [{
934
+ right: scaleInset()
935
+ }],
936
+ /**
937
+ * Bottom
938
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
939
+ */
940
+ bottom: [{
941
+ bottom: scaleInset()
942
+ }],
943
+ /**
944
+ * Left
945
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
946
+ */
947
+ left: [{
948
+ left: scaleInset()
949
+ }],
950
+ /**
951
+ * Visibility
952
+ * @see https://tailwindcss.com/docs/visibility
953
+ */
954
+ visibility: ["visible", "invisible", "collapse"],
955
+ /**
956
+ * Z-Index
957
+ * @see https://tailwindcss.com/docs/z-index
958
+ */
959
+ z: [{
960
+ z: [isInteger, "auto", isArbitraryVariable, isArbitraryValue]
961
+ }],
962
+ // ------------------------
963
+ // --- Flexbox and Grid ---
964
+ // ------------------------
965
+ /**
966
+ * Flex Basis
967
+ * @see https://tailwindcss.com/docs/flex-basis
968
+ */
969
+ basis: [{
970
+ basis: [isFraction, "full", "auto", themeContainer, ...scaleUnambiguousSpacing()]
971
+ }],
972
+ /**
973
+ * Flex Direction
974
+ * @see https://tailwindcss.com/docs/flex-direction
975
+ */
976
+ "flex-direction": [{
977
+ flex: ["row", "row-reverse", "col", "col-reverse"]
978
+ }],
979
+ /**
980
+ * Flex Wrap
981
+ * @see https://tailwindcss.com/docs/flex-wrap
982
+ */
983
+ "flex-wrap": [{
984
+ flex: ["nowrap", "wrap", "wrap-reverse"]
985
+ }],
986
+ /**
987
+ * Flex
988
+ * @see https://tailwindcss.com/docs/flex
989
+ */
990
+ flex: [{
991
+ flex: [isNumber, isFraction, "auto", "initial", "none", isArbitraryValue]
992
+ }],
993
+ /**
994
+ * Flex Grow
995
+ * @see https://tailwindcss.com/docs/flex-grow
996
+ */
997
+ grow: [{
998
+ grow: ["", isNumber, isArbitraryVariable, isArbitraryValue]
999
+ }],
1000
+ /**
1001
+ * Flex Shrink
1002
+ * @see https://tailwindcss.com/docs/flex-shrink
1003
+ */
1004
+ shrink: [{
1005
+ shrink: ["", isNumber, isArbitraryVariable, isArbitraryValue]
1006
+ }],
1007
+ /**
1008
+ * Order
1009
+ * @see https://tailwindcss.com/docs/order
1010
+ */
1011
+ order: [{
1012
+ order: [isInteger, "first", "last", "none", isArbitraryVariable, isArbitraryValue]
1013
+ }],
1014
+ /**
1015
+ * Grid Template Columns
1016
+ * @see https://tailwindcss.com/docs/grid-template-columns
1017
+ */
1018
+ "grid-cols": [{
1019
+ "grid-cols": scaleGridTemplateColsRows()
1020
+ }],
1021
+ /**
1022
+ * Grid Column Start / End
1023
+ * @see https://tailwindcss.com/docs/grid-column
1024
+ */
1025
+ "col-start-end": [{
1026
+ col: scaleGridColRowStartAndEnd()
1027
+ }],
1028
+ /**
1029
+ * Grid Column Start
1030
+ * @see https://tailwindcss.com/docs/grid-column
1031
+ */
1032
+ "col-start": [{
1033
+ "col-start": scaleGridColRowStartOrEnd()
1034
+ }],
1035
+ /**
1036
+ * Grid Column End
1037
+ * @see https://tailwindcss.com/docs/grid-column
1038
+ */
1039
+ "col-end": [{
1040
+ "col-end": scaleGridColRowStartOrEnd()
1041
+ }],
1042
+ /**
1043
+ * Grid Template Rows
1044
+ * @see https://tailwindcss.com/docs/grid-template-rows
1045
+ */
1046
+ "grid-rows": [{
1047
+ "grid-rows": scaleGridTemplateColsRows()
1048
+ }],
1049
+ /**
1050
+ * Grid Row Start / End
1051
+ * @see https://tailwindcss.com/docs/grid-row
1052
+ */
1053
+ "row-start-end": [{
1054
+ row: scaleGridColRowStartAndEnd()
1055
+ }],
1056
+ /**
1057
+ * Grid Row Start
1058
+ * @see https://tailwindcss.com/docs/grid-row
1059
+ */
1060
+ "row-start": [{
1061
+ "row-start": scaleGridColRowStartOrEnd()
1062
+ }],
1063
+ /**
1064
+ * Grid Row End
1065
+ * @see https://tailwindcss.com/docs/grid-row
1066
+ */
1067
+ "row-end": [{
1068
+ "row-end": scaleGridColRowStartOrEnd()
1069
+ }],
1070
+ /**
1071
+ * Grid Auto Flow
1072
+ * @see https://tailwindcss.com/docs/grid-auto-flow
1073
+ */
1074
+ "grid-flow": [{
1075
+ "grid-flow": ["row", "col", "dense", "row-dense", "col-dense"]
1076
+ }],
1077
+ /**
1078
+ * Grid Auto Columns
1079
+ * @see https://tailwindcss.com/docs/grid-auto-columns
1080
+ */
1081
+ "auto-cols": [{
1082
+ "auto-cols": scaleGridAutoColsRows()
1083
+ }],
1084
+ /**
1085
+ * Grid Auto Rows
1086
+ * @see https://tailwindcss.com/docs/grid-auto-rows
1087
+ */
1088
+ "auto-rows": [{
1089
+ "auto-rows": scaleGridAutoColsRows()
1090
+ }],
1091
+ /**
1092
+ * Gap
1093
+ * @see https://tailwindcss.com/docs/gap
1094
+ */
1095
+ gap: [{
1096
+ gap: scaleUnambiguousSpacing()
1097
+ }],
1098
+ /**
1099
+ * Gap X
1100
+ * @see https://tailwindcss.com/docs/gap
1101
+ */
1102
+ "gap-x": [{
1103
+ "gap-x": scaleUnambiguousSpacing()
1104
+ }],
1105
+ /**
1106
+ * Gap Y
1107
+ * @see https://tailwindcss.com/docs/gap
1108
+ */
1109
+ "gap-y": [{
1110
+ "gap-y": scaleUnambiguousSpacing()
1111
+ }],
1112
+ /**
1113
+ * Justify Content
1114
+ * @see https://tailwindcss.com/docs/justify-content
1115
+ */
1116
+ "justify-content": [{
1117
+ justify: [...scaleAlignPrimaryAxis(), "normal"]
1118
+ }],
1119
+ /**
1120
+ * Justify Items
1121
+ * @see https://tailwindcss.com/docs/justify-items
1122
+ */
1123
+ "justify-items": [{
1124
+ "justify-items": [...scaleAlignSecondaryAxis(), "normal"]
1125
+ }],
1126
+ /**
1127
+ * Justify Self
1128
+ * @see https://tailwindcss.com/docs/justify-self
1129
+ */
1130
+ "justify-self": [{
1131
+ "justify-self": ["auto", ...scaleAlignSecondaryAxis()]
1132
+ }],
1133
+ /**
1134
+ * Align Content
1135
+ * @see https://tailwindcss.com/docs/align-content
1136
+ */
1137
+ "align-content": [{
1138
+ content: ["normal", ...scaleAlignPrimaryAxis()]
1139
+ }],
1140
+ /**
1141
+ * Align Items
1142
+ * @see https://tailwindcss.com/docs/align-items
1143
+ */
1144
+ "align-items": [{
1145
+ items: [...scaleAlignSecondaryAxis(), {
1146
+ baseline: ["", "last"]
1147
+ }]
1148
+ }],
1149
+ /**
1150
+ * Align Self
1151
+ * @see https://tailwindcss.com/docs/align-self
1152
+ */
1153
+ "align-self": [{
1154
+ self: ["auto", ...scaleAlignSecondaryAxis(), {
1155
+ baseline: ["", "last"]
1156
+ }]
1157
+ }],
1158
+ /**
1159
+ * Place Content
1160
+ * @see https://tailwindcss.com/docs/place-content
1161
+ */
1162
+ "place-content": [{
1163
+ "place-content": scaleAlignPrimaryAxis()
1164
+ }],
1165
+ /**
1166
+ * Place Items
1167
+ * @see https://tailwindcss.com/docs/place-items
1168
+ */
1169
+ "place-items": [{
1170
+ "place-items": [...scaleAlignSecondaryAxis(), "baseline"]
1171
+ }],
1172
+ /**
1173
+ * Place Self
1174
+ * @see https://tailwindcss.com/docs/place-self
1175
+ */
1176
+ "place-self": [{
1177
+ "place-self": ["auto", ...scaleAlignSecondaryAxis()]
1178
+ }],
1179
+ // Spacing
1180
+ /**
1181
+ * Padding
1182
+ * @see https://tailwindcss.com/docs/padding
1183
+ */
1184
+ p: [{
1185
+ p: scaleUnambiguousSpacing()
1186
+ }],
1187
+ /**
1188
+ * Padding Inline
1189
+ * @see https://tailwindcss.com/docs/padding
1190
+ */
1191
+ px: [{
1192
+ px: scaleUnambiguousSpacing()
1193
+ }],
1194
+ /**
1195
+ * Padding Block
1196
+ * @see https://tailwindcss.com/docs/padding
1197
+ */
1198
+ py: [{
1199
+ py: scaleUnambiguousSpacing()
1200
+ }],
1201
+ /**
1202
+ * Padding Inline Start
1203
+ * @see https://tailwindcss.com/docs/padding
1204
+ */
1205
+ ps: [{
1206
+ ps: scaleUnambiguousSpacing()
1207
+ }],
1208
+ /**
1209
+ * Padding Inline End
1210
+ * @see https://tailwindcss.com/docs/padding
1211
+ */
1212
+ pe: [{
1213
+ pe: scaleUnambiguousSpacing()
1214
+ }],
1215
+ /**
1216
+ * Padding Block Start
1217
+ * @see https://tailwindcss.com/docs/padding
1218
+ */
1219
+ pbs: [{
1220
+ pbs: scaleUnambiguousSpacing()
1221
+ }],
1222
+ /**
1223
+ * Padding Block End
1224
+ * @see https://tailwindcss.com/docs/padding
1225
+ */
1226
+ pbe: [{
1227
+ pbe: scaleUnambiguousSpacing()
1228
+ }],
1229
+ /**
1230
+ * Padding Top
1231
+ * @see https://tailwindcss.com/docs/padding
1232
+ */
1233
+ pt: [{
1234
+ pt: scaleUnambiguousSpacing()
1235
+ }],
1236
+ /**
1237
+ * Padding Right
1238
+ * @see https://tailwindcss.com/docs/padding
1239
+ */
1240
+ pr: [{
1241
+ pr: scaleUnambiguousSpacing()
1242
+ }],
1243
+ /**
1244
+ * Padding Bottom
1245
+ * @see https://tailwindcss.com/docs/padding
1246
+ */
1247
+ pb: [{
1248
+ pb: scaleUnambiguousSpacing()
1249
+ }],
1250
+ /**
1251
+ * Padding Left
1252
+ * @see https://tailwindcss.com/docs/padding
1253
+ */
1254
+ pl: [{
1255
+ pl: scaleUnambiguousSpacing()
1256
+ }],
1257
+ /**
1258
+ * Margin
1259
+ * @see https://tailwindcss.com/docs/margin
1260
+ */
1261
+ m: [{
1262
+ m: scaleMargin()
1263
+ }],
1264
+ /**
1265
+ * Margin Inline
1266
+ * @see https://tailwindcss.com/docs/margin
1267
+ */
1268
+ mx: [{
1269
+ mx: scaleMargin()
1270
+ }],
1271
+ /**
1272
+ * Margin Block
1273
+ * @see https://tailwindcss.com/docs/margin
1274
+ */
1275
+ my: [{
1276
+ my: scaleMargin()
1277
+ }],
1278
+ /**
1279
+ * Margin Inline Start
1280
+ * @see https://tailwindcss.com/docs/margin
1281
+ */
1282
+ ms: [{
1283
+ ms: scaleMargin()
1284
+ }],
1285
+ /**
1286
+ * Margin Inline End
1287
+ * @see https://tailwindcss.com/docs/margin
1288
+ */
1289
+ me: [{
1290
+ me: scaleMargin()
1291
+ }],
1292
+ /**
1293
+ * Margin Block Start
1294
+ * @see https://tailwindcss.com/docs/margin
1295
+ */
1296
+ mbs: [{
1297
+ mbs: scaleMargin()
1298
+ }],
1299
+ /**
1300
+ * Margin Block End
1301
+ * @see https://tailwindcss.com/docs/margin
1302
+ */
1303
+ mbe: [{
1304
+ mbe: scaleMargin()
1305
+ }],
1306
+ /**
1307
+ * Margin Top
1308
+ * @see https://tailwindcss.com/docs/margin
1309
+ */
1310
+ mt: [{
1311
+ mt: scaleMargin()
1312
+ }],
1313
+ /**
1314
+ * Margin Right
1315
+ * @see https://tailwindcss.com/docs/margin
1316
+ */
1317
+ mr: [{
1318
+ mr: scaleMargin()
1319
+ }],
1320
+ /**
1321
+ * Margin Bottom
1322
+ * @see https://tailwindcss.com/docs/margin
1323
+ */
1324
+ mb: [{
1325
+ mb: scaleMargin()
1326
+ }],
1327
+ /**
1328
+ * Margin Left
1329
+ * @see https://tailwindcss.com/docs/margin
1330
+ */
1331
+ ml: [{
1332
+ ml: scaleMargin()
1333
+ }],
1334
+ /**
1335
+ * Space Between X
1336
+ * @see https://tailwindcss.com/docs/margin#adding-space-between-children
1337
+ */
1338
+ "space-x": [{
1339
+ "space-x": scaleUnambiguousSpacing()
1340
+ }],
1341
+ /**
1342
+ * Space Between X Reverse
1343
+ * @see https://tailwindcss.com/docs/margin#adding-space-between-children
1344
+ */
1345
+ "space-x-reverse": ["space-x-reverse"],
1346
+ /**
1347
+ * Space Between Y
1348
+ * @see https://tailwindcss.com/docs/margin#adding-space-between-children
1349
+ */
1350
+ "space-y": [{
1351
+ "space-y": scaleUnambiguousSpacing()
1352
+ }],
1353
+ /**
1354
+ * Space Between Y Reverse
1355
+ * @see https://tailwindcss.com/docs/margin#adding-space-between-children
1356
+ */
1357
+ "space-y-reverse": ["space-y-reverse"],
1358
+ // --------------
1359
+ // --- Sizing ---
1360
+ // --------------
1361
+ /**
1362
+ * Size
1363
+ * @see https://tailwindcss.com/docs/width#setting-both-width-and-height
1364
+ */
1365
+ size: [{
1366
+ size: scaleSizing()
1367
+ }],
1368
+ /**
1369
+ * Inline Size
1370
+ * @see https://tailwindcss.com/docs/width
1371
+ */
1372
+ "inline-size": [{
1373
+ inline: ["auto", ...scaleSizingInline()]
1374
+ }],
1375
+ /**
1376
+ * Min-Inline Size
1377
+ * @see https://tailwindcss.com/docs/min-width
1378
+ */
1379
+ "min-inline-size": [{
1380
+ "min-inline": ["auto", ...scaleSizingInline()]
1381
+ }],
1382
+ /**
1383
+ * Max-Inline Size
1384
+ * @see https://tailwindcss.com/docs/max-width
1385
+ */
1386
+ "max-inline-size": [{
1387
+ "max-inline": ["none", ...scaleSizingInline()]
1388
+ }],
1389
+ /**
1390
+ * Block Size
1391
+ * @see https://tailwindcss.com/docs/height
1392
+ */
1393
+ "block-size": [{
1394
+ block: ["auto", ...scaleSizingBlock()]
1395
+ }],
1396
+ /**
1397
+ * Min-Block Size
1398
+ * @see https://tailwindcss.com/docs/min-height
1399
+ */
1400
+ "min-block-size": [{
1401
+ "min-block": ["auto", ...scaleSizingBlock()]
1402
+ }],
1403
+ /**
1404
+ * Max-Block Size
1405
+ * @see https://tailwindcss.com/docs/max-height
1406
+ */
1407
+ "max-block-size": [{
1408
+ "max-block": ["none", ...scaleSizingBlock()]
1409
+ }],
1410
+ /**
1411
+ * Width
1412
+ * @see https://tailwindcss.com/docs/width
1413
+ */
1414
+ w: [{
1415
+ w: [themeContainer, "screen", ...scaleSizing()]
1416
+ }],
1417
+ /**
1418
+ * Min-Width
1419
+ * @see https://tailwindcss.com/docs/min-width
1420
+ */
1421
+ "min-w": [{
1422
+ "min-w": [
1423
+ themeContainer,
1424
+ "screen",
1425
+ /** Deprecated. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
1426
+ "none",
1427
+ ...scaleSizing()
1428
+ ]
1429
+ }],
1430
+ /**
1431
+ * Max-Width
1432
+ * @see https://tailwindcss.com/docs/max-width
1433
+ */
1434
+ "max-w": [{
1435
+ "max-w": [
1436
+ themeContainer,
1437
+ "screen",
1438
+ "none",
1439
+ /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
1440
+ "prose",
1441
+ /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
1442
+ {
1443
+ screen: [themeBreakpoint]
1444
+ },
1445
+ ...scaleSizing()
1446
+ ]
1447
+ }],
1448
+ /**
1449
+ * Height
1450
+ * @see https://tailwindcss.com/docs/height
1451
+ */
1452
+ h: [{
1453
+ h: ["screen", "lh", ...scaleSizing()]
1454
+ }],
1455
+ /**
1456
+ * Min-Height
1457
+ * @see https://tailwindcss.com/docs/min-height
1458
+ */
1459
+ "min-h": [{
1460
+ "min-h": ["screen", "lh", "none", ...scaleSizing()]
1461
+ }],
1462
+ /**
1463
+ * Max-Height
1464
+ * @see https://tailwindcss.com/docs/max-height
1465
+ */
1466
+ "max-h": [{
1467
+ "max-h": ["screen", "lh", ...scaleSizing()]
1468
+ }],
1469
+ // ------------------
1470
+ // --- Typography ---
1471
+ // ------------------
1472
+ /**
1473
+ * Font Size
1474
+ * @see https://tailwindcss.com/docs/font-size
1475
+ */
1476
+ "font-size": [{
1477
+ text: ["base", themeText, isArbitraryVariableLength, isArbitraryLength]
1478
+ }],
1479
+ /**
1480
+ * Font Smoothing
1481
+ * @see https://tailwindcss.com/docs/font-smoothing
1482
+ */
1483
+ "font-smoothing": ["antialiased", "subpixel-antialiased"],
1484
+ /**
1485
+ * Font Style
1486
+ * @see https://tailwindcss.com/docs/font-style
1487
+ */
1488
+ "font-style": ["italic", "not-italic"],
1489
+ /**
1490
+ * Font Weight
1491
+ * @see https://tailwindcss.com/docs/font-weight
1492
+ */
1493
+ "font-weight": [{
1494
+ font: [themeFontWeight, isArbitraryVariableWeight, isArbitraryWeight]
1495
+ }],
1496
+ /**
1497
+ * Font Stretch
1498
+ * @see https://tailwindcss.com/docs/font-stretch
1499
+ */
1500
+ "font-stretch": [{
1501
+ "font-stretch": ["ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded", isPercent, isArbitraryValue]
1502
+ }],
1503
+ /**
1504
+ * Font Family
1505
+ * @see https://tailwindcss.com/docs/font-family
1506
+ */
1507
+ "font-family": [{
1508
+ font: [isArbitraryVariableFamilyName, isArbitraryFamilyName, themeFont]
1509
+ }],
1510
+ /**
1511
+ * Font Feature Settings
1512
+ * @see https://tailwindcss.com/docs/font-feature-settings
1513
+ */
1514
+ "font-features": [{
1515
+ "font-features": [isArbitraryValue]
1516
+ }],
1517
+ /**
1518
+ * Font Variant Numeric
1519
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1520
+ */
1521
+ "fvn-normal": ["normal-nums"],
1522
+ /**
1523
+ * Font Variant Numeric
1524
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1525
+ */
1526
+ "fvn-ordinal": ["ordinal"],
1527
+ /**
1528
+ * Font Variant Numeric
1529
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1530
+ */
1531
+ "fvn-slashed-zero": ["slashed-zero"],
1532
+ /**
1533
+ * Font Variant Numeric
1534
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1535
+ */
1536
+ "fvn-figure": ["lining-nums", "oldstyle-nums"],
1537
+ /**
1538
+ * Font Variant Numeric
1539
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1540
+ */
1541
+ "fvn-spacing": ["proportional-nums", "tabular-nums"],
1542
+ /**
1543
+ * Font Variant Numeric
1544
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1545
+ */
1546
+ "fvn-fraction": ["diagonal-fractions", "stacked-fractions"],
1547
+ /**
1548
+ * Letter Spacing
1549
+ * @see https://tailwindcss.com/docs/letter-spacing
1550
+ */
1551
+ tracking: [{
1552
+ tracking: [themeTracking, isArbitraryVariable, isArbitraryValue]
1553
+ }],
1554
+ /**
1555
+ * Line Clamp
1556
+ * @see https://tailwindcss.com/docs/line-clamp
1557
+ */
1558
+ "line-clamp": [{
1559
+ "line-clamp": [isNumber, "none", isArbitraryVariable, isArbitraryNumber]
1560
+ }],
1561
+ /**
1562
+ * Line Height
1563
+ * @see https://tailwindcss.com/docs/line-height
1564
+ */
1565
+ leading: [{
1566
+ leading: [
1567
+ /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
1568
+ themeLeading,
1569
+ ...scaleUnambiguousSpacing()
1570
+ ]
1571
+ }],
1572
+ /**
1573
+ * List Style Image
1574
+ * @see https://tailwindcss.com/docs/list-style-image
1575
+ */
1576
+ "list-image": [{
1577
+ "list-image": ["none", isArbitraryVariable, isArbitraryValue]
1578
+ }],
1579
+ /**
1580
+ * List Style Position
1581
+ * @see https://tailwindcss.com/docs/list-style-position
1582
+ */
1583
+ "list-style-position": [{
1584
+ list: ["inside", "outside"]
1585
+ }],
1586
+ /**
1587
+ * List Style Type
1588
+ * @see https://tailwindcss.com/docs/list-style-type
1589
+ */
1590
+ "list-style-type": [{
1591
+ list: ["disc", "decimal", "none", isArbitraryVariable, isArbitraryValue]
1592
+ }],
1593
+ /**
1594
+ * Text Alignment
1595
+ * @see https://tailwindcss.com/docs/text-align
1596
+ */
1597
+ "text-alignment": [{
1598
+ text: ["left", "center", "right", "justify", "start", "end"]
1599
+ }],
1600
+ /**
1601
+ * Placeholder Color
1602
+ * @deprecated since Tailwind CSS v3.0.0
1603
+ * @see https://v3.tailwindcss.com/docs/placeholder-color
1604
+ */
1605
+ "placeholder-color": [{
1606
+ placeholder: scaleColor()
1607
+ }],
1608
+ /**
1609
+ * Text Color
1610
+ * @see https://tailwindcss.com/docs/text-color
1611
+ */
1612
+ "text-color": [{
1613
+ text: scaleColor()
1614
+ }],
1615
+ /**
1616
+ * Text Decoration
1617
+ * @see https://tailwindcss.com/docs/text-decoration
1618
+ */
1619
+ "text-decoration": ["underline", "overline", "line-through", "no-underline"],
1620
+ /**
1621
+ * Text Decoration Style
1622
+ * @see https://tailwindcss.com/docs/text-decoration-style
1623
+ */
1624
+ "text-decoration-style": [{
1625
+ decoration: [...scaleLineStyle(), "wavy"]
1626
+ }],
1627
+ /**
1628
+ * Text Decoration Thickness
1629
+ * @see https://tailwindcss.com/docs/text-decoration-thickness
1630
+ */
1631
+ "text-decoration-thickness": [{
1632
+ decoration: [isNumber, "from-font", "auto", isArbitraryVariable, isArbitraryLength]
1633
+ }],
1634
+ /**
1635
+ * Text Decoration Color
1636
+ * @see https://tailwindcss.com/docs/text-decoration-color
1637
+ */
1638
+ "text-decoration-color": [{
1639
+ decoration: scaleColor()
1640
+ }],
1641
+ /**
1642
+ * Text Underline Offset
1643
+ * @see https://tailwindcss.com/docs/text-underline-offset
1644
+ */
1645
+ "underline-offset": [{
1646
+ "underline-offset": [isNumber, "auto", isArbitraryVariable, isArbitraryValue]
1647
+ }],
1648
+ /**
1649
+ * Text Transform
1650
+ * @see https://tailwindcss.com/docs/text-transform
1651
+ */
1652
+ "text-transform": ["uppercase", "lowercase", "capitalize", "normal-case"],
1653
+ /**
1654
+ * Text Overflow
1655
+ * @see https://tailwindcss.com/docs/text-overflow
1656
+ */
1657
+ "text-overflow": ["truncate", "text-ellipsis", "text-clip"],
1658
+ /**
1659
+ * Text Wrap
1660
+ * @see https://tailwindcss.com/docs/text-wrap
1661
+ */
1662
+ "text-wrap": [{
1663
+ text: ["wrap", "nowrap", "balance", "pretty"]
1664
+ }],
1665
+ /**
1666
+ * Text Indent
1667
+ * @see https://tailwindcss.com/docs/text-indent
1668
+ */
1669
+ indent: [{
1670
+ indent: scaleUnambiguousSpacing()
1671
+ }],
1672
+ /**
1673
+ * Tab Size
1674
+ * @see https://tailwindcss.com/docs/tab-size
1675
+ */
1676
+ "tab-size": [{
1677
+ tab: [isInteger, isArbitraryVariable, isArbitraryValue]
1678
+ }],
1679
+ /**
1680
+ * Vertical Alignment
1681
+ * @see https://tailwindcss.com/docs/vertical-align
1682
+ */
1683
+ "vertical-align": [{
1684
+ align: ["baseline", "top", "middle", "bottom", "text-top", "text-bottom", "sub", "super", isArbitraryVariable, isArbitraryValue]
1685
+ }],
1686
+ /**
1687
+ * Whitespace
1688
+ * @see https://tailwindcss.com/docs/whitespace
1689
+ */
1690
+ whitespace: [{
1691
+ whitespace: ["normal", "nowrap", "pre", "pre-line", "pre-wrap", "break-spaces"]
1692
+ }],
1693
+ /**
1694
+ * Word Break
1695
+ * @see https://tailwindcss.com/docs/word-break
1696
+ */
1697
+ break: [{
1698
+ break: ["normal", "words", "all", "keep"]
1699
+ }],
1700
+ /**
1701
+ * Overflow Wrap
1702
+ * @see https://tailwindcss.com/docs/overflow-wrap
1703
+ */
1704
+ wrap: [{
1705
+ wrap: ["break-word", "anywhere", "normal"]
1706
+ }],
1707
+ /**
1708
+ * Hyphens
1709
+ * @see https://tailwindcss.com/docs/hyphens
1710
+ */
1711
+ hyphens: [{
1712
+ hyphens: ["none", "manual", "auto"]
1713
+ }],
1714
+ /**
1715
+ * Content
1716
+ * @see https://tailwindcss.com/docs/content
1717
+ */
1718
+ content: [{
1719
+ content: ["none", isArbitraryVariable, isArbitraryValue]
1720
+ }],
1721
+ // -------------------
1722
+ // --- Backgrounds ---
1723
+ // -------------------
1724
+ /**
1725
+ * Background Attachment
1726
+ * @see https://tailwindcss.com/docs/background-attachment
1727
+ */
1728
+ "bg-attachment": [{
1729
+ bg: ["fixed", "local", "scroll"]
1730
+ }],
1731
+ /**
1732
+ * Background Clip
1733
+ * @see https://tailwindcss.com/docs/background-clip
1734
+ */
1735
+ "bg-clip": [{
1736
+ "bg-clip": ["border", "padding", "content", "text"]
1737
+ }],
1738
+ /**
1739
+ * Background Origin
1740
+ * @see https://tailwindcss.com/docs/background-origin
1741
+ */
1742
+ "bg-origin": [{
1743
+ "bg-origin": ["border", "padding", "content"]
1744
+ }],
1745
+ /**
1746
+ * Background Position
1747
+ * @see https://tailwindcss.com/docs/background-position
1748
+ */
1749
+ "bg-position": [{
1750
+ bg: scaleBgPosition()
1751
+ }],
1752
+ /**
1753
+ * Background Repeat
1754
+ * @see https://tailwindcss.com/docs/background-repeat
1755
+ */
1756
+ "bg-repeat": [{
1757
+ bg: scaleBgRepeat()
1758
+ }],
1759
+ /**
1760
+ * Background Size
1761
+ * @see https://tailwindcss.com/docs/background-size
1762
+ */
1763
+ "bg-size": [{
1764
+ bg: scaleBgSize()
1765
+ }],
1766
+ /**
1767
+ * Background Image
1768
+ * @see https://tailwindcss.com/docs/background-image
1769
+ */
1770
+ "bg-image": [{
1771
+ bg: ["none", {
1772
+ linear: [{
1773
+ to: ["t", "tr", "r", "br", "b", "bl", "l", "tl"]
1774
+ }, isInteger, isArbitraryVariable, isArbitraryValue],
1775
+ radial: ["", isArbitraryVariable, isArbitraryValue],
1776
+ conic: [isInteger, isArbitraryVariable, isArbitraryValue]
1777
+ }, isArbitraryVariableImage, isArbitraryImage]
1778
+ }],
1779
+ /**
1780
+ * Background Color
1781
+ * @see https://tailwindcss.com/docs/background-color
1782
+ */
1783
+ "bg-color": [{
1784
+ bg: scaleColor()
1785
+ }],
1786
+ /**
1787
+ * Gradient Color Stops From Position
1788
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1789
+ */
1790
+ "gradient-from-pos": [{
1791
+ from: scaleGradientStopPosition()
1792
+ }],
1793
+ /**
1794
+ * Gradient Color Stops Via Position
1795
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1796
+ */
1797
+ "gradient-via-pos": [{
1798
+ via: scaleGradientStopPosition()
1799
+ }],
1800
+ /**
1801
+ * Gradient Color Stops To Position
1802
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1803
+ */
1804
+ "gradient-to-pos": [{
1805
+ to: scaleGradientStopPosition()
1806
+ }],
1807
+ /**
1808
+ * Gradient Color Stops From
1809
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1810
+ */
1811
+ "gradient-from": [{
1812
+ from: scaleColor()
1813
+ }],
1814
+ /**
1815
+ * Gradient Color Stops Via
1816
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1817
+ */
1818
+ "gradient-via": [{
1819
+ via: scaleColor()
1820
+ }],
1821
+ /**
1822
+ * Gradient Color Stops To
1823
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1824
+ */
1825
+ "gradient-to": [{
1826
+ to: scaleColor()
1827
+ }],
1828
+ // ---------------
1829
+ // --- Borders ---
1830
+ // ---------------
1831
+ /**
1832
+ * Border Radius
1833
+ * @see https://tailwindcss.com/docs/border-radius
1834
+ */
1835
+ rounded: [{
1836
+ rounded: scaleRadius()
1837
+ }],
1838
+ /**
1839
+ * Border Radius Start
1840
+ * @see https://tailwindcss.com/docs/border-radius
1841
+ */
1842
+ "rounded-s": [{
1843
+ "rounded-s": scaleRadius()
1844
+ }],
1845
+ /**
1846
+ * Border Radius End
1847
+ * @see https://tailwindcss.com/docs/border-radius
1848
+ */
1849
+ "rounded-e": [{
1850
+ "rounded-e": scaleRadius()
1851
+ }],
1852
+ /**
1853
+ * Border Radius Top
1854
+ * @see https://tailwindcss.com/docs/border-radius
1855
+ */
1856
+ "rounded-t": [{
1857
+ "rounded-t": scaleRadius()
1858
+ }],
1859
+ /**
1860
+ * Border Radius Right
1861
+ * @see https://tailwindcss.com/docs/border-radius
1862
+ */
1863
+ "rounded-r": [{
1864
+ "rounded-r": scaleRadius()
1865
+ }],
1866
+ /**
1867
+ * Border Radius Bottom
1868
+ * @see https://tailwindcss.com/docs/border-radius
1869
+ */
1870
+ "rounded-b": [{
1871
+ "rounded-b": scaleRadius()
1872
+ }],
1873
+ /**
1874
+ * Border Radius Left
1875
+ * @see https://tailwindcss.com/docs/border-radius
1876
+ */
1877
+ "rounded-l": [{
1878
+ "rounded-l": scaleRadius()
1879
+ }],
1880
+ /**
1881
+ * Border Radius Start Start
1882
+ * @see https://tailwindcss.com/docs/border-radius
1883
+ */
1884
+ "rounded-ss": [{
1885
+ "rounded-ss": scaleRadius()
1886
+ }],
1887
+ /**
1888
+ * Border Radius Start End
1889
+ * @see https://tailwindcss.com/docs/border-radius
1890
+ */
1891
+ "rounded-se": [{
1892
+ "rounded-se": scaleRadius()
1893
+ }],
1894
+ /**
1895
+ * Border Radius End End
1896
+ * @see https://tailwindcss.com/docs/border-radius
1897
+ */
1898
+ "rounded-ee": [{
1899
+ "rounded-ee": scaleRadius()
1900
+ }],
1901
+ /**
1902
+ * Border Radius End Start
1903
+ * @see https://tailwindcss.com/docs/border-radius
1904
+ */
1905
+ "rounded-es": [{
1906
+ "rounded-es": scaleRadius()
1907
+ }],
1908
+ /**
1909
+ * Border Radius Top Left
1910
+ * @see https://tailwindcss.com/docs/border-radius
1911
+ */
1912
+ "rounded-tl": [{
1913
+ "rounded-tl": scaleRadius()
1914
+ }],
1915
+ /**
1916
+ * Border Radius Top Right
1917
+ * @see https://tailwindcss.com/docs/border-radius
1918
+ */
1919
+ "rounded-tr": [{
1920
+ "rounded-tr": scaleRadius()
1921
+ }],
1922
+ /**
1923
+ * Border Radius Bottom Right
1924
+ * @see https://tailwindcss.com/docs/border-radius
1925
+ */
1926
+ "rounded-br": [{
1927
+ "rounded-br": scaleRadius()
1928
+ }],
1929
+ /**
1930
+ * Border Radius Bottom Left
1931
+ * @see https://tailwindcss.com/docs/border-radius
1932
+ */
1933
+ "rounded-bl": [{
1934
+ "rounded-bl": scaleRadius()
1935
+ }],
1936
+ /**
1937
+ * Border Width
1938
+ * @see https://tailwindcss.com/docs/border-width
1939
+ */
1940
+ "border-w": [{
1941
+ border: scaleBorderWidth()
1942
+ }],
1943
+ /**
1944
+ * Border Width Inline
1945
+ * @see https://tailwindcss.com/docs/border-width
1946
+ */
1947
+ "border-w-x": [{
1948
+ "border-x": scaleBorderWidth()
1949
+ }],
1950
+ /**
1951
+ * Border Width Block
1952
+ * @see https://tailwindcss.com/docs/border-width
1953
+ */
1954
+ "border-w-y": [{
1955
+ "border-y": scaleBorderWidth()
1956
+ }],
1957
+ /**
1958
+ * Border Width Inline Start
1959
+ * @see https://tailwindcss.com/docs/border-width
1960
+ */
1961
+ "border-w-s": [{
1962
+ "border-s": scaleBorderWidth()
1963
+ }],
1964
+ /**
1965
+ * Border Width Inline End
1966
+ * @see https://tailwindcss.com/docs/border-width
1967
+ */
1968
+ "border-w-e": [{
1969
+ "border-e": scaleBorderWidth()
1970
+ }],
1971
+ /**
1972
+ * Border Width Block Start
1973
+ * @see https://tailwindcss.com/docs/border-width
1974
+ */
1975
+ "border-w-bs": [{
1976
+ "border-bs": scaleBorderWidth()
1977
+ }],
1978
+ /**
1979
+ * Border Width Block End
1980
+ * @see https://tailwindcss.com/docs/border-width
1981
+ */
1982
+ "border-w-be": [{
1983
+ "border-be": scaleBorderWidth()
1984
+ }],
1985
+ /**
1986
+ * Border Width Top
1987
+ * @see https://tailwindcss.com/docs/border-width
1988
+ */
1989
+ "border-w-t": [{
1990
+ "border-t": scaleBorderWidth()
1991
+ }],
1992
+ /**
1993
+ * Border Width Right
1994
+ * @see https://tailwindcss.com/docs/border-width
1995
+ */
1996
+ "border-w-r": [{
1997
+ "border-r": scaleBorderWidth()
1998
+ }],
1999
+ /**
2000
+ * Border Width Bottom
2001
+ * @see https://tailwindcss.com/docs/border-width
2002
+ */
2003
+ "border-w-b": [{
2004
+ "border-b": scaleBorderWidth()
2005
+ }],
2006
+ /**
2007
+ * Border Width Left
2008
+ * @see https://tailwindcss.com/docs/border-width
2009
+ */
2010
+ "border-w-l": [{
2011
+ "border-l": scaleBorderWidth()
2012
+ }],
2013
+ /**
2014
+ * Divide Width X
2015
+ * @see https://tailwindcss.com/docs/border-width#between-children
2016
+ */
2017
+ "divide-x": [{
2018
+ "divide-x": scaleBorderWidth()
2019
+ }],
2020
+ /**
2021
+ * Divide Width X Reverse
2022
+ * @see https://tailwindcss.com/docs/border-width#between-children
2023
+ */
2024
+ "divide-x-reverse": ["divide-x-reverse"],
2025
+ /**
2026
+ * Divide Width Y
2027
+ * @see https://tailwindcss.com/docs/border-width#between-children
2028
+ */
2029
+ "divide-y": [{
2030
+ "divide-y": scaleBorderWidth()
2031
+ }],
2032
+ /**
2033
+ * Divide Width Y Reverse
2034
+ * @see https://tailwindcss.com/docs/border-width#between-children
2035
+ */
2036
+ "divide-y-reverse": ["divide-y-reverse"],
2037
+ /**
2038
+ * Border Style
2039
+ * @see https://tailwindcss.com/docs/border-style
2040
+ */
2041
+ "border-style": [{
2042
+ border: [...scaleLineStyle(), "hidden", "none"]
2043
+ }],
2044
+ /**
2045
+ * Divide Style
2046
+ * @see https://tailwindcss.com/docs/border-style#setting-the-divider-style
2047
+ */
2048
+ "divide-style": [{
2049
+ divide: [...scaleLineStyle(), "hidden", "none"]
2050
+ }],
2051
+ /**
2052
+ * Border Color
2053
+ * @see https://tailwindcss.com/docs/border-color
2054
+ */
2055
+ "border-color": [{
2056
+ border: scaleColor()
2057
+ }],
2058
+ /**
2059
+ * Border Color Inline
2060
+ * @see https://tailwindcss.com/docs/border-color
2061
+ */
2062
+ "border-color-x": [{
2063
+ "border-x": scaleColor()
2064
+ }],
2065
+ /**
2066
+ * Border Color Block
2067
+ * @see https://tailwindcss.com/docs/border-color
2068
+ */
2069
+ "border-color-y": [{
2070
+ "border-y": scaleColor()
2071
+ }],
2072
+ /**
2073
+ * Border Color Inline Start
2074
+ * @see https://tailwindcss.com/docs/border-color
2075
+ */
2076
+ "border-color-s": [{
2077
+ "border-s": scaleColor()
2078
+ }],
2079
+ /**
2080
+ * Border Color Inline End
2081
+ * @see https://tailwindcss.com/docs/border-color
2082
+ */
2083
+ "border-color-e": [{
2084
+ "border-e": scaleColor()
2085
+ }],
2086
+ /**
2087
+ * Border Color Block Start
2088
+ * @see https://tailwindcss.com/docs/border-color
2089
+ */
2090
+ "border-color-bs": [{
2091
+ "border-bs": scaleColor()
2092
+ }],
2093
+ /**
2094
+ * Border Color Block End
2095
+ * @see https://tailwindcss.com/docs/border-color
2096
+ */
2097
+ "border-color-be": [{
2098
+ "border-be": scaleColor()
2099
+ }],
2100
+ /**
2101
+ * Border Color Top
2102
+ * @see https://tailwindcss.com/docs/border-color
2103
+ */
2104
+ "border-color-t": [{
2105
+ "border-t": scaleColor()
2106
+ }],
2107
+ /**
2108
+ * Border Color Right
2109
+ * @see https://tailwindcss.com/docs/border-color
2110
+ */
2111
+ "border-color-r": [{
2112
+ "border-r": scaleColor()
2113
+ }],
2114
+ /**
2115
+ * Border Color Bottom
2116
+ * @see https://tailwindcss.com/docs/border-color
2117
+ */
2118
+ "border-color-b": [{
2119
+ "border-b": scaleColor()
2120
+ }],
2121
+ /**
2122
+ * Border Color Left
2123
+ * @see https://tailwindcss.com/docs/border-color
2124
+ */
2125
+ "border-color-l": [{
2126
+ "border-l": scaleColor()
2127
+ }],
2128
+ /**
2129
+ * Divide Color
2130
+ * @see https://tailwindcss.com/docs/divide-color
2131
+ */
2132
+ "divide-color": [{
2133
+ divide: scaleColor()
2134
+ }],
2135
+ /**
2136
+ * Outline Style
2137
+ * @see https://tailwindcss.com/docs/outline-style
2138
+ */
2139
+ "outline-style": [{
2140
+ outline: [...scaleLineStyle(), "none", "hidden"]
2141
+ }],
2142
+ /**
2143
+ * Outline Offset
2144
+ * @see https://tailwindcss.com/docs/outline-offset
2145
+ */
2146
+ "outline-offset": [{
2147
+ "outline-offset": [isNumber, isArbitraryVariable, isArbitraryValue]
2148
+ }],
2149
+ /**
2150
+ * Outline Width
2151
+ * @see https://tailwindcss.com/docs/outline-width
2152
+ */
2153
+ "outline-w": [{
2154
+ outline: ["", isNumber, isArbitraryVariableLength, isArbitraryLength]
2155
+ }],
2156
+ /**
2157
+ * Outline Color
2158
+ * @see https://tailwindcss.com/docs/outline-color
2159
+ */
2160
+ "outline-color": [{
2161
+ outline: scaleColor()
2162
+ }],
2163
+ // ---------------
2164
+ // --- Effects ---
2165
+ // ---------------
2166
+ /**
2167
+ * Box Shadow
2168
+ * @see https://tailwindcss.com/docs/box-shadow
2169
+ */
2170
+ shadow: [{
2171
+ shadow: [
2172
+ // Deprecated since Tailwind CSS v4.0.0
2173
+ "",
2174
+ "none",
2175
+ themeShadow,
2176
+ isArbitraryVariableShadow,
2177
+ isArbitraryShadow
2178
+ ]
2179
+ }],
2180
+ /**
2181
+ * Box Shadow Color
2182
+ * @see https://tailwindcss.com/docs/box-shadow#setting-the-shadow-color
2183
+ */
2184
+ "shadow-color": [{
2185
+ shadow: scaleColor()
2186
+ }],
2187
+ /**
2188
+ * Inset Box Shadow
2189
+ * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-shadow
2190
+ */
2191
+ "inset-shadow": [{
2192
+ "inset-shadow": ["none", themeInsetShadow, isArbitraryVariableShadow, isArbitraryShadow]
2193
+ }],
2194
+ /**
2195
+ * Inset Box Shadow Color
2196
+ * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-shadow-color
2197
+ */
2198
+ "inset-shadow-color": [{
2199
+ "inset-shadow": scaleColor()
2200
+ }],
2201
+ /**
2202
+ * Ring Width
2203
+ * @see https://tailwindcss.com/docs/box-shadow#adding-a-ring
2204
+ */
2205
+ "ring-w": [{
2206
+ ring: scaleBorderWidth()
2207
+ }],
2208
+ /**
2209
+ * Ring Width Inset
2210
+ * @see https://v3.tailwindcss.com/docs/ring-width#inset-rings
2211
+ * @deprecated since Tailwind CSS v4.0.0
2212
+ * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158
2213
+ */
2214
+ "ring-w-inset": ["ring-inset"],
2215
+ /**
2216
+ * Ring Color
2217
+ * @see https://tailwindcss.com/docs/box-shadow#setting-the-ring-color
2218
+ */
2219
+ "ring-color": [{
2220
+ ring: scaleColor()
2221
+ }],
2222
+ /**
2223
+ * Ring Offset Width
2224
+ * @see https://v3.tailwindcss.com/docs/ring-offset-width
2225
+ * @deprecated since Tailwind CSS v4.0.0
2226
+ * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158
2227
+ */
2228
+ "ring-offset-w": [{
2229
+ "ring-offset": [isNumber, isArbitraryLength]
2230
+ }],
2231
+ /**
2232
+ * Ring Offset Color
2233
+ * @see https://v3.tailwindcss.com/docs/ring-offset-color
2234
+ * @deprecated since Tailwind CSS v4.0.0
2235
+ * @see https://github.com/tailwindlabs/tailwindcss/blob/v4.0.0/packages/tailwindcss/src/utilities.ts#L4158
2236
+ */
2237
+ "ring-offset-color": [{
2238
+ "ring-offset": scaleColor()
2239
+ }],
2240
+ /**
2241
+ * Inset Ring Width
2242
+ * @see https://tailwindcss.com/docs/box-shadow#adding-an-inset-ring
2243
+ */
2244
+ "inset-ring-w": [{
2245
+ "inset-ring": scaleBorderWidth()
2246
+ }],
2247
+ /**
2248
+ * Inset Ring Color
2249
+ * @see https://tailwindcss.com/docs/box-shadow#setting-the-inset-ring-color
2250
+ */
2251
+ "inset-ring-color": [{
2252
+ "inset-ring": scaleColor()
2253
+ }],
2254
+ /**
2255
+ * Text Shadow
2256
+ * @see https://tailwindcss.com/docs/text-shadow
2257
+ */
2258
+ "text-shadow": [{
2259
+ "text-shadow": ["none", themeTextShadow, isArbitraryVariableShadow, isArbitraryShadow]
2260
+ }],
2261
+ /**
2262
+ * Text Shadow Color
2263
+ * @see https://tailwindcss.com/docs/text-shadow#setting-the-shadow-color
2264
+ */
2265
+ "text-shadow-color": [{
2266
+ "text-shadow": scaleColor()
2267
+ }],
2268
+ /**
2269
+ * Opacity
2270
+ * @see https://tailwindcss.com/docs/opacity
2271
+ */
2272
+ opacity: [{
2273
+ opacity: [isNumber, isArbitraryVariable, isArbitraryValue]
2274
+ }],
2275
+ /**
2276
+ * Mix Blend Mode
2277
+ * @see https://tailwindcss.com/docs/mix-blend-mode
2278
+ */
2279
+ "mix-blend": [{
2280
+ "mix-blend": [...scaleBlendMode(), "plus-darker", "plus-lighter"]
2281
+ }],
2282
+ /**
2283
+ * Background Blend Mode
2284
+ * @see https://tailwindcss.com/docs/background-blend-mode
2285
+ */
2286
+ "bg-blend": [{
2287
+ "bg-blend": scaleBlendMode()
2288
+ }],
2289
+ /**
2290
+ * Mask Clip
2291
+ * @see https://tailwindcss.com/docs/mask-clip
2292
+ */
2293
+ "mask-clip": [{
2294
+ "mask-clip": ["border", "padding", "content", "fill", "stroke", "view"]
2295
+ }, "mask-no-clip"],
2296
+ /**
2297
+ * Mask Composite
2298
+ * @see https://tailwindcss.com/docs/mask-composite
2299
+ */
2300
+ "mask-composite": [{
2301
+ mask: ["add", "subtract", "intersect", "exclude"]
2302
+ }],
2303
+ /**
2304
+ * Mask Image
2305
+ * @see https://tailwindcss.com/docs/mask-image
2306
+ */
2307
+ "mask-image-linear-pos": [{
2308
+ "mask-linear": [isNumber]
2309
+ }],
2310
+ "mask-image-linear-from-pos": [{
2311
+ "mask-linear-from": scaleMaskImagePosition()
2312
+ }],
2313
+ "mask-image-linear-to-pos": [{
2314
+ "mask-linear-to": scaleMaskImagePosition()
2315
+ }],
2316
+ "mask-image-linear-from-color": [{
2317
+ "mask-linear-from": scaleColor()
2318
+ }],
2319
+ "mask-image-linear-to-color": [{
2320
+ "mask-linear-to": scaleColor()
2321
+ }],
2322
+ "mask-image-t-from-pos": [{
2323
+ "mask-t-from": scaleMaskImagePosition()
2324
+ }],
2325
+ "mask-image-t-to-pos": [{
2326
+ "mask-t-to": scaleMaskImagePosition()
2327
+ }],
2328
+ "mask-image-t-from-color": [{
2329
+ "mask-t-from": scaleColor()
2330
+ }],
2331
+ "mask-image-t-to-color": [{
2332
+ "mask-t-to": scaleColor()
2333
+ }],
2334
+ "mask-image-r-from-pos": [{
2335
+ "mask-r-from": scaleMaskImagePosition()
2336
+ }],
2337
+ "mask-image-r-to-pos": [{
2338
+ "mask-r-to": scaleMaskImagePosition()
2339
+ }],
2340
+ "mask-image-r-from-color": [{
2341
+ "mask-r-from": scaleColor()
2342
+ }],
2343
+ "mask-image-r-to-color": [{
2344
+ "mask-r-to": scaleColor()
2345
+ }],
2346
+ "mask-image-b-from-pos": [{
2347
+ "mask-b-from": scaleMaskImagePosition()
2348
+ }],
2349
+ "mask-image-b-to-pos": [{
2350
+ "mask-b-to": scaleMaskImagePosition()
2351
+ }],
2352
+ "mask-image-b-from-color": [{
2353
+ "mask-b-from": scaleColor()
2354
+ }],
2355
+ "mask-image-b-to-color": [{
2356
+ "mask-b-to": scaleColor()
2357
+ }],
2358
+ "mask-image-l-from-pos": [{
2359
+ "mask-l-from": scaleMaskImagePosition()
2360
+ }],
2361
+ "mask-image-l-to-pos": [{
2362
+ "mask-l-to": scaleMaskImagePosition()
2363
+ }],
2364
+ "mask-image-l-from-color": [{
2365
+ "mask-l-from": scaleColor()
2366
+ }],
2367
+ "mask-image-l-to-color": [{
2368
+ "mask-l-to": scaleColor()
2369
+ }],
2370
+ "mask-image-x-from-pos": [{
2371
+ "mask-x-from": scaleMaskImagePosition()
2372
+ }],
2373
+ "mask-image-x-to-pos": [{
2374
+ "mask-x-to": scaleMaskImagePosition()
2375
+ }],
2376
+ "mask-image-x-from-color": [{
2377
+ "mask-x-from": scaleColor()
2378
+ }],
2379
+ "mask-image-x-to-color": [{
2380
+ "mask-x-to": scaleColor()
2381
+ }],
2382
+ "mask-image-y-from-pos": [{
2383
+ "mask-y-from": scaleMaskImagePosition()
2384
+ }],
2385
+ "mask-image-y-to-pos": [{
2386
+ "mask-y-to": scaleMaskImagePosition()
2387
+ }],
2388
+ "mask-image-y-from-color": [{
2389
+ "mask-y-from": scaleColor()
2390
+ }],
2391
+ "mask-image-y-to-color": [{
2392
+ "mask-y-to": scaleColor()
2393
+ }],
2394
+ "mask-image-radial": [{
2395
+ "mask-radial": [isArbitraryVariable, isArbitraryValue]
2396
+ }],
2397
+ "mask-image-radial-from-pos": [{
2398
+ "mask-radial-from": scaleMaskImagePosition()
2399
+ }],
2400
+ "mask-image-radial-to-pos": [{
2401
+ "mask-radial-to": scaleMaskImagePosition()
2402
+ }],
2403
+ "mask-image-radial-from-color": [{
2404
+ "mask-radial-from": scaleColor()
2405
+ }],
2406
+ "mask-image-radial-to-color": [{
2407
+ "mask-radial-to": scaleColor()
2408
+ }],
2409
+ "mask-image-radial-shape": [{
2410
+ "mask-radial": ["circle", "ellipse"]
2411
+ }],
2412
+ "mask-image-radial-size": [{
2413
+ "mask-radial": [{
2414
+ closest: ["side", "corner"],
2415
+ farthest: ["side", "corner"]
2416
+ }]
2417
+ }],
2418
+ "mask-image-radial-pos": [{
2419
+ "mask-radial-at": scalePosition()
2420
+ }],
2421
+ "mask-image-conic-pos": [{
2422
+ "mask-conic": [isNumber]
2423
+ }],
2424
+ "mask-image-conic-from-pos": [{
2425
+ "mask-conic-from": scaleMaskImagePosition()
2426
+ }],
2427
+ "mask-image-conic-to-pos": [{
2428
+ "mask-conic-to": scaleMaskImagePosition()
2429
+ }],
2430
+ "mask-image-conic-from-color": [{
2431
+ "mask-conic-from": scaleColor()
2432
+ }],
2433
+ "mask-image-conic-to-color": [{
2434
+ "mask-conic-to": scaleColor()
2435
+ }],
2436
+ /**
2437
+ * Mask Mode
2438
+ * @see https://tailwindcss.com/docs/mask-mode
2439
+ */
2440
+ "mask-mode": [{
2441
+ mask: ["alpha", "luminance", "match"]
2442
+ }],
2443
+ /**
2444
+ * Mask Origin
2445
+ * @see https://tailwindcss.com/docs/mask-origin
2446
+ */
2447
+ "mask-origin": [{
2448
+ "mask-origin": ["border", "padding", "content", "fill", "stroke", "view"]
2449
+ }],
2450
+ /**
2451
+ * Mask Position
2452
+ * @see https://tailwindcss.com/docs/mask-position
2453
+ */
2454
+ "mask-position": [{
2455
+ mask: scaleBgPosition()
2456
+ }],
2457
+ /**
2458
+ * Mask Repeat
2459
+ * @see https://tailwindcss.com/docs/mask-repeat
2460
+ */
2461
+ "mask-repeat": [{
2462
+ mask: scaleBgRepeat()
2463
+ }],
2464
+ /**
2465
+ * Mask Size
2466
+ * @see https://tailwindcss.com/docs/mask-size
2467
+ */
2468
+ "mask-size": [{
2469
+ mask: scaleBgSize()
2470
+ }],
2471
+ /**
2472
+ * Mask Type
2473
+ * @see https://tailwindcss.com/docs/mask-type
2474
+ */
2475
+ "mask-type": [{
2476
+ "mask-type": ["alpha", "luminance"]
2477
+ }],
2478
+ /**
2479
+ * Mask Image
2480
+ * @see https://tailwindcss.com/docs/mask-image
2481
+ */
2482
+ "mask-image": [{
2483
+ mask: ["none", isArbitraryVariable, isArbitraryValue]
2484
+ }],
2485
+ // ---------------
2486
+ // --- Filters ---
2487
+ // ---------------
2488
+ /**
2489
+ * Filter
2490
+ * @see https://tailwindcss.com/docs/filter
2491
+ */
2492
+ filter: [{
2493
+ filter: [
2494
+ // Deprecated since Tailwind CSS v3.0.0
2495
+ "",
2496
+ "none",
2497
+ isArbitraryVariable,
2498
+ isArbitraryValue
2499
+ ]
2500
+ }],
2501
+ /**
2502
+ * Blur
2503
+ * @see https://tailwindcss.com/docs/blur
2504
+ */
2505
+ blur: [{
2506
+ blur: scaleBlur()
2507
+ }],
2508
+ /**
2509
+ * Brightness
2510
+ * @see https://tailwindcss.com/docs/brightness
2511
+ */
2512
+ brightness: [{
2513
+ brightness: [isNumber, isArbitraryVariable, isArbitraryValue]
2514
+ }],
2515
+ /**
2516
+ * Contrast
2517
+ * @see https://tailwindcss.com/docs/contrast
2518
+ */
2519
+ contrast: [{
2520
+ contrast: [isNumber, isArbitraryVariable, isArbitraryValue]
2521
+ }],
2522
+ /**
2523
+ * Drop Shadow
2524
+ * @see https://tailwindcss.com/docs/drop-shadow
2525
+ */
2526
+ "drop-shadow": [{
2527
+ "drop-shadow": [
2528
+ // Deprecated since Tailwind CSS v4.0.0
2529
+ "",
2530
+ "none",
2531
+ themeDropShadow,
2532
+ isArbitraryVariableShadow,
2533
+ isArbitraryShadow
2534
+ ]
2535
+ }],
2536
+ /**
2537
+ * Drop Shadow Color
2538
+ * @see https://tailwindcss.com/docs/filter-drop-shadow#setting-the-shadow-color
2539
+ */
2540
+ "drop-shadow-color": [{
2541
+ "drop-shadow": scaleColor()
2542
+ }],
2543
+ /**
2544
+ * Grayscale
2545
+ * @see https://tailwindcss.com/docs/grayscale
2546
+ */
2547
+ grayscale: [{
2548
+ grayscale: ["", isNumber, isArbitraryVariable, isArbitraryValue]
2549
+ }],
2550
+ /**
2551
+ * Hue Rotate
2552
+ * @see https://tailwindcss.com/docs/hue-rotate
2553
+ */
2554
+ "hue-rotate": [{
2555
+ "hue-rotate": [isNumber, isArbitraryVariable, isArbitraryValue]
2556
+ }],
2557
+ /**
2558
+ * Invert
2559
+ * @see https://tailwindcss.com/docs/invert
2560
+ */
2561
+ invert: [{
2562
+ invert: ["", isNumber, isArbitraryVariable, isArbitraryValue]
2563
+ }],
2564
+ /**
2565
+ * Saturate
2566
+ * @see https://tailwindcss.com/docs/saturate
2567
+ */
2568
+ saturate: [{
2569
+ saturate: [isNumber, isArbitraryVariable, isArbitraryValue]
2570
+ }],
2571
+ /**
2572
+ * Sepia
2573
+ * @see https://tailwindcss.com/docs/sepia
2574
+ */
2575
+ sepia: [{
2576
+ sepia: ["", isNumber, isArbitraryVariable, isArbitraryValue]
2577
+ }],
2578
+ /**
2579
+ * Backdrop Filter
2580
+ * @see https://tailwindcss.com/docs/backdrop-filter
2581
+ */
2582
+ "backdrop-filter": [{
2583
+ "backdrop-filter": [
2584
+ // Deprecated since Tailwind CSS v3.0.0
2585
+ "",
2586
+ "none",
2587
+ isArbitraryVariable,
2588
+ isArbitraryValue
2589
+ ]
2590
+ }],
2591
+ /**
2592
+ * Backdrop Blur
2593
+ * @see https://tailwindcss.com/docs/backdrop-blur
2594
+ */
2595
+ "backdrop-blur": [{
2596
+ "backdrop-blur": scaleBlur()
2597
+ }],
2598
+ /**
2599
+ * Backdrop Brightness
2600
+ * @see https://tailwindcss.com/docs/backdrop-brightness
2601
+ */
2602
+ "backdrop-brightness": [{
2603
+ "backdrop-brightness": [isNumber, isArbitraryVariable, isArbitraryValue]
2604
+ }],
2605
+ /**
2606
+ * Backdrop Contrast
2607
+ * @see https://tailwindcss.com/docs/backdrop-contrast
2608
+ */
2609
+ "backdrop-contrast": [{
2610
+ "backdrop-contrast": [isNumber, isArbitraryVariable, isArbitraryValue]
2611
+ }],
2612
+ /**
2613
+ * Backdrop Grayscale
2614
+ * @see https://tailwindcss.com/docs/backdrop-grayscale
2615
+ */
2616
+ "backdrop-grayscale": [{
2617
+ "backdrop-grayscale": ["", isNumber, isArbitraryVariable, isArbitraryValue]
2618
+ }],
2619
+ /**
2620
+ * Backdrop Hue Rotate
2621
+ * @see https://tailwindcss.com/docs/backdrop-hue-rotate
2622
+ */
2623
+ "backdrop-hue-rotate": [{
2624
+ "backdrop-hue-rotate": [isNumber, isArbitraryVariable, isArbitraryValue]
2625
+ }],
2626
+ /**
2627
+ * Backdrop Invert
2628
+ * @see https://tailwindcss.com/docs/backdrop-invert
2629
+ */
2630
+ "backdrop-invert": [{
2631
+ "backdrop-invert": ["", isNumber, isArbitraryVariable, isArbitraryValue]
2632
+ }],
2633
+ /**
2634
+ * Backdrop Opacity
2635
+ * @see https://tailwindcss.com/docs/backdrop-opacity
2636
+ */
2637
+ "backdrop-opacity": [{
2638
+ "backdrop-opacity": [isNumber, isArbitraryVariable, isArbitraryValue]
2639
+ }],
2640
+ /**
2641
+ * Backdrop Saturate
2642
+ * @see https://tailwindcss.com/docs/backdrop-saturate
2643
+ */
2644
+ "backdrop-saturate": [{
2645
+ "backdrop-saturate": [isNumber, isArbitraryVariable, isArbitraryValue]
2646
+ }],
2647
+ /**
2648
+ * Backdrop Sepia
2649
+ * @see https://tailwindcss.com/docs/backdrop-sepia
2650
+ */
2651
+ "backdrop-sepia": [{
2652
+ "backdrop-sepia": ["", isNumber, isArbitraryVariable, isArbitraryValue]
2653
+ }],
2654
+ // --------------
2655
+ // --- Tables ---
2656
+ // --------------
2657
+ /**
2658
+ * Border Collapse
2659
+ * @see https://tailwindcss.com/docs/border-collapse
2660
+ */
2661
+ "border-collapse": [{
2662
+ border: ["collapse", "separate"]
2663
+ }],
2664
+ /**
2665
+ * Border Spacing
2666
+ * @see https://tailwindcss.com/docs/border-spacing
2667
+ */
2668
+ "border-spacing": [{
2669
+ "border-spacing": scaleUnambiguousSpacing()
2670
+ }],
2671
+ /**
2672
+ * Border Spacing X
2673
+ * @see https://tailwindcss.com/docs/border-spacing
2674
+ */
2675
+ "border-spacing-x": [{
2676
+ "border-spacing-x": scaleUnambiguousSpacing()
2677
+ }],
2678
+ /**
2679
+ * Border Spacing Y
2680
+ * @see https://tailwindcss.com/docs/border-spacing
2681
+ */
2682
+ "border-spacing-y": [{
2683
+ "border-spacing-y": scaleUnambiguousSpacing()
2684
+ }],
2685
+ /**
2686
+ * Table Layout
2687
+ * @see https://tailwindcss.com/docs/table-layout
2688
+ */
2689
+ "table-layout": [{
2690
+ table: ["auto", "fixed"]
2691
+ }],
2692
+ /**
2693
+ * Caption Side
2694
+ * @see https://tailwindcss.com/docs/caption-side
2695
+ */
2696
+ caption: [{
2697
+ caption: ["top", "bottom"]
2698
+ }],
2699
+ // ---------------------------------
2700
+ // --- Transitions and Animation ---
2701
+ // ---------------------------------
2702
+ /**
2703
+ * Transition Property
2704
+ * @see https://tailwindcss.com/docs/transition-property
2705
+ */
2706
+ transition: [{
2707
+ transition: ["", "all", "colors", "opacity", "shadow", "transform", "none", isArbitraryVariable, isArbitraryValue]
2708
+ }],
2709
+ /**
2710
+ * Transition Behavior
2711
+ * @see https://tailwindcss.com/docs/transition-behavior
2712
+ */
2713
+ "transition-behavior": [{
2714
+ transition: ["normal", "discrete"]
2715
+ }],
2716
+ /**
2717
+ * Transition Duration
2718
+ * @see https://tailwindcss.com/docs/transition-duration
2719
+ */
2720
+ duration: [{
2721
+ duration: [isNumber, "initial", isArbitraryVariable, isArbitraryValue]
2722
+ }],
2723
+ /**
2724
+ * Transition Timing Function
2725
+ * @see https://tailwindcss.com/docs/transition-timing-function
2726
+ */
2727
+ ease: [{
2728
+ ease: ["linear", "initial", themeEase, isArbitraryVariable, isArbitraryValue]
2729
+ }],
2730
+ /**
2731
+ * Transition Delay
2732
+ * @see https://tailwindcss.com/docs/transition-delay
2733
+ */
2734
+ delay: [{
2735
+ delay: [isNumber, isArbitraryVariable, isArbitraryValue]
2736
+ }],
2737
+ /**
2738
+ * Animation
2739
+ * @see https://tailwindcss.com/docs/animation
2740
+ */
2741
+ animate: [{
2742
+ animate: ["none", themeAnimate, isArbitraryVariable, isArbitraryValue]
2743
+ }],
2744
+ // ------------------
2745
+ // --- Transforms ---
2746
+ // ------------------
2747
+ /**
2748
+ * Backface Visibility
2749
+ * @see https://tailwindcss.com/docs/backface-visibility
2750
+ */
2751
+ backface: [{
2752
+ backface: ["hidden", "visible"]
2753
+ }],
2754
+ /**
2755
+ * Perspective
2756
+ * @see https://tailwindcss.com/docs/perspective
2757
+ */
2758
+ perspective: [{
2759
+ perspective: [themePerspective, isArbitraryVariable, isArbitraryValue]
2760
+ }],
2761
+ /**
2762
+ * Perspective Origin
2763
+ * @see https://tailwindcss.com/docs/perspective-origin
2764
+ */
2765
+ "perspective-origin": [{
2766
+ "perspective-origin": scalePositionWithArbitrary()
2767
+ }],
2768
+ /**
2769
+ * Rotate
2770
+ * @see https://tailwindcss.com/docs/rotate
2771
+ */
2772
+ rotate: [{
2773
+ rotate: scaleRotate()
2774
+ }],
2775
+ /**
2776
+ * Rotate X
2777
+ * @see https://tailwindcss.com/docs/rotate
2778
+ */
2779
+ "rotate-x": [{
2780
+ "rotate-x": scaleRotate()
2781
+ }],
2782
+ /**
2783
+ * Rotate Y
2784
+ * @see https://tailwindcss.com/docs/rotate
2785
+ */
2786
+ "rotate-y": [{
2787
+ "rotate-y": scaleRotate()
2788
+ }],
2789
+ /**
2790
+ * Rotate Z
2791
+ * @see https://tailwindcss.com/docs/rotate
2792
+ */
2793
+ "rotate-z": [{
2794
+ "rotate-z": scaleRotate()
2795
+ }],
2796
+ /**
2797
+ * Scale
2798
+ * @see https://tailwindcss.com/docs/scale
2799
+ */
2800
+ scale: [{
2801
+ scale: scaleScale()
2802
+ }],
2803
+ /**
2804
+ * Scale X
2805
+ * @see https://tailwindcss.com/docs/scale
2806
+ */
2807
+ "scale-x": [{
2808
+ "scale-x": scaleScale()
2809
+ }],
2810
+ /**
2811
+ * Scale Y
2812
+ * @see https://tailwindcss.com/docs/scale
2813
+ */
2814
+ "scale-y": [{
2815
+ "scale-y": scaleScale()
2816
+ }],
2817
+ /**
2818
+ * Scale Z
2819
+ * @see https://tailwindcss.com/docs/scale
2820
+ */
2821
+ "scale-z": [{
2822
+ "scale-z": scaleScale()
2823
+ }],
2824
+ /**
2825
+ * Scale 3D
2826
+ * @see https://tailwindcss.com/docs/scale
2827
+ */
2828
+ "scale-3d": ["scale-3d"],
2829
+ /**
2830
+ * Skew
2831
+ * @see https://tailwindcss.com/docs/skew
2832
+ */
2833
+ skew: [{
2834
+ skew: scaleSkew()
2835
+ }],
2836
+ /**
2837
+ * Skew X
2838
+ * @see https://tailwindcss.com/docs/skew
2839
+ */
2840
+ "skew-x": [{
2841
+ "skew-x": scaleSkew()
2842
+ }],
2843
+ /**
2844
+ * Skew Y
2845
+ * @see https://tailwindcss.com/docs/skew
2846
+ */
2847
+ "skew-y": [{
2848
+ "skew-y": scaleSkew()
2849
+ }],
2850
+ /**
2851
+ * Transform
2852
+ * @see https://tailwindcss.com/docs/transform
2853
+ */
2854
+ transform: [{
2855
+ transform: [isArbitraryVariable, isArbitraryValue, "", "none", "gpu", "cpu"]
2856
+ }],
2857
+ /**
2858
+ * Transform Origin
2859
+ * @see https://tailwindcss.com/docs/transform-origin
2860
+ */
2861
+ "transform-origin": [{
2862
+ origin: scalePositionWithArbitrary()
2863
+ }],
2864
+ /**
2865
+ * Transform Style
2866
+ * @see https://tailwindcss.com/docs/transform-style
2867
+ */
2868
+ "transform-style": [{
2869
+ transform: ["3d", "flat"]
2870
+ }],
2871
+ /**
2872
+ * Translate
2873
+ * @see https://tailwindcss.com/docs/translate
2874
+ */
2875
+ translate: [{
2876
+ translate: scaleTranslate()
2877
+ }],
2878
+ /**
2879
+ * Translate X
2880
+ * @see https://tailwindcss.com/docs/translate
2881
+ */
2882
+ "translate-x": [{
2883
+ "translate-x": scaleTranslate()
2884
+ }],
2885
+ /**
2886
+ * Translate Y
2887
+ * @see https://tailwindcss.com/docs/translate
2888
+ */
2889
+ "translate-y": [{
2890
+ "translate-y": scaleTranslate()
2891
+ }],
2892
+ /**
2893
+ * Translate Z
2894
+ * @see https://tailwindcss.com/docs/translate
2895
+ */
2896
+ "translate-z": [{
2897
+ "translate-z": scaleTranslate()
2898
+ }],
2899
+ /**
2900
+ * Translate None
2901
+ * @see https://tailwindcss.com/docs/translate
2902
+ */
2903
+ "translate-none": ["translate-none"],
2904
+ /**
2905
+ * Zoom
2906
+ * @see https://tailwindcss.com/docs/zoom
2907
+ */
2908
+ zoom: [{
2909
+ zoom: [isInteger, isArbitraryVariable, isArbitraryValue]
2910
+ }],
2911
+ // ---------------------
2912
+ // --- Interactivity ---
2913
+ // ---------------------
2914
+ /**
2915
+ * Accent Color
2916
+ * @see https://tailwindcss.com/docs/accent-color
2917
+ */
2918
+ accent: [{
2919
+ accent: scaleColor()
2920
+ }],
2921
+ /**
2922
+ * Appearance
2923
+ * @see https://tailwindcss.com/docs/appearance
2924
+ */
2925
+ appearance: [{
2926
+ appearance: ["none", "auto"]
2927
+ }],
2928
+ /**
2929
+ * Caret Color
2930
+ * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities
2931
+ */
2932
+ "caret-color": [{
2933
+ caret: scaleColor()
2934
+ }],
2935
+ /**
2936
+ * Color Scheme
2937
+ * @see https://tailwindcss.com/docs/color-scheme
2938
+ */
2939
+ "color-scheme": [{
2940
+ scheme: ["normal", "dark", "light", "light-dark", "only-dark", "only-light"]
2941
+ }],
2942
+ /**
2943
+ * Cursor
2944
+ * @see https://tailwindcss.com/docs/cursor
2945
+ */
2946
+ cursor: [{
2947
+ cursor: ["auto", "default", "pointer", "wait", "text", "move", "help", "not-allowed", "none", "context-menu", "progress", "cell", "crosshair", "vertical-text", "alias", "copy", "no-drop", "grab", "grabbing", "all-scroll", "col-resize", "row-resize", "n-resize", "e-resize", "s-resize", "w-resize", "ne-resize", "nw-resize", "se-resize", "sw-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "zoom-in", "zoom-out", isArbitraryVariable, isArbitraryValue]
2948
+ }],
2949
+ /**
2950
+ * Field Sizing
2951
+ * @see https://tailwindcss.com/docs/field-sizing
2952
+ */
2953
+ "field-sizing": [{
2954
+ "field-sizing": ["fixed", "content"]
2955
+ }],
2956
+ /**
2957
+ * Pointer Events
2958
+ * @see https://tailwindcss.com/docs/pointer-events
2959
+ */
2960
+ "pointer-events": [{
2961
+ "pointer-events": ["auto", "none"]
2962
+ }],
2963
+ /**
2964
+ * Resize
2965
+ * @see https://tailwindcss.com/docs/resize
2966
+ */
2967
+ resize: [{
2968
+ resize: ["none", "", "y", "x"]
2969
+ }],
2970
+ /**
2971
+ * Scroll Behavior
2972
+ * @see https://tailwindcss.com/docs/scroll-behavior
2973
+ */
2974
+ "scroll-behavior": [{
2975
+ scroll: ["auto", "smooth"]
2976
+ }],
2977
+ /**
2978
+ * Scrollbar Thumb Color
2979
+ * @see https://tailwindcss.com/docs/scrollbar-color
2980
+ */
2981
+ "scrollbar-thumb-color": [{
2982
+ "scrollbar-thumb": scaleColor()
2983
+ }],
2984
+ /**
2985
+ * Scrollbar Track Color
2986
+ * @see https://tailwindcss.com/docs/scrollbar-color
2987
+ */
2988
+ "scrollbar-track-color": [{
2989
+ "scrollbar-track": scaleColor()
2990
+ }],
2991
+ /**
2992
+ * Scrollbar Gutter
2993
+ * @see https://tailwindcss.com/docs/scrollbar-gutter
2994
+ */
2995
+ "scrollbar-gutter": [{
2996
+ "scrollbar-gutter": ["auto", "stable", "both"]
2997
+ }],
2998
+ /**
2999
+ * Scrollbar Width
3000
+ * @see https://tailwindcss.com/docs/scrollbar-width
3001
+ */
3002
+ "scrollbar-w": [{
3003
+ scrollbar: ["auto", "thin", "none"]
3004
+ }],
3005
+ /**
3006
+ * Scroll Margin
3007
+ * @see https://tailwindcss.com/docs/scroll-margin
3008
+ */
3009
+ "scroll-m": [{
3010
+ "scroll-m": scaleUnambiguousSpacing()
3011
+ }],
3012
+ /**
3013
+ * Scroll Margin Inline
3014
+ * @see https://tailwindcss.com/docs/scroll-margin
3015
+ */
3016
+ "scroll-mx": [{
3017
+ "scroll-mx": scaleUnambiguousSpacing()
3018
+ }],
3019
+ /**
3020
+ * Scroll Margin Block
3021
+ * @see https://tailwindcss.com/docs/scroll-margin
3022
+ */
3023
+ "scroll-my": [{
3024
+ "scroll-my": scaleUnambiguousSpacing()
3025
+ }],
3026
+ /**
3027
+ * Scroll Margin Inline Start
3028
+ * @see https://tailwindcss.com/docs/scroll-margin
3029
+ */
3030
+ "scroll-ms": [{
3031
+ "scroll-ms": scaleUnambiguousSpacing()
3032
+ }],
3033
+ /**
3034
+ * Scroll Margin Inline End
3035
+ * @see https://tailwindcss.com/docs/scroll-margin
3036
+ */
3037
+ "scroll-me": [{
3038
+ "scroll-me": scaleUnambiguousSpacing()
3039
+ }],
3040
+ /**
3041
+ * Scroll Margin Block Start
3042
+ * @see https://tailwindcss.com/docs/scroll-margin
3043
+ */
3044
+ "scroll-mbs": [{
3045
+ "scroll-mbs": scaleUnambiguousSpacing()
3046
+ }],
3047
+ /**
3048
+ * Scroll Margin Block End
3049
+ * @see https://tailwindcss.com/docs/scroll-margin
3050
+ */
3051
+ "scroll-mbe": [{
3052
+ "scroll-mbe": scaleUnambiguousSpacing()
3053
+ }],
3054
+ /**
3055
+ * Scroll Margin Top
3056
+ * @see https://tailwindcss.com/docs/scroll-margin
3057
+ */
3058
+ "scroll-mt": [{
3059
+ "scroll-mt": scaleUnambiguousSpacing()
3060
+ }],
3061
+ /**
3062
+ * Scroll Margin Right
3063
+ * @see https://tailwindcss.com/docs/scroll-margin
3064
+ */
3065
+ "scroll-mr": [{
3066
+ "scroll-mr": scaleUnambiguousSpacing()
3067
+ }],
3068
+ /**
3069
+ * Scroll Margin Bottom
3070
+ * @see https://tailwindcss.com/docs/scroll-margin
3071
+ */
3072
+ "scroll-mb": [{
3073
+ "scroll-mb": scaleUnambiguousSpacing()
3074
+ }],
3075
+ /**
3076
+ * Scroll Margin Left
3077
+ * @see https://tailwindcss.com/docs/scroll-margin
3078
+ */
3079
+ "scroll-ml": [{
3080
+ "scroll-ml": scaleUnambiguousSpacing()
3081
+ }],
3082
+ /**
3083
+ * Scroll Padding
3084
+ * @see https://tailwindcss.com/docs/scroll-padding
3085
+ */
3086
+ "scroll-p": [{
3087
+ "scroll-p": scaleUnambiguousSpacing()
3088
+ }],
3089
+ /**
3090
+ * Scroll Padding Inline
3091
+ * @see https://tailwindcss.com/docs/scroll-padding
3092
+ */
3093
+ "scroll-px": [{
3094
+ "scroll-px": scaleUnambiguousSpacing()
3095
+ }],
3096
+ /**
3097
+ * Scroll Padding Block
3098
+ * @see https://tailwindcss.com/docs/scroll-padding
3099
+ */
3100
+ "scroll-py": [{
3101
+ "scroll-py": scaleUnambiguousSpacing()
3102
+ }],
3103
+ /**
3104
+ * Scroll Padding Inline Start
3105
+ * @see https://tailwindcss.com/docs/scroll-padding
3106
+ */
3107
+ "scroll-ps": [{
3108
+ "scroll-ps": scaleUnambiguousSpacing()
3109
+ }],
3110
+ /**
3111
+ * Scroll Padding Inline End
3112
+ * @see https://tailwindcss.com/docs/scroll-padding
3113
+ */
3114
+ "scroll-pe": [{
3115
+ "scroll-pe": scaleUnambiguousSpacing()
3116
+ }],
3117
+ /**
3118
+ * Scroll Padding Block Start
3119
+ * @see https://tailwindcss.com/docs/scroll-padding
3120
+ */
3121
+ "scroll-pbs": [{
3122
+ "scroll-pbs": scaleUnambiguousSpacing()
3123
+ }],
3124
+ /**
3125
+ * Scroll Padding Block End
3126
+ * @see https://tailwindcss.com/docs/scroll-padding
3127
+ */
3128
+ "scroll-pbe": [{
3129
+ "scroll-pbe": scaleUnambiguousSpacing()
3130
+ }],
3131
+ /**
3132
+ * Scroll Padding Top
3133
+ * @see https://tailwindcss.com/docs/scroll-padding
3134
+ */
3135
+ "scroll-pt": [{
3136
+ "scroll-pt": scaleUnambiguousSpacing()
3137
+ }],
3138
+ /**
3139
+ * Scroll Padding Right
3140
+ * @see https://tailwindcss.com/docs/scroll-padding
3141
+ */
3142
+ "scroll-pr": [{
3143
+ "scroll-pr": scaleUnambiguousSpacing()
3144
+ }],
3145
+ /**
3146
+ * Scroll Padding Bottom
3147
+ * @see https://tailwindcss.com/docs/scroll-padding
3148
+ */
3149
+ "scroll-pb": [{
3150
+ "scroll-pb": scaleUnambiguousSpacing()
3151
+ }],
3152
+ /**
3153
+ * Scroll Padding Left
3154
+ * @see https://tailwindcss.com/docs/scroll-padding
3155
+ */
3156
+ "scroll-pl": [{
3157
+ "scroll-pl": scaleUnambiguousSpacing()
3158
+ }],
3159
+ /**
3160
+ * Scroll Snap Align
3161
+ * @see https://tailwindcss.com/docs/scroll-snap-align
3162
+ */
3163
+ "snap-align": [{
3164
+ snap: ["start", "end", "center", "align-none"]
3165
+ }],
3166
+ /**
3167
+ * Scroll Snap Stop
3168
+ * @see https://tailwindcss.com/docs/scroll-snap-stop
3169
+ */
3170
+ "snap-stop": [{
3171
+ snap: ["normal", "always"]
3172
+ }],
3173
+ /**
3174
+ * Scroll Snap Type
3175
+ * @see https://tailwindcss.com/docs/scroll-snap-type
3176
+ */
3177
+ "snap-type": [{
3178
+ snap: ["none", "x", "y", "both"]
3179
+ }],
3180
+ /**
3181
+ * Scroll Snap Type Strictness
3182
+ * @see https://tailwindcss.com/docs/scroll-snap-type
3183
+ */
3184
+ "snap-strictness": [{
3185
+ snap: ["mandatory", "proximity"]
3186
+ }],
3187
+ /**
3188
+ * Touch Action
3189
+ * @see https://tailwindcss.com/docs/touch-action
3190
+ */
3191
+ touch: [{
3192
+ touch: ["auto", "none", "manipulation"]
3193
+ }],
3194
+ /**
3195
+ * Touch Action X
3196
+ * @see https://tailwindcss.com/docs/touch-action
3197
+ */
3198
+ "touch-x": [{
3199
+ "touch-pan": ["x", "left", "right"]
3200
+ }],
3201
+ /**
3202
+ * Touch Action Y
3203
+ * @see https://tailwindcss.com/docs/touch-action
3204
+ */
3205
+ "touch-y": [{
3206
+ "touch-pan": ["y", "up", "down"]
3207
+ }],
3208
+ /**
3209
+ * Touch Action Pinch Zoom
3210
+ * @see https://tailwindcss.com/docs/touch-action
3211
+ */
3212
+ "touch-pz": ["touch-pinch-zoom"],
3213
+ /**
3214
+ * User Select
3215
+ * @see https://tailwindcss.com/docs/user-select
3216
+ */
3217
+ select: [{
3218
+ select: ["none", "text", "all", "auto"]
3219
+ }],
3220
+ /**
3221
+ * Will Change
3222
+ * @see https://tailwindcss.com/docs/will-change
3223
+ */
3224
+ "will-change": [{
3225
+ "will-change": ["auto", "scroll", "contents", "transform", isArbitraryVariable, isArbitraryValue]
3226
+ }],
3227
+ // -----------
3228
+ // --- SVG ---
3229
+ // -----------
3230
+ /**
3231
+ * Fill
3232
+ * @see https://tailwindcss.com/docs/fill
3233
+ */
3234
+ fill: [{
3235
+ fill: ["none", ...scaleColor()]
3236
+ }],
3237
+ /**
3238
+ * Stroke Width
3239
+ * @see https://tailwindcss.com/docs/stroke-width
3240
+ */
3241
+ "stroke-w": [{
3242
+ stroke: [isNumber, isArbitraryVariableLength, isArbitraryLength, isArbitraryNumber]
3243
+ }],
3244
+ /**
3245
+ * Stroke
3246
+ * @see https://tailwindcss.com/docs/stroke
3247
+ */
3248
+ stroke: [{
3249
+ stroke: ["none", ...scaleColor()]
3250
+ }],
3251
+ // ---------------------
3252
+ // --- Accessibility ---
3253
+ // ---------------------
3254
+ /**
3255
+ * Forced Color Adjust
3256
+ * @see https://tailwindcss.com/docs/forced-color-adjust
3257
+ */
3258
+ "forced-color-adjust": [{
3259
+ "forced-color-adjust": ["auto", "none"]
3260
+ }]
3261
+ },
3262
+ conflictingClassGroups: {
3263
+ "container-named": ["container-type"],
3264
+ overflow: ["overflow-x", "overflow-y"],
3265
+ overscroll: ["overscroll-x", "overscroll-y"],
3266
+ inset: ["inset-x", "inset-y", "inset-bs", "inset-be", "start", "end", "top", "right", "bottom", "left"],
3267
+ "inset-x": ["right", "left"],
3268
+ "inset-y": ["top", "bottom"],
3269
+ flex: ["basis", "grow", "shrink"],
3270
+ gap: ["gap-x", "gap-y"],
3271
+ p: ["px", "py", "ps", "pe", "pbs", "pbe", "pt", "pr", "pb", "pl"],
3272
+ px: ["pr", "pl"],
3273
+ py: ["pt", "pb"],
3274
+ m: ["mx", "my", "ms", "me", "mbs", "mbe", "mt", "mr", "mb", "ml"],
3275
+ mx: ["mr", "ml"],
3276
+ my: ["mt", "mb"],
3277
+ size: ["w", "h"],
3278
+ "font-size": ["leading"],
3279
+ "fvn-normal": ["fvn-ordinal", "fvn-slashed-zero", "fvn-figure", "fvn-spacing", "fvn-fraction"],
3280
+ "fvn-ordinal": ["fvn-normal"],
3281
+ "fvn-slashed-zero": ["fvn-normal"],
3282
+ "fvn-figure": ["fvn-normal"],
3283
+ "fvn-spacing": ["fvn-normal"],
3284
+ "fvn-fraction": ["fvn-normal"],
3285
+ "line-clamp": ["display", "overflow"],
3286
+ rounded: ["rounded-s", "rounded-e", "rounded-t", "rounded-r", "rounded-b", "rounded-l", "rounded-ss", "rounded-se", "rounded-ee", "rounded-es", "rounded-tl", "rounded-tr", "rounded-br", "rounded-bl"],
3287
+ "rounded-s": ["rounded-ss", "rounded-es"],
3288
+ "rounded-e": ["rounded-se", "rounded-ee"],
3289
+ "rounded-t": ["rounded-tl", "rounded-tr"],
3290
+ "rounded-r": ["rounded-tr", "rounded-br"],
3291
+ "rounded-b": ["rounded-br", "rounded-bl"],
3292
+ "rounded-l": ["rounded-tl", "rounded-bl"],
3293
+ "border-spacing": ["border-spacing-x", "border-spacing-y"],
3294
+ "border-w": ["border-w-x", "border-w-y", "border-w-s", "border-w-e", "border-w-bs", "border-w-be", "border-w-t", "border-w-r", "border-w-b", "border-w-l"],
3295
+ "border-w-x": ["border-w-r", "border-w-l"],
3296
+ "border-w-y": ["border-w-t", "border-w-b"],
3297
+ "border-color": ["border-color-x", "border-color-y", "border-color-s", "border-color-e", "border-color-bs", "border-color-be", "border-color-t", "border-color-r", "border-color-b", "border-color-l"],
3298
+ "border-color-x": ["border-color-r", "border-color-l"],
3299
+ "border-color-y": ["border-color-t", "border-color-b"],
3300
+ translate: ["translate-x", "translate-y", "translate-none"],
3301
+ "translate-none": ["translate", "translate-x", "translate-y", "translate-z"],
3302
+ "scroll-m": ["scroll-mx", "scroll-my", "scroll-ms", "scroll-me", "scroll-mbs", "scroll-mbe", "scroll-mt", "scroll-mr", "scroll-mb", "scroll-ml"],
3303
+ "scroll-mx": ["scroll-mr", "scroll-ml"],
3304
+ "scroll-my": ["scroll-mt", "scroll-mb"],
3305
+ "scroll-p": ["scroll-px", "scroll-py", "scroll-ps", "scroll-pe", "scroll-pbs", "scroll-pbe", "scroll-pt", "scroll-pr", "scroll-pb", "scroll-pl"],
3306
+ "scroll-px": ["scroll-pr", "scroll-pl"],
3307
+ "scroll-py": ["scroll-pt", "scroll-pb"],
3308
+ touch: ["touch-x", "touch-y", "touch-pz"],
3309
+ "touch-x": ["touch"],
3310
+ "touch-y": ["touch"],
3311
+ "touch-pz": ["touch"]
3312
+ },
3313
+ conflictingClassGroupModifiers: {
3314
+ "font-size": ["leading"]
3315
+ },
3316
+ postfixLookupClassGroups: ["container-type"],
3317
+ orderSensitiveModifiers: ["*", "**", "after", "backdrop", "before", "details-content", "file", "first-letter", "first-line", "marker", "placeholder", "selection"]
3318
+ };
3319
+ };
3320
+ var twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
3321
+
3322
+ // src/components/utils/index.ts
3323
+ function cn(...inputs) {
3324
+ return twMerge(clsx(inputs));
3325
+ }
3326
+
3327
+ // src/helpers/viewer360PropsHelpers.ts
3328
+ function mergeViewer360Labels(labels) {
3329
+ return {
3330
+ loading: labels?.loading ?? defaultViewer360Labels.loading,
3331
+ dragHint: labels?.dragHint ?? defaultViewer360Labels.dragHint,
3332
+ frameIndicator: labels?.frameIndicator ?? defaultViewer360Labels.frameIndicator,
3333
+ zoom: labels?.zoom ?? defaultViewer360Labels.zoom,
3334
+ hotspotModeActive: labels?.hotspotModeActive ?? defaultViewer360Labels.hotspotModeActive,
3335
+ addHotspot: labels?.addHotspot ?? defaultViewer360Labels.addHotspot,
3336
+ zoomIn: labels?.zoomIn ?? defaultViewer360Labels.zoomIn,
3337
+ zoomOut: labels?.zoomOut ?? defaultViewer360Labels.zoomOut,
3338
+ resetView: labels?.resetView ?? defaultViewer360Labels.resetView,
3339
+ deleteMarker: labels?.deleteMarker ?? defaultViewer360Labels.deleteMarker
3340
+ };
3341
+ }
3342
+ function mergeViewer360ClassNames(classNames) {
3343
+ return {
3344
+ root: cn(viewer360ClassNames.root, classNames?.root),
3345
+ viewport: cn(viewer360ClassNames.viewport, classNames?.viewport),
3346
+ canvas: cn(viewer360ClassNames.canvas, classNames?.canvas),
3347
+ overlay: cn(viewer360ClassNames.overlay, classNames?.overlay),
3348
+ loading: cn(viewer360ClassNames.loading, classNames?.loading),
3349
+ loadingText: cn(viewer360ClassNames.loadingText, classNames?.loadingText),
3350
+ frameIndicator: cn(viewer360ClassNames.frameIndicator, classNames?.frameIndicator),
3351
+ hotspotModeBanner: cn(viewer360ClassNames.hotspotModeBanner, classNames?.hotspotModeBanner),
3352
+ toolbar: cn(viewer360ClassNames.toolbar, classNames?.toolbar),
3353
+ dragHint: cn(viewer360ClassNames.dragHint, classNames?.dragHint),
3354
+ controls: cn(viewer360ClassNames.controls, classNames?.controls),
3355
+ controlButton: cn(viewer360ClassNames.controlButton, classNames?.controlButton),
3356
+ controlButtonActive: cn(viewer360ClassNames.controlButtonActive, classNames?.controlButtonActive),
3357
+ controlButtonDisabled: cn(viewer360ClassNames.controlButtonDisabled, classNames?.controlButtonDisabled),
3358
+ zoomDisplay: cn(viewer360ClassNames.zoomDisplay, classNames?.zoomDisplay),
3359
+ divider: cn(viewer360ClassNames.divider, classNames?.divider)
3360
+ };
3361
+ }
3362
+ function buildViewer360ThemeStyle(theme) {
3363
+ return theme ? theme : {};
3364
+ }
3365
+
3366
+ // src/hooks/useViewer360.ts
3367
+ import { useEffect, useMemo, useRef, useState } from "react";
3368
+
3369
+ // src/helpers/adjustViewerZoom.ts
3370
+ function resolveViewer360Config(config) {
3371
+ return {
3372
+ minZoom: config?.minZoom ?? 1,
3373
+ maxZoom: config?.maxZoom ?? 3,
3374
+ zoomStep: config?.zoomStep ?? 0.15,
3375
+ dragSensitivity: config?.dragSensitivity ?? 8,
3376
+ autoRotate: config?.autoRotate ?? false,
3377
+ autoRotateIntervalMs: config?.autoRotateIntervalMs ?? 100,
3378
+ autoRotateDirection: config?.autoRotateDirection ?? "forward"
3379
+ };
3380
+ }
3381
+ function clampZoom(zoom, config) {
3382
+ return Math.min(config.maxZoom, Math.max(config.minZoom, zoom));
3383
+ }
3384
+ function applyWheelZoom(currentZoom, deltaY, currentPan, config) {
3385
+ const delta = deltaY > 0 ? -config.zoomStep : config.zoomStep;
3386
+ const zoom = clampZoom(currentZoom + delta, config);
3387
+ return {
3388
+ zoom,
3389
+ pan: zoom === config.minZoom ? { panX: 0, panY: 0 } : currentPan
3390
+ };
3391
+ }
3392
+ function stepZoomIn(currentZoom, config) {
3393
+ return clampZoom(currentZoom + config.zoomStep, config);
3394
+ }
3395
+ function stepZoomOut(currentZoom, currentPan, config) {
3396
+ const zoom = clampZoom(currentZoom - config.zoomStep, config);
3397
+ return {
3398
+ zoom,
3399
+ pan: zoom === config.minZoom ? { panX: 0, panY: 0 } : currentPan
3400
+ };
3401
+ }
3402
+ function isResetDisabled(zoom, pan, config) {
3403
+ return zoom === config.minZoom && pan.panX === 0 && pan.panY === 0;
3404
+ }
3405
+ function getViewerCursorClass(isHotspotMode, zoom, isDragging, minZoom) {
3406
+ if (isHotspotMode) return "cursor-crosshair";
3407
+ if (isDragging) return "cursor-grabbing";
3408
+ if (zoom > minZoom) return "cursor-grab";
3409
+ return "cursor-ew-resize";
3410
+ }
3411
+
3412
+ // src/helpers/computeDragFrameIndex.ts
3413
+ function clampFrameIndex(index, frameCount) {
3414
+ if (frameCount === 0) return 0;
3415
+ return (index % frameCount + frameCount) % frameCount;
3416
+ }
3417
+ function computeDragFrameIndex(dragStart, clientX, frameCount, dragSensitivity) {
3418
+ const deltaX = clientX - dragStart.pointerX;
3419
+ const frameDelta = Math.round(-deltaX / dragSensitivity);
3420
+ if (frameDelta === 0) return null;
3421
+ return clampFrameIndex(dragStart.frameIndex + frameDelta, frameCount);
3422
+ }
3423
+
3424
+ // src/helpers/computeViewerImageLayout.ts
3425
+ function computeViewerImageLayout({
3426
+ containerWidth,
3427
+ containerHeight,
3428
+ imageWidth,
3429
+ imageHeight,
3430
+ pan = { panX: 0, panY: 0 }
3431
+ }) {
3432
+ const imgAspect = imageWidth / imageHeight;
3433
+ const containerAspect = containerWidth / containerHeight;
3434
+ let drawWidth;
3435
+ let drawHeight;
3436
+ if (imgAspect > containerAspect) {
3437
+ drawWidth = containerWidth;
3438
+ drawHeight = containerWidth / imgAspect;
3439
+ } else {
3440
+ drawHeight = containerHeight;
3441
+ drawWidth = containerHeight * imgAspect;
3442
+ }
3443
+ const offsetX = (containerWidth - drawWidth) / 2 + pan.panX;
3444
+ const offsetY = (containerHeight - drawHeight) / 2 + pan.panY;
3445
+ return {
3446
+ width: containerWidth,
3447
+ height: containerHeight,
3448
+ centerX: containerWidth / 2,
3449
+ centerY: containerHeight / 2,
3450
+ drawWidth,
3451
+ drawHeight,
3452
+ offsetX,
3453
+ offsetY
3454
+ };
3455
+ }
3456
+ function computeHotspotScreenPosition(hotspotX, hotspotY, layout, zoom) {
3457
+ const baseOffsetX = (layout.width - layout.drawWidth) / 2;
3458
+ const baseOffsetY = (layout.height - layout.drawHeight) / 2;
3459
+ const containerX = hotspotX / 100 * layout.width;
3460
+ const containerY = hotspotY / 100 * layout.height;
3461
+ const imageLocalX = (containerX - baseOffsetX) / layout.drawWidth;
3462
+ const imageLocalY = (containerY - baseOffsetY) / layout.drawHeight;
3463
+ const imagePointX = layout.offsetX + imageLocalX * layout.drawWidth;
3464
+ const imagePointY = layout.offsetY + imageLocalY * layout.drawHeight;
3465
+ const screenX = layout.centerX + zoom * (imagePointX - layout.centerX);
3466
+ const screenY = layout.centerY + zoom * (imagePointY - layout.centerY);
3467
+ return {
3468
+ leftPercent: screenX / layout.width * 100,
3469
+ topPercent: screenY / layout.height * 100
3470
+ };
3471
+ }
3472
+ function computeHotspotPositionFromClick(clientX, clientY, containerRect, layout, zoom) {
3473
+ const clickX = clientX - containerRect.left;
3474
+ const clickY = clientY - containerRect.top;
3475
+ const unzoomedX = layout.centerX + (clickX - layout.centerX) / zoom;
3476
+ const unzoomedY = layout.centerY + (clickY - layout.centerY) / zoom;
3477
+ const baseOffsetX = (layout.width - layout.drawWidth) / 2;
3478
+ const baseOffsetY = (layout.height - layout.drawHeight) / 2;
3479
+ const imageLocalX = Math.min(1, Math.max(0, (unzoomedX - layout.offsetX) / layout.drawWidth));
3480
+ const imageLocalY = Math.min(1, Math.max(0, (unzoomedY - layout.offsetY) / layout.drawHeight));
3481
+ const storedX = baseOffsetX + imageLocalX * layout.drawWidth;
3482
+ const storedY = baseOffsetY + imageLocalY * layout.drawHeight;
3483
+ return {
3484
+ positionX: Math.min(100, Math.max(0, storedX / layout.width * 100)),
3485
+ positionY: Math.min(100, Math.max(0, storedY / layout.height * 100))
3486
+ };
3487
+ }
3488
+
3489
+ // src/helpers/computeViewerPanOffset.ts
3490
+ function computeViewerPanOffset(panStart, clientX, clientY) {
3491
+ const deltaX = clientX - panStart.pointerX;
3492
+ const deltaY = clientY - panStart.pointerY;
3493
+ return {
3494
+ panX: panStart.panX + deltaX,
3495
+ panY: panStart.panY + deltaY
3496
+ };
3497
+ }
3498
+
3499
+ // src/helpers/viewerHelpers.ts
3500
+ function drawFrameOnCanvas({ canvas, container, image, zoom, pan }) {
3501
+ if (!image.complete || !image.naturalWidth) return;
3502
+ const rect = container.getBoundingClientRect();
3503
+ const dpr = window.devicePixelRatio || 1;
3504
+ canvas.width = rect.width * dpr;
3505
+ canvas.height = rect.height * dpr;
3506
+ canvas.style.width = `${rect.width}px`;
3507
+ canvas.style.height = `${rect.height}px`;
3508
+ const ctx = canvas.getContext("2d");
3509
+ if (!ctx) return;
3510
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
3511
+ ctx.clearRect(0, 0, rect.width, rect.height);
3512
+ const layout = computeViewerImageLayout({
3513
+ containerWidth: rect.width,
3514
+ containerHeight: rect.height,
3515
+ imageWidth: image.naturalWidth,
3516
+ imageHeight: image.naturalHeight,
3517
+ pan
3518
+ });
3519
+ ctx.save();
3520
+ ctx.translate(layout.centerX, layout.centerY);
3521
+ ctx.scale(zoom, zoom);
3522
+ ctx.translate(-layout.centerX, -layout.centerY);
3523
+ ctx.drawImage(image, layout.offsetX, layout.offsetY, layout.drawWidth, layout.drawHeight);
3524
+ ctx.restore();
3525
+ }
3526
+ function getFramesSignature(frames) {
3527
+ return frames.map((frame) => frame.id).join("-");
3528
+ }
3529
+ function preloadFrameImage(frame) {
3530
+ return new Promise((resolve, reject) => {
3531
+ const img = new Image();
3532
+ img.onload = () => resolve(img);
3533
+ img.onerror = () => reject(new Error(`Failed to load frame: ${frame.src}`));
3534
+ img.src = frame.src;
3535
+ });
3536
+ }
3537
+ async function preloadViewerFrames(frames) {
3538
+ const results = await Promise.allSettled(frames.map(preloadFrameImage));
3539
+ return results.map((result) => result.status === "fulfilled" ? result.value : new Image());
3540
+ }
3541
+ function hasLoadedViewerFrame(images) {
3542
+ return images.some((image) => image.complete && image.naturalWidth > 0);
3543
+ }
3544
+ function filterHotspotsByFrame(hotspots, frameIndex) {
3545
+ return hotspots.filter((hotspot) => hotspot.frameIndex === frameIndex);
3546
+ }
3547
+
3548
+ // src/hooks/useViewer360.ts
3549
+ function useViewer360({
3550
+ frames,
3551
+ hotspots = [],
3552
+ currentFrameIndex,
3553
+ onFrameChange,
3554
+ config,
3555
+ hotspotMode: hotspotModeProp = false,
3556
+ onHotspotAdd
3557
+ }) {
3558
+ const resolvedConfig = useMemo(() => resolveViewer360Config(config), [config]);
3559
+ const { minZoom, maxZoom } = resolvedConfig;
3560
+ const canvasRef = useRef(null);
3561
+ const containerRef = useRef(null);
3562
+ const imagesRef = useRef([]);
3563
+ const dragStartRef = useRef(null);
3564
+ const panStartRef = useRef(null);
3565
+ const framesSignature = getFramesSignature(frames);
3566
+ const [loadedSignature, setLoadedSignature] = useState(null);
3567
+ const [zoom, setZoom] = useState(minZoom);
3568
+ const [pan, setPan] = useState({ panX: 0, panY: 0 });
3569
+ const [isDragging, setIsDragging] = useState(false);
3570
+ const isHotspotMode = hotspotModeProp;
3571
+ const imagesLoaded = loadedSignature === framesSignature;
3572
+ const currentFrameHotspots = filterHotspotsByFrame(hotspots, currentFrameIndex);
3573
+ const currentFrame = frames[currentFrameIndex];
3574
+ const viewerCursorClass = getViewerCursorClass(isHotspotMode, zoom, isDragging, minZoom);
3575
+ const resetDisabled = isResetDisabled(zoom, pan, resolvedConfig);
3576
+ useEffect(() => {
3577
+ let cancelled = false;
3578
+ imagesRef.current = [];
3579
+ async function loadFrames() {
3580
+ const loadedImages = await preloadViewerFrames(frames);
3581
+ if (!cancelled && hasLoadedViewerFrame(loadedImages)) {
3582
+ imagesRef.current = loadedImages;
3583
+ setLoadedSignature(framesSignature);
3584
+ }
3585
+ }
3586
+ void loadFrames();
3587
+ return () => {
3588
+ cancelled = true;
3589
+ };
3590
+ }, [frames, framesSignature]);
3591
+ useEffect(() => {
3592
+ if (!imagesLoaded) return;
3593
+ function renderFrame() {
3594
+ const canvas = canvasRef.current;
3595
+ const container = containerRef.current;
3596
+ const img = imagesRef.current[currentFrameIndex];
3597
+ if (!canvas || !container) return;
3598
+ drawFrameOnCanvas({ canvas, container, image: img, zoom, pan });
3599
+ }
3600
+ renderFrame();
3601
+ window.addEventListener("resize", renderFrame);
3602
+ return () => window.removeEventListener("resize", renderFrame);
3603
+ }, [imagesLoaded, currentFrameIndex, zoom, pan]);
3604
+ useEffect(() => {
3605
+ if (!resolvedConfig.autoRotate || frames.length <= 1 || isDragging || isHotspotMode || zoom > minZoom) {
3606
+ return;
3607
+ }
3608
+ const direction = resolvedConfig.autoRotateDirection === "backward" ? -1 : 1;
3609
+ const interval = window.setInterval(() => {
3610
+ onFrameChange((currentFrameIndex + direction + frames.length) % frames.length);
3611
+ }, resolvedConfig.autoRotateIntervalMs);
3612
+ return () => window.clearInterval(interval);
3613
+ }, [
3614
+ resolvedConfig.autoRotate,
3615
+ resolvedConfig.autoRotateDirection,
3616
+ resolvedConfig.autoRotateIntervalMs,
3617
+ frames.length,
3618
+ currentFrameIndex,
3619
+ isDragging,
3620
+ isHotspotMode,
3621
+ zoom,
3622
+ minZoom,
3623
+ onFrameChange
3624
+ ]);
3625
+ function getCurrentImageLayout() {
3626
+ const container = containerRef.current;
3627
+ const image = imagesRef.current[currentFrameIndex];
3628
+ if (!container || !image || !image.complete || !image.naturalWidth) return null;
3629
+ const rect = container.getBoundingClientRect();
3630
+ return computeViewerImageLayout({
3631
+ containerWidth: rect.width,
3632
+ containerHeight: rect.height,
3633
+ imageWidth: image.naturalWidth,
3634
+ imageHeight: image.naturalHeight,
3635
+ pan
3636
+ });
3637
+ }
3638
+ function getHotspotScreenPosition(hotspot) {
3639
+ const layout = getCurrentImageLayout();
3640
+ if (!layout) {
3641
+ return { leftPercent: hotspot.positionX, topPercent: hotspot.positionY };
3642
+ }
3643
+ return computeHotspotScreenPosition(hotspot.positionX, hotspot.positionY, layout, zoom);
3644
+ }
3645
+ function handlePointerDown(event) {
3646
+ if (isHotspotMode) return;
3647
+ event.currentTarget.setPointerCapture(event.pointerId);
3648
+ if (zoom > minZoom) {
3649
+ panStartRef.current = { pointerX: event.clientX, pointerY: event.clientY, panX: pan.panX, panY: pan.panY };
3650
+ } else {
3651
+ dragStartRef.current = { pointerX: event.clientX, frameIndex: currentFrameIndex };
3652
+ }
3653
+ setIsDragging(true);
3654
+ }
3655
+ function handlePointerMove(event) {
3656
+ if (!isDragging) return;
3657
+ if (panStartRef.current && zoom > minZoom) {
3658
+ setPan(computeViewerPanOffset(panStartRef.current, event.clientX, event.clientY));
3659
+ return;
3660
+ }
3661
+ if (dragStartRef.current && zoom <= minZoom) {
3662
+ const nextFrameIndex = computeDragFrameIndex(
3663
+ dragStartRef.current,
3664
+ event.clientX,
3665
+ frames.length,
3666
+ resolvedConfig.dragSensitivity
3667
+ );
3668
+ if (nextFrameIndex !== null) {
3669
+ onFrameChange(nextFrameIndex);
3670
+ }
3671
+ }
3672
+ }
3673
+ function handlePointerUp(event) {
3674
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
3675
+ event.currentTarget.releasePointerCapture(event.pointerId);
3676
+ }
3677
+ dragStartRef.current = null;
3678
+ panStartRef.current = null;
3679
+ setIsDragging(false);
3680
+ }
3681
+ function handleWheel(event) {
3682
+ event.preventDefault();
3683
+ const { zoom: nextZoom, pan: nextPan } = applyWheelZoom(zoom, event.deltaY, pan, resolvedConfig);
3684
+ setZoom(nextZoom);
3685
+ setPan(nextPan);
3686
+ }
3687
+ function handleCanvasClick(event) {
3688
+ if (!isHotspotMode || isDragging || !onHotspotAdd) return;
3689
+ const frame = frames[currentFrameIndex];
3690
+ if (!frame) return;
3691
+ const layout = getCurrentImageLayout();
3692
+ if (!layout) return;
3693
+ const { positionX, positionY } = computeHotspotPositionFromClick(
3694
+ event.clientX,
3695
+ event.clientY,
3696
+ event.currentTarget.getBoundingClientRect(),
3697
+ layout,
3698
+ zoom
3699
+ );
3700
+ onHotspotAdd({
3701
+ frameIndex: currentFrameIndex,
3702
+ frameId: frame.id,
3703
+ positionX,
3704
+ positionY
3705
+ });
3706
+ }
3707
+ function handleResetView() {
3708
+ setZoom(minZoom);
3709
+ setPan({ panX: 0, panY: 0 });
3710
+ }
3711
+ function handleZoomIn() {
3712
+ setZoom(stepZoomIn(zoom, resolvedConfig));
3713
+ }
3714
+ function handleZoomOut() {
3715
+ const { zoom: nextZoom, pan: nextPan } = stepZoomOut(zoom, pan, resolvedConfig);
3716
+ setZoom(nextZoom);
3717
+ setPan(nextPan);
3718
+ }
3719
+ return {
3720
+ canvasRef,
3721
+ containerRef,
3722
+ currentFrame,
3723
+ currentFrameHotspots,
3724
+ imagesLoaded,
3725
+ isHotspotMode,
3726
+ isResetDisabled: resetDisabled,
3727
+ maxZoom,
3728
+ minZoom,
3729
+ viewerCursorClass,
3730
+ zoom,
3731
+ getCurrentImageLayout,
3732
+ getHotspotScreenPosition,
3733
+ handleCanvasClick,
3734
+ handlePointerDown,
3735
+ handlePointerMove,
3736
+ handlePointerUp,
3737
+ handleResetView,
3738
+ handleWheel,
3739
+ handleZoomIn,
3740
+ handleZoomOut
3741
+ };
3742
+ }
3743
+
3744
+ // src/components/ui/Card/index.tsx
3745
+ import { jsx } from "react/jsx-runtime";
3746
+ function Card({ className, size = "default", ...props }) {
3747
+ return /* @__PURE__ */ jsx(
3748
+ "div",
3749
+ {
3750
+ "data-slot": "card",
3751
+ "data-size": size,
3752
+ className: cn(
3753
+ "ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
3754
+ className
3755
+ ),
3756
+ ...props
3757
+ }
3758
+ );
3759
+ }
3760
+ function CardFooter({ className, ...props }) {
3761
+ return /* @__PURE__ */ jsx(
3762
+ "div",
3763
+ {
3764
+ "data-slot": "card-footer",
3765
+ className: cn(
3766
+ "rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center",
3767
+ className
3768
+ ),
3769
+ ...props
3770
+ }
3771
+ );
3772
+ }
3773
+
3774
+ // node_modules/class-variance-authority/dist/index.mjs
3775
+ var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
3776
+ var cx = clsx;
3777
+ var cva = (base, config) => (props) => {
3778
+ var _config_compoundVariants;
3779
+ if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
3780
+ const { variants, defaultVariants } = config;
3781
+ const getVariantClassNames = Object.keys(variants).map((variant) => {
3782
+ const variantProp = props === null || props === void 0 ? void 0 : props[variant];
3783
+ const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
3784
+ if (variantProp === null) return null;
3785
+ const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
3786
+ return variants[variant][variantKey];
3787
+ });
3788
+ const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
3789
+ let [key, value] = param;
3790
+ if (value === void 0) {
3791
+ return acc;
3792
+ }
3793
+ acc[key] = value;
3794
+ return acc;
3795
+ }, {});
3796
+ const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
3797
+ let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
3798
+ return Object.entries(compoundVariantOptions).every((param2) => {
3799
+ let [key, value] = param2;
3800
+ return Array.isArray(value) ? value.includes({
3801
+ ...defaultVariants,
3802
+ ...propsWithoutUndefined
3803
+ }[key]) : {
3804
+ ...defaultVariants,
3805
+ ...propsWithoutUndefined
3806
+ }[key] === value;
3807
+ }) ? [
3808
+ ...acc,
3809
+ cvClass,
3810
+ cvClassName
3811
+ ] : acc;
3812
+ }, []);
3813
+ return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
3814
+ };
3815
+
3816
+ // node_modules/@radix-ui/react-slot/dist/index.mjs
3817
+ import * as React2 from "react";
3818
+
3819
+ // node_modules/@radix-ui/react-compose-refs/dist/index.mjs
3820
+ import * as React from "react";
3821
+ function setRef(ref, value) {
3822
+ if (typeof ref === "function") {
3823
+ return ref(value);
3824
+ } else if (ref !== null && ref !== void 0) {
3825
+ ref.current = value;
3826
+ }
3827
+ }
3828
+ function composeRefs(...refs) {
3829
+ return (node) => {
3830
+ let hasCleanup = false;
3831
+ const cleanups = refs.map((ref) => {
3832
+ const cleanup = setRef(ref, node);
3833
+ if (!hasCleanup && typeof cleanup == "function") {
3834
+ hasCleanup = true;
3835
+ }
3836
+ return cleanup;
3837
+ });
3838
+ if (hasCleanup) {
3839
+ return () => {
3840
+ for (let i = 0; i < cleanups.length; i++) {
3841
+ const cleanup = cleanups[i];
3842
+ if (typeof cleanup == "function") {
3843
+ cleanup();
3844
+ } else {
3845
+ setRef(refs[i], null);
3846
+ }
3847
+ }
3848
+ };
3849
+ }
3850
+ };
3851
+ }
3852
+ function useComposedRefs(...refs) {
3853
+ return React.useCallback(composeRefs(...refs), refs);
3854
+ }
3855
+
3856
+ // node_modules/@radix-ui/react-slot/dist/index.mjs
3857
+ // @__NO_SIDE_EFFECTS__
3858
+ function createSlot(ownerName) {
3859
+ const Slot2 = React2.forwardRef((props, forwardedRef) => {
3860
+ let { children, ...slotProps } = props;
3861
+ let slottableElement = null;
3862
+ let hasSlottable = false;
3863
+ const newChildren = [];
3864
+ if (isLazyComponent(children) && typeof use === "function") {
3865
+ children = use(children._payload);
3866
+ }
3867
+ React2.Children.forEach(children, (maybeSlottable) => {
3868
+ if (isSlottable(maybeSlottable)) {
3869
+ hasSlottable = true;
3870
+ const slottable = maybeSlottable;
3871
+ let child = "child" in slottable.props ? slottable.props.child : slottable.props.children;
3872
+ if (isLazyComponent(child) && typeof use === "function") {
3873
+ child = use(child._payload);
3874
+ }
3875
+ slottableElement = getSlottableElementFromSlottable(slottable, child);
3876
+ newChildren.push(slottableElement?.props?.children);
3877
+ } else {
3878
+ newChildren.push(maybeSlottable);
3879
+ }
3880
+ });
3881
+ if (slottableElement) {
3882
+ slottableElement = React2.cloneElement(slottableElement, void 0, newChildren);
3883
+ } else if (
3884
+ // A `Slottable` was found but it didn't resolve to a single element (e.g.
3885
+ // it wrapped multiple elements, text, or a render-prop `child` that
3886
+ // wasn't an element). Don't fall back to treating the `Slottable` wrapper
3887
+ // itself as the slot target — throw a descriptive error below instead.
3888
+ !hasSlottable && React2.Children.count(children) === 1 && React2.isValidElement(children)
3889
+ ) {
3890
+ slottableElement = children;
3891
+ }
3892
+ const slottableElementRef = slottableElement ? getElementRef(slottableElement) : void 0;
3893
+ const composedRef = useComposedRefs(forwardedRef, slottableElementRef);
3894
+ if (!slottableElement) {
3895
+ if (children || children === 0) {
3896
+ throw new Error(
3897
+ hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName)
3898
+ );
3899
+ }
3900
+ return children;
3901
+ }
3902
+ const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});
3903
+ if (slottableElement.type !== React2.Fragment) {
3904
+ mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;
3905
+ }
3906
+ return React2.cloneElement(slottableElement, mergedProps);
3907
+ });
3908
+ Slot2.displayName = `${ownerName}.Slot`;
3909
+ return Slot2;
3910
+ }
3911
+ var Slot = /* @__PURE__ */ createSlot("Slot");
3912
+ var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol.for("radix.slottable");
3913
+ var getSlottableElementFromSlottable = (slottable, child) => {
3914
+ if ("child" in slottable.props) {
3915
+ const child2 = slottable.props.child;
3916
+ if (!React2.isValidElement(child2)) return null;
3917
+ return React2.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
3918
+ }
3919
+ return React2.isValidElement(child) ? child : null;
3920
+ };
3921
+ function mergeProps(slotProps, childProps) {
3922
+ const overrideProps = { ...childProps };
3923
+ for (const propName in childProps) {
3924
+ const slotPropValue = slotProps[propName];
3925
+ const childPropValue = childProps[propName];
3926
+ const isHandler = /^on[A-Z]/.test(propName);
3927
+ if (isHandler) {
3928
+ if (slotPropValue && childPropValue) {
3929
+ overrideProps[propName] = (...args) => {
3930
+ const result = childPropValue(...args);
3931
+ slotPropValue(...args);
3932
+ return result;
3933
+ };
3934
+ } else if (slotPropValue) {
3935
+ overrideProps[propName] = slotPropValue;
3936
+ }
3937
+ } else if (propName === "style") {
3938
+ overrideProps[propName] = { ...slotPropValue, ...childPropValue };
3939
+ } else if (propName === "className") {
3940
+ overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
3941
+ }
3942
+ }
3943
+ return { ...slotProps, ...overrideProps };
3944
+ }
3945
+ function getElementRef(element) {
3946
+ let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
3947
+ let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
3948
+ if (mayWarn) {
3949
+ return element.ref;
3950
+ }
3951
+ getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
3952
+ mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
3953
+ if (mayWarn) {
3954
+ return element.props.ref;
3955
+ }
3956
+ return element.props.ref || element.ref;
3957
+ }
3958
+ function isSlottable(child) {
3959
+ return React2.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
3960
+ }
3961
+ var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
3962
+ function isLazyComponent(element) {
3963
+ return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
3964
+ }
3965
+ function isPromiseLike(value) {
3966
+ return typeof value === "object" && value !== null && "then" in value;
3967
+ }
3968
+ var createSlotError = (ownerName) => {
3969
+ return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
3970
+ };
3971
+ var createSlottableError = (ownerName) => {
3972
+ return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
3973
+ };
3974
+ var use = React2[" use ".trim().toString()];
3975
+
3976
+ // src/components/ui/Badge/index.tsx
3977
+ import { jsx as jsx2 } from "react/jsx-runtime";
3978
+ var badgeVariants = cva(
3979
+ "h-5 gap-1 rounded-full border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
3980
+ {
3981
+ variants: {
3982
+ variant: {
3983
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
3984
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
3985
+ destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
3986
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
3987
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
3988
+ link: "text-primary underline-offset-4 hover:underline",
3989
+ info: "bg-blue-500 text-white hover:bg-blue-600",
3990
+ warning: "rounded-full border-transparent bg-amber-500 px-2.5 text-white shadow-none [a]:hover:bg-amber-600 [&>svg]:text-white",
3991
+ success: "bg-green-600 text-white hover:bg-green-700"
3992
+ }
3993
+ },
3994
+ defaultVariants: {
3995
+ variant: "default"
3996
+ }
3997
+ }
3998
+ );
3999
+ function Badge({
4000
+ className,
4001
+ variant = "default",
4002
+ asChild = false,
4003
+ ...props
4004
+ }) {
4005
+ const Comp = asChild ? Slot : "span";
4006
+ return /* @__PURE__ */ jsx2(Comp, { "data-slot": "badge", "data-variant": variant, className: cn(badgeVariants({ variant }), className), ...props });
4007
+ }
4008
+
4009
+ // src/feature/Viewer360AddModeBanner.tsx
4010
+ import { jsx as jsx3 } from "react/jsx-runtime";
4011
+ function Viewer360AddModeBanner({ className, label }) {
4012
+ return /* @__PURE__ */ jsx3(Badge, { variant: "outline", className: cn("pointer-events-none", className), children: label });
4013
+ }
4014
+
4015
+ // src/feature/Viewer360FrameIndicator.tsx
4016
+ import { jsx as jsx4 } from "react/jsx-runtime";
4017
+ function Viewer360FrameIndicator({ className, label }) {
4018
+ return /* @__PURE__ */ jsx4(Badge, { variant: "outline", className: cn("pointer-events-none shadow-sm", className), children: label });
4019
+ }
4020
+
4021
+ // src/helpers/markerHelpers.ts
4022
+ function hotspotToViewer360Marker(hotspot) {
4023
+ const data = hotspot.data;
4024
+ if (data && typeof data === "object" && "title" in data && typeof data.title === "string") {
4025
+ const marker = data;
4026
+ return {
4027
+ id: marker.id ?? hotspot.id,
4028
+ title: marker.title,
4029
+ description: marker.description
4030
+ };
4031
+ }
4032
+ return {
4033
+ id: hotspot.id,
4034
+ title: hotspot.id
4035
+ };
4036
+ }
4037
+ function toViewer360Hotspots(markers, mapData) {
4038
+ return markers.map((marker) => ({
4039
+ id: marker.id,
4040
+ frameIndex: marker.frameIndex,
4041
+ positionX: marker.positionX,
4042
+ positionY: marker.positionY,
4043
+ data: mapData ? mapData(marker) : marker
4044
+ }));
4045
+ }
4046
+
4047
+ // node_modules/@radix-ui/react-separator/dist/index.mjs
4048
+ import * as React4 from "react";
4049
+
4050
+ // node_modules/@radix-ui/react-primitive/dist/index.mjs
4051
+ import * as React3 from "react";
4052
+ import * as ReactDOM from "react-dom";
4053
+ import { jsx as jsx5 } from "react/jsx-runtime";
4054
+ var NODES = [
4055
+ "a",
4056
+ "button",
4057
+ "div",
4058
+ "form",
4059
+ "h2",
4060
+ "h3",
4061
+ "img",
4062
+ "input",
4063
+ "label",
4064
+ "li",
4065
+ "nav",
4066
+ "ol",
4067
+ "p",
4068
+ "select",
4069
+ "span",
4070
+ "svg",
4071
+ "ul"
4072
+ ];
4073
+ var Primitive = NODES.reduce((primitive, node) => {
4074
+ const Slot2 = createSlot(`Primitive.${node}`);
4075
+ const Node = React3.forwardRef((props, forwardedRef) => {
4076
+ const { asChild, ...primitiveProps } = props;
4077
+ const Comp = asChild ? Slot2 : node;
4078
+ if (typeof window !== "undefined") {
4079
+ window[/* @__PURE__ */ Symbol.for("radix-ui")] = true;
4080
+ }
4081
+ return /* @__PURE__ */ jsx5(Comp, { ...primitiveProps, ref: forwardedRef });
4082
+ });
4083
+ Node.displayName = `Primitive.${node}`;
4084
+ return { ...primitive, [node]: Node };
4085
+ }, {});
4086
+
4087
+ // node_modules/@radix-ui/react-separator/dist/index.mjs
4088
+ import { jsx as jsx6 } from "react/jsx-runtime";
4089
+ var NAME = "Separator";
4090
+ var DEFAULT_ORIENTATION = "horizontal";
4091
+ var ORIENTATIONS = ["horizontal", "vertical"];
4092
+ var Separator = React4.forwardRef((props, forwardedRef) => {
4093
+ const { decorative, orientation: orientationProp = DEFAULT_ORIENTATION, ...domProps } = props;
4094
+ const orientation = isValidOrientation(orientationProp) ? orientationProp : DEFAULT_ORIENTATION;
4095
+ const ariaOrientation = orientation === "vertical" ? orientation : void 0;
4096
+ const semanticProps = decorative ? { role: "none" } : { "aria-orientation": ariaOrientation, role: "separator" };
4097
+ return /* @__PURE__ */ jsx6(
4098
+ Primitive.div,
4099
+ {
4100
+ "data-orientation": orientation,
4101
+ ...semanticProps,
4102
+ ...domProps,
4103
+ ref: forwardedRef
4104
+ }
4105
+ );
4106
+ });
4107
+ Separator.displayName = NAME;
4108
+ function isValidOrientation(orientation) {
4109
+ return ORIENTATIONS.includes(orientation);
4110
+ }
4111
+ var Root = Separator;
4112
+
4113
+ // src/components/ui/Separator/index.tsx
4114
+ import { jsx as jsx7 } from "react/jsx-runtime";
4115
+ function Separator2({
4116
+ className,
4117
+ orientation = "horizontal",
4118
+ decorative = true,
4119
+ ...props
4120
+ }) {
4121
+ return /* @__PURE__ */ jsx7(
4122
+ Root,
4123
+ {
4124
+ "data-slot": "separator",
4125
+ decorative,
4126
+ orientation,
4127
+ className: cn(
4128
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
4129
+ className
4130
+ ),
4131
+ ...props
4132
+ }
4133
+ );
4134
+ }
4135
+
4136
+ // src/components/ui/Item/index.tsx
4137
+ import { jsx as jsx8 } from "react/jsx-runtime";
4138
+ var itemVariants = cva(
4139
+ "[a]:hover:bg-muted rounded-md border text-sm w-full group/item focus-visible:border-ring focus-visible:ring-ring/50 flex items-center flex-wrap outline-none transition-colors duration-100 focus-visible:ring-[3px] [a]:transition-colors",
4140
+ {
4141
+ variants: {
4142
+ variant: {
4143
+ default: "border-transparent",
4144
+ outline: "border-border",
4145
+ muted: "bg-muted/50 border-transparent"
4146
+ },
4147
+ size: {
4148
+ default: "gap-3.5 px-4 py-3.5",
4149
+ sm: "gap-2.5 px-3 py-2.5",
4150
+ xs: "gap-2 px-2.5 py-2 [[data-slot=dropdown-menu-content]_&]:p-0"
4151
+ }
4152
+ },
4153
+ defaultVariants: {
4154
+ variant: "default",
4155
+ size: "default"
4156
+ }
4157
+ }
4158
+ );
4159
+ function Item({
4160
+ className,
4161
+ variant = "default",
4162
+ size = "default",
4163
+ asChild = false,
4164
+ ...props
4165
+ }) {
4166
+ const Comp = asChild ? Slot : "div";
4167
+ return /* @__PURE__ */ jsx8(
4168
+ Comp,
4169
+ {
4170
+ "data-slot": "item",
4171
+ "data-variant": variant,
4172
+ "data-size": size,
4173
+ className: cn(itemVariants({ variant, size, className })),
4174
+ ...props
4175
+ }
4176
+ );
4177
+ }
4178
+ var itemMediaVariants = cva(
4179
+ "gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none",
4180
+ {
4181
+ variants: {
4182
+ variant: {
4183
+ default: "bg-transparent",
4184
+ icon: "[&_svg]:size-4",
4185
+ image: "size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover"
4186
+ }
4187
+ },
4188
+ defaultVariants: {
4189
+ variant: "default"
4190
+ }
4191
+ }
4192
+ );
4193
+ function ItemContent({ className, ...props }) {
4194
+ return /* @__PURE__ */ jsx8(
4195
+ "div",
4196
+ {
4197
+ "data-slot": "item-content",
4198
+ className: cn("gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none", className),
4199
+ ...props
4200
+ }
4201
+ );
4202
+ }
4203
+ function ItemTitle({ className, ...props }) {
4204
+ return /* @__PURE__ */ jsx8(
4205
+ "div",
4206
+ {
4207
+ "data-slot": "item-title",
4208
+ className: cn("gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center", className),
4209
+ ...props
4210
+ }
4211
+ );
4212
+ }
4213
+ function ItemDescription({ className, ...props }) {
4214
+ return /* @__PURE__ */ jsx8(
4215
+ "p",
4216
+ {
4217
+ "data-slot": "item-description",
4218
+ className: cn(
4219
+ "text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs [&>a:hover]:text-primary line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4",
4220
+ className
4221
+ ),
4222
+ ...props
4223
+ }
4224
+ );
4225
+ }
4226
+ function ItemActions({ className, ...props }) {
4227
+ return /* @__PURE__ */ jsx8("div", { "data-slot": "item-actions", className: cn("gap-2 flex items-center", className), ...props });
4228
+ }
4229
+
4230
+ // src/feature/Viewer360MarkerPin.tsx
4231
+ import { Trash2 } from "lucide-react";
4232
+
4233
+ // src/constants/viewer360MarkerLabels.ts
4234
+ var defaultViewer360MarkerPinLabels = {
4235
+ delete: "Remove marker"
4236
+ };
4237
+
4238
+ // src/components/ui/Button/index.tsx
4239
+ import { jsx as jsx9 } from "react/jsx-runtime";
4240
+ var buttonVariants = cva(
4241
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
4242
+ {
4243
+ variants: {
4244
+ variant: {
4245
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
4246
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
4247
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
4248
+ ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
4249
+ destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
4250
+ link: "text-primary underline-offset-4 hover:underline"
4251
+ },
4252
+ size: {
4253
+ default: "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2",
4254
+ xs: "h-6 gap-1 rounded-md px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
4255
+ sm: "h-8 gap-1 rounded-md px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
4256
+ lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3",
4257
+ icon: "size-9",
4258
+ "icon-xs": "size-6 rounded-md in-data-[slot=button-group]:rounded-md",
4259
+ "icon-sm": "size-8 rounded-md in-data-[slot=button-group]:rounded-md",
4260
+ "icon-lg": "size-10"
4261
+ }
4262
+ },
4263
+ defaultVariants: {
4264
+ variant: "default",
4265
+ size: "default"
4266
+ }
4267
+ }
4268
+ );
4269
+ function Button({
4270
+ className,
4271
+ variant = "default",
4272
+ size = "default",
4273
+ asChild = false,
4274
+ ...props
4275
+ }) {
4276
+ const Comp = asChild ? Slot : "button";
4277
+ return /* @__PURE__ */ jsx9(
4278
+ Comp,
4279
+ {
4280
+ "data-slot": "button",
4281
+ "data-variant": variant,
4282
+ "data-size": size,
4283
+ className: cn(buttonVariants({ variant, size, className })),
4284
+ ...props
4285
+ }
4286
+ );
4287
+ }
4288
+
4289
+ // src/feature/Viewer360MarkerPin.tsx
4290
+ import { jsx as jsx10, jsxs } from "react/jsx-runtime";
4291
+ function Viewer360MarkerPin({
4292
+ marker,
4293
+ hotspot,
4294
+ leftPercent,
4295
+ topPercent,
4296
+ onDelete,
4297
+ isDeletePending = false,
4298
+ onClick,
4299
+ renderTag,
4300
+ classNames,
4301
+ labels
4302
+ }) {
4303
+ const deleteLabel = labels?.delete ?? defaultViewer360MarkerPinLabels.delete;
4304
+ const showTooltip = Boolean(marker.title || marker.description || onDelete || renderTag);
4305
+ return /* @__PURE__ */ jsxs(
4306
+ "div",
4307
+ {
4308
+ className: cn(viewer360MarkerPinClassNames.root, classNames?.root, "group/marker"),
4309
+ style: { left: `${leftPercent}%`, top: `${topPercent}%` },
4310
+ children: [
4311
+ /* @__PURE__ */ jsxs("div", { className: "relative size-4 shrink-0", children: [
4312
+ /* @__PURE__ */ jsx10("span", { className: cn(viewer360MarkerPinClassNames.ping, classNames?.ping), "aria-hidden": "true" }),
4313
+ /* @__PURE__ */ jsx10(
4314
+ Button,
4315
+ {
4316
+ type: "button",
4317
+ variant: "destructive",
4318
+ size: "icon-xs",
4319
+ className: cn(viewer360MarkerPinClassNames.dot, classNames?.dot, "hover:bg-destructive"),
4320
+ "aria-label": marker.title,
4321
+ onClick
4322
+ }
4323
+ )
4324
+ ] }),
4325
+ showTooltip && /* @__PURE__ */ jsx10(
4326
+ "div",
4327
+ {
4328
+ className: cn(
4329
+ viewer360MarkerPinClassNames.tooltip,
4330
+ classNames?.tooltip,
4331
+ "pointer-events-none opacity-0 transition-opacity duration-150 group-hover/marker:pointer-events-auto group-hover/marker:opacity-100 group-focus-within/marker:pointer-events-auto group-focus-within/marker:opacity-100"
4332
+ ),
4333
+ children: /* @__PURE__ */ jsxs(
4334
+ Item,
4335
+ {
4336
+ size: "sm",
4337
+ variant: "default",
4338
+ className: cn(viewer360MarkerPinClassNames.tooltipHeader, classNames?.tooltipHeader, "w-full border-transparent"),
4339
+ children: [
4340
+ /* @__PURE__ */ jsxs(ItemContent, { className: cn(viewer360MarkerPinClassNames.tooltipBody, classNames?.tooltipBody), children: [
4341
+ /* @__PURE__ */ jsx10(ItemTitle, { className: cn(viewer360MarkerPinClassNames.tooltipTitle, classNames?.tooltipTitle), children: marker.title }),
4342
+ renderTag && /* @__PURE__ */ jsx10("div", { className: "mt-1 flex w-fit items-center", children: renderTag({ marker, hotspot }) }),
4343
+ marker.description && /* @__PURE__ */ jsx10(ItemDescription, { className: cn(viewer360MarkerPinClassNames.tooltipDescription, classNames?.tooltipDescription), children: marker.description })
4344
+ ] }),
4345
+ onDelete && /* @__PURE__ */ jsx10(ItemActions, { children: /* @__PURE__ */ jsx10(
4346
+ Button,
4347
+ {
4348
+ variant: "ghost",
4349
+ size: "icon-sm",
4350
+ className: classNames?.deleteButton,
4351
+ disabled: isDeletePending,
4352
+ "aria-label": deleteLabel,
4353
+ onClick: (event) => {
4354
+ event.stopPropagation();
4355
+ onDelete(marker.id);
4356
+ },
4357
+ children: /* @__PURE__ */ jsx10(Trash2, { className: "size-4" })
4358
+ }
4359
+ ) })
4360
+ ]
4361
+ }
4362
+ )
4363
+ }
4364
+ )
4365
+ ]
4366
+ }
4367
+ );
4368
+ }
4369
+
4370
+ // src/feature/Viewer360HotspotOverlay.tsx
4371
+ import { jsx as jsx11 } from "react/jsx-runtime";
4372
+ function Viewer360HotspotOverlay({
4373
+ hotspot,
4374
+ leftPercent,
4375
+ topPercent,
4376
+ hotspotPin,
4377
+ renderHotspot,
4378
+ onHotspotClick
4379
+ }) {
4380
+ if (renderHotspot) {
4381
+ return /* @__PURE__ */ jsx11(Item, { size: "xs", variant: "default", className: "pointer-events-auto w-auto border-transparent p-0", children: renderHotspot({ hotspot, leftPercent, topPercent }) });
4382
+ }
4383
+ const marker = hotspotPin?.getMarker?.(hotspot) ?? hotspotToViewer360Marker(hotspot);
4384
+ return /* @__PURE__ */ jsx11(
4385
+ Viewer360MarkerPin,
4386
+ {
4387
+ marker,
4388
+ hotspot,
4389
+ leftPercent,
4390
+ topPercent,
4391
+ onDelete: hotspotPin?.onDelete,
4392
+ isDeletePending: hotspotPin?.deletingMarkerId === hotspot.id,
4393
+ renderTag: hotspotPin?.renderTag,
4394
+ classNames: hotspotPin?.classNames,
4395
+ labels: hotspotPin?.labels,
4396
+ onClick: onHotspotClick ? (event) => onHotspotClick(hotspot, event) : void 0
4397
+ }
4398
+ );
4399
+ }
4400
+
4401
+ // src/components/ui/Label/index.tsx
4402
+ import { jsx as jsx12 } from "react/jsx-runtime";
4403
+ function Label({ className, ...props }) {
4404
+ return /* @__PURE__ */ jsx12(
4405
+ "label",
4406
+ {
4407
+ "data-slot": "label",
4408
+ className: cn(
4409
+ "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
4410
+ className
4411
+ ),
4412
+ ...props
4413
+ }
4414
+ );
4415
+ }
4416
+
4417
+ // src/components/ui/Spinner/index.tsx
4418
+ import { Loader2Icon } from "lucide-react";
4419
+ import { jsx as jsx13 } from "react/jsx-runtime";
4420
+ function Spinner({ className, ...props }) {
4421
+ return /* @__PURE__ */ jsx13(Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
4422
+ }
4423
+
4424
+ // src/feature/Viewer360LoadingOverlay.tsx
4425
+ import { jsx as jsx14, jsxs as jsxs2 } from "react/jsx-runtime";
4426
+ function Viewer360LoadingOverlay({ className, textClassName, label }) {
4427
+ return /* @__PURE__ */ jsxs2(
4428
+ Item,
4429
+ {
4430
+ size: "sm",
4431
+ variant: "muted",
4432
+ className: cn("pointer-events-none w-auto justify-center border-transparent bg-muted/80", className),
4433
+ children: [
4434
+ /* @__PURE__ */ jsx14(Spinner, { className: "size-5 text-muted-foreground" }),
4435
+ /* @__PURE__ */ jsx14(Label, { className: cn("font-normal text-muted-foreground", textClassName), children: label })
4436
+ ]
4437
+ }
4438
+ );
4439
+ }
4440
+
4441
+ // src/feature/Viewer360Toolbar.tsx
4442
+ import { Crosshair, Minus, Plus, RotateCcw, ZoomIn } from "lucide-react";
4443
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs3 } from "react/jsx-runtime";
4444
+ function Viewer360Toolbar({
4445
+ showDragHint,
4446
+ showHotspotModeControl,
4447
+ showZoomControls,
4448
+ showResetControl,
4449
+ labels,
4450
+ isHotspotMode,
4451
+ zoom,
4452
+ minZoom,
4453
+ maxZoom,
4454
+ isResetDisabled: isResetDisabled2,
4455
+ onHotspotModeChange,
4456
+ onZoomIn,
4457
+ onZoomOut,
4458
+ onResetView
4459
+ }) {
4460
+ return /* @__PURE__ */ jsxs3(CardFooter, { className: cn(viewer360ClassNames.toolbar, "gap-2 border-t px-4 py-3 pt-3"), children: [
4461
+ showDragHint && /* @__PURE__ */ jsx15(Label, { className: cn(viewer360ClassNames.dragHint, "font-normal text-muted-foreground"), children: labels.dragHint }),
4462
+ /* @__PURE__ */ jsxs3("div", { className: viewer360ClassNames.controls, children: [
4463
+ showHotspotModeControl && /* @__PURE__ */ jsxs3(Fragment2, { children: [
4464
+ /* @__PURE__ */ jsxs3(Button, { variant: isHotspotMode ? "default" : "outline", size: "sm", onClick: () => onHotspotModeChange(!isHotspotMode), children: [
4465
+ /* @__PURE__ */ jsx15(Crosshair, { className: "me-1.5 size-4" }),
4466
+ labels.addHotspot
4467
+ ] }),
4468
+ /* @__PURE__ */ jsx15(Separator2, { orientation: "vertical", className: cn(viewer360ClassNames.divider, "h-6") })
4469
+ ] }),
4470
+ showZoomControls && /* @__PURE__ */ jsxs3(Fragment2, { children: [
4471
+ /* @__PURE__ */ jsx15(Button, { variant: "outline", size: "icon-sm", disabled: zoom <= minZoom, "aria-label": labels.zoomOut, onClick: onZoomOut, children: /* @__PURE__ */ jsx15(Minus, { className: "size-4" }) }),
4472
+ /* @__PURE__ */ jsxs3(Badge, { variant: "outline", className: cn(viewer360ClassNames.zoomDisplay, "h-8 gap-1 px-2 py-1"), children: [
4473
+ /* @__PURE__ */ jsx15(ZoomIn, { className: "size-3 text-muted-foreground" }),
4474
+ labels.zoom(Math.round(zoom * 100))
4475
+ ] }),
4476
+ /* @__PURE__ */ jsx15(Button, { variant: "outline", size: "icon-sm", disabled: zoom >= maxZoom, "aria-label": labels.zoomIn, onClick: onZoomIn, children: /* @__PURE__ */ jsx15(Plus, { className: "size-4" }) })
4477
+ ] }),
4478
+ showResetControl && /* @__PURE__ */ jsx15(Button, { variant: "outline", size: "icon-sm", disabled: isResetDisabled2, "aria-label": labels.resetView, onClick: onResetView, children: /* @__PURE__ */ jsx15(RotateCcw, { className: "size-4" }) })
4479
+ ] })
4480
+ ] });
4481
+ }
4482
+
4483
+ // src/feature/Viewer360.tsx
4484
+ import { jsx as jsx16, jsxs as jsxs4 } from "react/jsx-runtime";
4485
+ function Viewer360({
4486
+ frames,
4487
+ currentFrameIndex: controlledFrameIndex,
4488
+ defaultFrameIndex = 0,
4489
+ onFrameChange,
4490
+ config,
4491
+ className,
4492
+ classNames,
4493
+ style,
4494
+ theme,
4495
+ labels,
4496
+ aspectRatio = "16 / 10",
4497
+ showZoomControls = true,
4498
+ showResetControl = true,
4499
+ showFrameIndicator = true,
4500
+ showDragHint = true,
4501
+ showHotspotModeControl = false,
4502
+ hotspotPin,
4503
+ hotspots = [],
4504
+ renderHotspot,
4505
+ renderLoading,
4506
+ renderFrameIndicator,
4507
+ renderHotspotModeBanner,
4508
+ renderToolbar,
4509
+ onHotspotClick,
4510
+ hotspotMode: controlledHotspotMode,
4511
+ defaultHotspotMode = false,
4512
+ onHotspotModeChange,
4513
+ onHotspotAdd,
4514
+ children
4515
+ }) {
4516
+ const mergedLabels = useMemo2(() => mergeViewer360Labels(labels), [labels]);
4517
+ const mergedClassNames = useMemo2(() => mergeViewer360ClassNames(classNames), [classNames]);
4518
+ const themeStyle = useMemo2(() => buildViewer360ThemeStyle(theme), [theme]);
4519
+ const [internalFrameIndex, setInternalFrameIndex] = useState2(defaultFrameIndex);
4520
+ const [internalHotspotMode, setInternalHotspotMode] = useState2(defaultHotspotMode);
4521
+ const currentFrameIndex = controlledFrameIndex ?? internalFrameIndex;
4522
+ const hotspotMode = controlledHotspotMode ?? internalHotspotMode;
4523
+ function handleFrameChange(index) {
4524
+ if (controlledFrameIndex === void 0) {
4525
+ setInternalFrameIndex(index);
4526
+ }
4527
+ onFrameChange?.(index);
4528
+ }
4529
+ function handleHotspotModeChange(active) {
4530
+ if (controlledHotspotMode === void 0) {
4531
+ setInternalHotspotMode(active);
4532
+ }
4533
+ onHotspotModeChange?.(active);
4534
+ }
4535
+ const {
4536
+ canvasRef,
4537
+ containerRef,
4538
+ currentFrame,
4539
+ currentFrameHotspots,
4540
+ imagesLoaded,
4541
+ isHotspotMode,
4542
+ isResetDisabled: isResetDisabled2,
4543
+ maxZoom,
4544
+ minZoom,
4545
+ viewerCursorClass,
4546
+ zoom,
4547
+ getHotspotScreenPosition,
4548
+ handleCanvasClick,
4549
+ handlePointerDown,
4550
+ handlePointerMove,
4551
+ handlePointerUp,
4552
+ handleWheel,
4553
+ handleResetView,
4554
+ handleZoomIn,
4555
+ handleZoomOut
4556
+ } = useViewer360({
4557
+ frames,
4558
+ hotspots,
4559
+ currentFrameIndex,
4560
+ onFrameChange: handleFrameChange,
4561
+ config,
4562
+ hotspotMode,
4563
+ onHotspotAdd
4564
+ });
4565
+ useEffect2(() => {
4566
+ if (controlledHotspotMode === void 0) return;
4567
+ if (controlledHotspotMode !== internalHotspotMode) {
4568
+ setInternalHotspotMode(controlledHotspotMode);
4569
+ }
4570
+ }, [controlledHotspotMode, internalHotspotMode]);
4571
+ const frameLabel = currentFrame?.label ?? frames[currentFrameIndex]?.label;
4572
+ const overlayProps = {
4573
+ currentFrameIndex,
4574
+ frameCount: frames.length,
4575
+ frameLabel,
4576
+ isHotspotMode,
4577
+ labels: mergedLabels,
4578
+ frameIndicatorClassName: mergedClassNames.frameIndicator
4579
+ };
4580
+ const toolbarProps = {
4581
+ zoom,
4582
+ minZoom,
4583
+ maxZoom,
4584
+ isResetDisabled: isResetDisabled2,
4585
+ isHotspotMode,
4586
+ showHotspotModeControl,
4587
+ showZoomControls,
4588
+ showResetControl,
4589
+ showDragHint,
4590
+ labels: mergedLabels,
4591
+ onZoomIn: handleZoomIn,
4592
+ onZoomOut: handleZoomOut,
4593
+ onResetView: handleResetView,
4594
+ onHotspotModeChange: handleHotspotModeChange
4595
+ };
4596
+ const showDefaultToolbar = showZoomControls || showResetControl || showHotspotModeControl || showDragHint;
4597
+ return /* @__PURE__ */ jsxs4(
4598
+ Card,
4599
+ {
4600
+ "data-viewer-360": "",
4601
+ className: cn(mergedClassNames.root, "gap-0 py-0 shadow-none ring-0", className),
4602
+ style: { ...themeStyle, ...style },
4603
+ children: [
4604
+ /* @__PURE__ */ jsxs4(
4605
+ "div",
4606
+ {
4607
+ ref: containerRef,
4608
+ className: cn(mergedClassNames.viewport, viewerCursorClass),
4609
+ style: { aspectRatio },
4610
+ onPointerDown: handlePointerDown,
4611
+ onPointerMove: handlePointerMove,
4612
+ onPointerUp: handlePointerUp,
4613
+ onPointerLeave: handlePointerUp,
4614
+ onWheel: handleWheel,
4615
+ onClick: handleCanvasClick,
4616
+ children: [
4617
+ /* @__PURE__ */ jsx16("canvas", { ref: canvasRef, className: mergedClassNames.canvas }),
4618
+ /* @__PURE__ */ jsxs4("div", { className: mergedClassNames.overlay, children: [
4619
+ currentFrameHotspots.map((hotspot) => {
4620
+ const position = getHotspotScreenPosition(hotspot);
4621
+ return /* @__PURE__ */ jsx16(
4622
+ Viewer360HotspotOverlay,
4623
+ {
4624
+ hotspot,
4625
+ leftPercent: position.leftPercent,
4626
+ topPercent: position.topPercent,
4627
+ hotspotPin,
4628
+ renderHotspot,
4629
+ onHotspotClick
4630
+ },
4631
+ hotspot.id
4632
+ );
4633
+ }),
4634
+ children
4635
+ ] }),
4636
+ !imagesLoaded && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx16(
4637
+ Viewer360LoadingOverlay,
4638
+ {
4639
+ className: mergedClassNames.loading,
4640
+ textClassName: mergedClassNames.loadingText,
4641
+ label: mergedLabels.loading
4642
+ }
4643
+ )),
4644
+ showFrameIndicator && frames.length > 0 && (renderFrameIndicator ? renderFrameIndicator(overlayProps) : /* @__PURE__ */ jsx16(
4645
+ Viewer360FrameIndicator,
4646
+ {
4647
+ className: mergedClassNames.frameIndicator,
4648
+ label: mergedLabels.frameIndicator({
4649
+ current: currentFrameIndex + 1,
4650
+ total: frames.length,
4651
+ label: frameLabel
4652
+ })
4653
+ }
4654
+ )),
4655
+ isHotspotMode && onHotspotAdd && (renderHotspotModeBanner ? renderHotspotModeBanner({ labels: mergedLabels }) : /* @__PURE__ */ jsx16(Viewer360AddModeBanner, { className: mergedClassNames.hotspotModeBanner, label: mergedLabels.hotspotModeActive }))
4656
+ ]
4657
+ }
4658
+ ),
4659
+ renderToolbar ? renderToolbar(toolbarProps) : showDefaultToolbar ? /* @__PURE__ */ jsx16(Viewer360Toolbar, { ...toolbarProps }) : null
4660
+ ]
4661
+ }
4662
+ );
4663
+ }
4664
+
4665
+ // src/constants/viewer360Config.ts
4666
+ var viewer360Config = {
4667
+ minZoom: 1,
4668
+ maxZoom: 3,
4669
+ zoomStep: 0.15,
4670
+ dragSensitivity: 8,
4671
+ autoRotate: false,
4672
+ autoRotateIntervalMs: 100,
4673
+ autoRotateDirection: "forward"
4674
+ };
4675
+ var defaultViewer360Config = viewer360Config;
4676
+ export {
4677
+ Button,
4678
+ Viewer360,
4679
+ Viewer360AddModeBanner,
4680
+ Viewer360FrameIndicator,
4681
+ Viewer360LoadingOverlay,
4682
+ Viewer360MarkerPin,
4683
+ Viewer360Toolbar,
4684
+ applyWheelZoom,
4685
+ buttonVariants,
4686
+ clampFrameIndex,
4687
+ clampZoom,
4688
+ cn,
4689
+ computeDragFrameIndex,
4690
+ computeHotspotPositionFromClick,
4691
+ computeHotspotScreenPosition,
4692
+ computeViewerImageLayout,
4693
+ computeViewerPanOffset,
4694
+ defaultViewer360ClassNames,
4695
+ defaultViewer360Config,
4696
+ defaultViewer360Labels,
4697
+ defaultViewer360MarkerPinClassNames,
4698
+ defaultViewer360MarkerPinLabels,
4699
+ drawFrameOnCanvas,
4700
+ filterHotspotsByFrame,
4701
+ getFramesSignature,
4702
+ getViewerCursorClass,
4703
+ hasLoadedViewerFrame,
4704
+ hotspotToViewer360Marker,
4705
+ isResetDisabled,
4706
+ preloadFrameImage,
4707
+ preloadViewerFrames,
4708
+ resolveViewer360Config,
4709
+ stepZoomIn,
4710
+ stepZoomOut,
4711
+ toViewer360Hotspots,
4712
+ useViewer360,
4713
+ viewer360ClassNames,
4714
+ viewer360MarkerPinClassNames
4715
+ };
4716
+ //# sourceMappingURL=index.js.map