@endfield-ui/react 0.1.0 → 0.3.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/LICENSE +21 -21
- package/README.md +55 -5
- package/dist/index.d.ts +213 -4
- package/dist/index.js +688 -109
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/README.md +8 -1
- package/src/components/avatar/Avatar.tsx +3 -0
- package/src/components/carousel/Carousel.tsx +7 -47
- package/src/components/carousel/snap-track.ts +74 -0
- package/src/components/checkbox/choice-style.ts +5 -2
- package/src/components/corner-brackets/CornerBrackets.tsx +2 -1
- package/src/components/dialog/Dialog.tsx +4 -2
- package/src/components/dropdown-menu/menu-style.ts +9 -2
- package/src/components/file-upload/FileUpload.tsx +1 -1
- package/src/components/hover-card/HoverCard.tsx +101 -0
- package/src/components/hover-card/index.ts +6 -0
- package/src/components/image-viewer/ImageViewer.tsx +467 -0
- package/src/components/image-viewer/index.ts +7 -0
- package/src/components/item-slot/ItemSlot.tsx +3 -1
- package/src/components/nav-menu/NavMenu.tsx +14 -1
- package/src/components/otp-input/OtpInput.tsx +214 -0
- package/src/components/otp-input/index.ts +7 -0
- package/src/components/page-header/PageHeader.tsx +182 -0
- package/src/components/page-header/index.ts +6 -0
- package/src/components/popover/Popover.tsx +4 -12
- package/src/components/popover/panel-style.ts +15 -0
- package/src/components/scroll-area/ScrollArea.tsx +137 -0
- package/src/components/scroll-area/index.ts +5 -0
- package/src/components/slider/Slider.tsx +6 -2
- package/src/components/steps/Steps.tsx +12 -3
- package/src/components/table/Table.tsx +1 -0
- package/src/components/tag-input/TagInput.tsx +1 -0
- package/src/components/toast/Toast.tsx +18 -5
- package/src/components/toc/Toc.tsx +1 -0
- package/src/components/tooltip/Tooltip.tsx +5 -1
- package/src/components/viewfinder/Viewfinder.tsx +9 -0
- package/src/index.ts +5 -0
- package/src/lib/README.md +1 -0
- package/src/lib/cn.ts +2 -0
- package/src/lib/motion.ts +15 -0
- package/src/styles/theme.css +43 -0
- package/src/styles/utilities.css +2 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
|
13
13
|
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
|
|
14
14
|
import { Drawer as Drawer$1 } from "@base-ui/react/drawer";
|
|
15
15
|
import { Menu as Menu$1 } from "@base-ui/react/menu";
|
|
16
|
+
import { PreviewCard } from "@base-ui/react/preview-card";
|
|
17
|
+
import { OTPField } from "@base-ui/react/otp-field";
|
|
18
|
+
import { ScrollArea as ScrollArea$1 } from "@base-ui/react/scroll-area";
|
|
16
19
|
import { Select as Select$1 } from "@base-ui/react/select";
|
|
17
20
|
import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
|
|
18
21
|
import { Collapsible } from "@base-ui/react/collapsible";
|
|
@@ -73,7 +76,9 @@ const twMerge = extendTailwindMerge({ extend: { theme: {
|
|
|
73
76
|
"slide-in",
|
|
74
77
|
"turn-in",
|
|
75
78
|
"fade-in",
|
|
76
|
-
"indeterminate"
|
|
79
|
+
"indeterminate",
|
|
80
|
+
"bracket-in",
|
|
81
|
+
"shift-in"
|
|
77
82
|
]
|
|
78
83
|
} } });
|
|
79
84
|
function cn(...inputs) {
|
|
@@ -959,7 +964,7 @@ const alignClass$1 = {
|
|
|
959
964
|
end: "justify-end",
|
|
960
965
|
between: "justify-between"
|
|
961
966
|
};
|
|
962
|
-
const gapClass = {
|
|
967
|
+
const gapClass$1 = {
|
|
963
968
|
sm: "gap-2",
|
|
964
969
|
md: "gap-3"
|
|
965
970
|
};
|
|
@@ -973,7 +978,7 @@ function ButtonGroup({ align = "end", orientation = "horizontal", gap = "md", cl
|
|
|
973
978
|
role: "group",
|
|
974
979
|
...props,
|
|
975
980
|
"data-orientation": orientation,
|
|
976
|
-
className: cn("flex", gapClass[gap], orientation === "vertical" ? "flex-col items-stretch" : ["flex-wrap items-center", alignClass$1[align]], className)
|
|
981
|
+
className: cn("flex", gapClass$1[gap], orientation === "vertical" ? "flex-col items-stretch" : ["flex-wrap items-center", alignClass$1[align]], className)
|
|
977
982
|
});
|
|
978
983
|
}
|
|
979
984
|
//#endregion
|
|
@@ -1309,48 +1314,32 @@ function DashIndicator({ count, index, className, ...props }) {
|
|
|
1309
1314
|
});
|
|
1310
1315
|
}
|
|
1311
1316
|
//#endregion
|
|
1312
|
-
//#region src/components/carousel/
|
|
1313
|
-
const ratioClass$2 = {
|
|
1314
|
-
"16/9": "aspect-video",
|
|
1315
|
-
"4/3": "aspect-4/3",
|
|
1316
|
-
"1/1": "aspect-square",
|
|
1317
|
-
"3/4": "aspect-3/4",
|
|
1318
|
-
"21/9": "aspect-21/9"
|
|
1319
|
-
};
|
|
1320
|
-
const SlideContext = createContext(null);
|
|
1317
|
+
//#region src/components/carousel/snap-track.ts
|
|
1321
1318
|
/** 滚动停下之后多久算"停稳了"。没有 scrollend 事件的浏览器靠它 */
|
|
1322
1319
|
const SETTLE_MS$1 = 120;
|
|
1323
1320
|
/**
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1321
|
+
* 一条横向滚动加滚动吸附的轨道,每一张和轨道一样宽。下标和滚动位置互相跟:
|
|
1322
|
+
* 下标变了轨道滚过去;用户自己滑的时候反过来,等滚动停稳,由停下的位置定下标。
|
|
1323
|
+
* 手势、惯性都是浏览器的。媒体轮播和图片查看共用,不对外导出。
|
|
1324
|
+
*
|
|
1325
|
+
* 把 `trackRef` 和 `onScroll` 交给那个滚动容器。
|
|
1326
1326
|
*/
|
|
1327
|
-
function
|
|
1328
|
-
const slides = Children.toArray(children).filter((child) => isValidElement(child));
|
|
1329
|
-
const count = slides.length;
|
|
1330
|
-
const last = Math.max(0, count - 1);
|
|
1331
|
-
const [rawIndex, setIndex] = useControllableState({
|
|
1332
|
-
value: indexProp,
|
|
1333
|
-
defaultValue: defaultIndex,
|
|
1334
|
-
onChange: onIndexChange
|
|
1335
|
-
});
|
|
1336
|
-
const index = Math.min(Math.max(rawIndex, 0), last);
|
|
1327
|
+
function useSnapTrack({ index, count, onSettle }) {
|
|
1337
1328
|
const trackRef = useRef(null);
|
|
1338
1329
|
const settleTimer = useRef(void 0);
|
|
1330
|
+
const last = Math.max(0, count - 1);
|
|
1339
1331
|
const indexRef = useRef(index);
|
|
1340
1332
|
indexRef.current = index;
|
|
1341
|
-
const
|
|
1342
|
-
if (count === 0) return;
|
|
1343
|
-
const target = loop ? (next + count) % count : Math.min(Math.max(next, 0), last);
|
|
1344
|
-
setIndex(target);
|
|
1345
|
-
if (!loop && (target === 0 || target === last) && document.activeElement instanceof HTMLButtonElement && trackRef.current?.parentElement?.contains(document.activeElement)) trackRef.current.focus({ preventScroll: true });
|
|
1346
|
-
};
|
|
1333
|
+
const placed = useRef(false);
|
|
1347
1334
|
useEffect(() => {
|
|
1335
|
+
const first = !placed.current;
|
|
1336
|
+
placed.current = true;
|
|
1348
1337
|
const track = trackRef.current;
|
|
1349
1338
|
if (!track || track.clientWidth === 0) return;
|
|
1350
1339
|
if (Math.round(track.scrollLeft / track.clientWidth) === index) return;
|
|
1351
1340
|
track.scrollTo?.({
|
|
1352
1341
|
left: index * track.clientWidth,
|
|
1353
|
-
behavior: "smooth"
|
|
1342
|
+
behavior: first ? "instant" : "smooth"
|
|
1354
1343
|
});
|
|
1355
1344
|
}, [index]);
|
|
1356
1345
|
useEffect(() => {
|
|
@@ -1366,15 +1355,54 @@ function Carousel({ index: indexProp, defaultIndex = 0, onIndexChange, loop = fa
|
|
|
1366
1355
|
return () => observer.disconnect();
|
|
1367
1356
|
}, []);
|
|
1368
1357
|
useEffect(() => () => clearTimeout(settleTimer.current), []);
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1358
|
+
return {
|
|
1359
|
+
trackRef,
|
|
1360
|
+
onScroll: useCallback(() => {
|
|
1361
|
+
clearTimeout(settleTimer.current);
|
|
1362
|
+
settleTimer.current = setTimeout(() => {
|
|
1363
|
+
const track = trackRef.current;
|
|
1364
|
+
if (!track || track.clientWidth === 0) return;
|
|
1365
|
+
const stopped = Math.round(track.scrollLeft / track.clientWidth);
|
|
1366
|
+
onSettle(Math.min(Math.max(stopped, 0), last));
|
|
1367
|
+
}, SETTLE_MS$1);
|
|
1368
|
+
}, [onSettle, last])
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
//#endregion
|
|
1372
|
+
//#region src/components/carousel/Carousel.tsx
|
|
1373
|
+
const ratioClass$3 = {
|
|
1374
|
+
"16/9": "aspect-video",
|
|
1375
|
+
"4/3": "aspect-4/3",
|
|
1376
|
+
"1/1": "aspect-square",
|
|
1377
|
+
"3/4": "aspect-3/4",
|
|
1378
|
+
"21/9": "aspect-21/9"
|
|
1379
|
+
};
|
|
1380
|
+
const SlideContext = createContext(null);
|
|
1381
|
+
/**
|
|
1382
|
+
* 媒体轮播:一次看一张的大幅媒体,前后翻。不自动播放。
|
|
1383
|
+
* 轨道是原生的横向滚动加滚动吸附,触屏滑动是浏览器自带的。
|
|
1384
|
+
*/
|
|
1385
|
+
function Carousel({ index: indexProp, defaultIndex = 0, onIndexChange, loop = false, ratio = "16/9", indicator = false, previousLabel = "上一张", nextLabel = "下一张", className, children, ...props }) {
|
|
1386
|
+
const slides = Children.toArray(children).filter((child) => isValidElement(child));
|
|
1387
|
+
const count = slides.length;
|
|
1388
|
+
const last = Math.max(0, count - 1);
|
|
1389
|
+
const [rawIndex, setIndex] = useControllableState({
|
|
1390
|
+
value: indexProp,
|
|
1391
|
+
defaultValue: defaultIndex,
|
|
1392
|
+
onChange: onIndexChange
|
|
1393
|
+
});
|
|
1394
|
+
const index = Math.min(Math.max(rawIndex, 0), last);
|
|
1395
|
+
const { trackRef, onScroll } = useSnapTrack({
|
|
1396
|
+
index,
|
|
1397
|
+
count,
|
|
1398
|
+
onSettle: setIndex
|
|
1399
|
+
});
|
|
1400
|
+
const go = (next) => {
|
|
1401
|
+
if (count === 0) return;
|
|
1402
|
+
const target = loop ? (next + count) % count : Math.min(Math.max(next, 0), last);
|
|
1403
|
+
setIndex(target);
|
|
1404
|
+
if (!loop && (target === 0 || target === last) && document.activeElement instanceof HTMLButtonElement && trackRef.current?.parentElement?.contains(document.activeElement)) trackRef.current.focus({ preventScroll: true });
|
|
1405
|
+
};
|
|
1378
1406
|
const onKeyDown = (event) => {
|
|
1379
1407
|
const next = {
|
|
1380
1408
|
ArrowLeft: index - 1,
|
|
@@ -1403,7 +1431,7 @@ function Carousel({ index: indexProp, defaultIndex = 0, onIndexChange, loop = fa
|
|
|
1403
1431
|
onKeyDown: (event) => {
|
|
1404
1432
|
if (event.target === event.currentTarget) onKeyDown(event);
|
|
1405
1433
|
},
|
|
1406
|
-
className: cn("flex snap-x snap-mandatory overflow-x-auto overscroll-x-contain bg-surface-muted [scrollbar-width:none]", focusRing, ratioClass$
|
|
1434
|
+
className: cn("flex snap-x snap-mandatory overflow-x-auto overscroll-x-contain bg-surface-muted [scrollbar-width:none]", focusRing, ratioClass$3[ratio]),
|
|
1407
1435
|
children: slides.map((slide, position) => /* @__PURE__ */ jsx(SlideContext, {
|
|
1408
1436
|
value: {
|
|
1409
1437
|
position,
|
|
@@ -1498,8 +1526,11 @@ const choiceControl = "flex h-[1lh] shrink-0 items-center";
|
|
|
1498
1526
|
const choiceBox = "peer size-5 shrink-0 appearance-none border-2 choice-diamond:size-3.5 choice-diamond:rotate-45 choice-diamond:checked:disabled:bg-ink-disabled border-line-strong bg-surface-sunken transition-colors duration-(--duration-fast) ease-standard group-hover:border-ink-secondary checked:border-surface-inverse checked:bg-surface-inverse aria-invalid:border-danger disabled:cursor-not-allowed disabled:border-ink-disabled disabled:bg-surface-sunken";
|
|
1499
1527
|
/** 方格 / 圆的定位盒:菱形比它小一圈,所以居中 */
|
|
1500
1528
|
const choiceFrame = "relative inline-flex size-5 items-center justify-center";
|
|
1501
|
-
/**
|
|
1502
|
-
|
|
1529
|
+
/**
|
|
1530
|
+
* 压在方格 / 圆正中的记号,默认透明,由 peer 的状态点亮。
|
|
1531
|
+
* 淡入淡出,和方格换底色的那一下同时走——底色有过渡而记号硬切,看起来是两件事
|
|
1532
|
+
*/
|
|
1533
|
+
const choiceMark = "pointer-events-none absolute inset-0 m-auto text-accent-ink-inverse opacity-0 transition-opacity duration-(--duration-fast) ease-standard peer-disabled:text-ink-disabled";
|
|
1503
1534
|
//#endregion
|
|
1504
1535
|
//#region src/components/checkbox/Checkbox.tsx
|
|
1505
1536
|
/**
|
|
@@ -1691,14 +1722,37 @@ function MenuCheck({ checked, disabled = false }) {
|
|
|
1691
1722
|
});
|
|
1692
1723
|
}
|
|
1693
1724
|
//#endregion
|
|
1725
|
+
//#region src/lib/motion.ts
|
|
1726
|
+
/**
|
|
1727
|
+
* 浮层从触发它的那一侧来:进场的起点往触发处偏 `--motion-shift`,走完归位。
|
|
1728
|
+
* 用在基元的浮层面板上——它们带着 `data-side`(面板在触发元素的哪一侧)。
|
|
1729
|
+
*
|
|
1730
|
+
* 位移只写在 `data-starting-style` 上:退场不走位移,只淡出。
|
|
1731
|
+
* 用的地方自己给 `translate` 加过渡。`inline-start` / `inline-end` 按从左往右的排版算。
|
|
1732
|
+
*/
|
|
1733
|
+
const enterFromSide = [
|
|
1734
|
+
"data-starting-style:data-[side=bottom]:-translate-y-(--motion-shift)",
|
|
1735
|
+
"data-starting-style:data-[side=top]:translate-y-(--motion-shift)",
|
|
1736
|
+
"data-starting-style:data-[side=right]:-translate-x-(--motion-shift)",
|
|
1737
|
+
"data-starting-style:data-[side=left]:translate-x-(--motion-shift)",
|
|
1738
|
+
"data-starting-style:data-[side=inline-end]:-translate-x-(--motion-shift)",
|
|
1739
|
+
"data-starting-style:data-[side=inline-start]:translate-x-(--motion-shift)"
|
|
1740
|
+
].join(" ");
|
|
1741
|
+
//#endregion
|
|
1694
1742
|
//#region src/components/dropdown-menu/menu-style.ts
|
|
1695
1743
|
/** 选项要知道自己在哪种面板里,才知道"当前项"怎么画 */
|
|
1696
1744
|
const MenuVariantContext = createContext("plain");
|
|
1697
|
-
/**
|
|
1745
|
+
/**
|
|
1746
|
+
* 面板:直角、1px 线、环境阴影;顶部一条半宽的行动色细条,展开时从 0 伸到一半宽。
|
|
1747
|
+
*
|
|
1748
|
+
* 进场:淡入(200ms),同时从触发处那一侧挪过来 4px(300ms,和细条同一拍)。
|
|
1749
|
+
* 退场只淡出——位移只写在起点上。两个时长、两条缓动按属性一一对应
|
|
1750
|
+
*/
|
|
1698
1751
|
const menuPanel = [
|
|
1699
1752
|
"relative min-w-40 border border-line bg-surface-raised py-1 text-ink shadow-sm outline-none",
|
|
1700
|
-
"origin-(--transform-origin) transition-opacity duration-(--duration-fast) ease-standard",
|
|
1753
|
+
"origin-(--transform-origin) transition-[opacity,translate] duration-[var(--duration-fast),var(--duration-base)] ease-[var(--ease-standard),var(--ease-exit)]",
|
|
1701
1754
|
"data-ending-style:opacity-0 data-starting-style:opacity-0",
|
|
1755
|
+
enterFromSide,
|
|
1702
1756
|
"before:pointer-events-none before:absolute before:-top-px before:-left-px before:h-[3px] before:w-1/2 before:origin-left before:bg-action before:content-['']",
|
|
1703
1757
|
"before:transition-transform before:duration-(--duration-base) before:ease-exit data-starting-style:before:scale-x-0",
|
|
1704
1758
|
"forced-colors:before:hidden"
|
|
@@ -2179,13 +2233,14 @@ const sizeClass$7 = {
|
|
|
2179
2233
|
* 用于矩阵里的单选(物品格、名册卡、图鉴);列表行用左缘色条,页签用底色,都不用它。
|
|
2180
2234
|
*
|
|
2181
2235
|
* 括号画在盒子之外 4px:它所在的容器不能贴着盒子裁切。
|
|
2236
|
+
* 出现时八段短线从角的顶点伸到满长(200ms),消失是直接消失。
|
|
2182
2237
|
* 括号只是给眼睛看的,选中态另外要有 `aria-selected` / `aria-pressed`。
|
|
2183
2238
|
*/
|
|
2184
2239
|
function CornerBrackets({ visible = true, size = "sm", className, ...props }) {
|
|
2185
2240
|
return /* @__PURE__ */ jsx("div", {
|
|
2186
2241
|
...props,
|
|
2187
2242
|
"data-visible": visible ? "" : void 0,
|
|
2188
|
-
className: cn("relative", visible && "corner-brackets", sizeClass$7[size], className)
|
|
2243
|
+
className: cn("relative", visible && "corner-brackets after:animate-bracket-in", sizeClass$7[size], className)
|
|
2189
2244
|
});
|
|
2190
2245
|
}
|
|
2191
2246
|
//#endregion
|
|
@@ -2842,7 +2897,7 @@ function Dialog({ trigger, open, defaultOpen, onOpenChange, title, description,
|
|
|
2842
2897
|
initialFocus: initialFocus ?? firstFocusableIn(contentRef),
|
|
2843
2898
|
finalFocus,
|
|
2844
2899
|
"data-size": size,
|
|
2845
|
-
className: cn(overlaySurface, "relative flex max-h-full min-h-0 w-full flex-col", "transition-[
|
|
2900
|
+
className: cn(overlaySurface, "relative flex max-h-full min-h-0 w-full flex-col", "transition-[opacity,translate] duration-[var(--duration-fast),var(--duration-base)] ease-[var(--ease-standard),var(--ease-exit)]", "data-ending-style:opacity-0 data-starting-style:translate-y-(--motion-shift-lg) data-starting-style:opacity-0", sizeClass$5[size], className),
|
|
2846
2901
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
2847
2902
|
"data-theme": "dark",
|
|
2848
2903
|
className: cn(overlayHeader, "min-h-16 justify-center px-12"),
|
|
@@ -3594,7 +3649,7 @@ function FileUpload({ value, defaultValue, onValueChange, multiple = false, acce
|
|
|
3594
3649
|
dragging && /* @__PURE__ */ jsx("span", {
|
|
3595
3650
|
"aria-hidden": "true",
|
|
3596
3651
|
"data-brackets": "",
|
|
3597
|
-
className: cn("pointer-events-none select-none forced-colors:hidden print:hidden", "corner-brackets absolute inset-1.5 [--bracket-offset:0px]")
|
|
3652
|
+
className: cn("pointer-events-none select-none forced-colors:hidden print:hidden", "corner-brackets absolute inset-1.5 [--bracket-offset:0px] after:animate-bracket-in")
|
|
3598
3653
|
}),
|
|
3599
3654
|
/* @__PURE__ */ jsx("input", {
|
|
3600
3655
|
ref: mergeRefs(inputRef, ref),
|
|
@@ -3753,6 +3808,346 @@ function HazardStripe({ size = "md", className, ...props }) {
|
|
|
3753
3808
|
});
|
|
3754
3809
|
}
|
|
3755
3810
|
//#endregion
|
|
3811
|
+
//#region src/components/popover/panel-style.ts
|
|
3812
|
+
/**
|
|
3813
|
+
* 气泡卡片与悬浮卡共用的面板:下拉菜单的那一块,只把上下 4px 的内边距换成四周 16px。
|
|
3814
|
+
* 宽度跟着内容走,最宽 320px,也不超过视口里剩下的地方。
|
|
3815
|
+
* 要经过 `cn()`:后面的 `p-4` 要盖掉菜单面板自带的上下内边距
|
|
3816
|
+
*/
|
|
3817
|
+
const popoverPanel = [menuPanel, "flex w-max max-w-[min(20rem,var(--available-width))] flex-col gap-3 p-4 text-sm wrap-anywhere"];
|
|
3818
|
+
//#endregion
|
|
3819
|
+
//#region src/components/hover-card/HoverCard.tsx
|
|
3820
|
+
/**
|
|
3821
|
+
* 悬浮卡:指针停在一个链接上时,在旁边预览它的去处。面板和气泡卡片是同一块。
|
|
3822
|
+
*
|
|
3823
|
+
* 触屏上不出现,读屏也不会读到它:里面只放"点进去也看得到"的内容,
|
|
3824
|
+
* 不放只有这里才有的信息和操作。要操作的用 `Popover`,一句说明用 `Tooltip`。
|
|
3825
|
+
*/
|
|
3826
|
+
function HoverCard({ trigger, title, description, side = "bottom", align = "start", delay = 600, closeDelay = 300, open, defaultOpen, onOpenChange, className, children, ...props }) {
|
|
3827
|
+
const { anchorRef, portalRef } = usePortalScope();
|
|
3828
|
+
return /* @__PURE__ */ jsxs(PreviewCard.Root, {
|
|
3829
|
+
open,
|
|
3830
|
+
defaultOpen,
|
|
3831
|
+
onOpenChange: onOpenChange && ((next) => onOpenChange(next)),
|
|
3832
|
+
children: [/* @__PURE__ */ jsx(PreviewCard.Trigger, {
|
|
3833
|
+
ref: anchorRef,
|
|
3834
|
+
render: trigger,
|
|
3835
|
+
delay,
|
|
3836
|
+
closeDelay
|
|
3837
|
+
}), /* @__PURE__ */ jsx(PreviewCard.Portal, {
|
|
3838
|
+
ref: portalRef,
|
|
3839
|
+
children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
|
|
3840
|
+
side,
|
|
3841
|
+
align,
|
|
3842
|
+
sideOffset: 4,
|
|
3843
|
+
className: "z-(--z-overlay) outline-none",
|
|
3844
|
+
children: /* @__PURE__ */ jsxs(PreviewCard.Popup, {
|
|
3845
|
+
...props,
|
|
3846
|
+
"data-hover-card": "",
|
|
3847
|
+
className: cn(popoverPanel, className),
|
|
3848
|
+
children: [(title || description) && /* @__PURE__ */ jsxs("div", {
|
|
3849
|
+
className: "flex flex-col gap-1",
|
|
3850
|
+
children: [title && /* @__PURE__ */ jsx("p", {
|
|
3851
|
+
className: "text-base font-bold",
|
|
3852
|
+
children: title
|
|
3853
|
+
}), description && /* @__PURE__ */ jsx("p", {
|
|
3854
|
+
className: "text-ink-secondary",
|
|
3855
|
+
children: description
|
|
3856
|
+
})]
|
|
3857
|
+
}), children]
|
|
3858
|
+
})
|
|
3859
|
+
})
|
|
3860
|
+
})]
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
//#endregion
|
|
3864
|
+
//#region src/components/viewfinder/Viewfinder.tsx
|
|
3865
|
+
const armClass = {
|
|
3866
|
+
sm: "[--bracket-arm:16px]",
|
|
3867
|
+
md: "[--bracket-arm:24px]"
|
|
3868
|
+
};
|
|
3869
|
+
const readoutPosition = {
|
|
3870
|
+
topLeft: "top-5 left-5",
|
|
3871
|
+
topRight: "top-5 right-5 text-right",
|
|
3872
|
+
bottomLeft: "bottom-5 left-5",
|
|
3873
|
+
bottomRight: "right-5 bottom-5 text-right"
|
|
3874
|
+
};
|
|
3875
|
+
/**
|
|
3876
|
+
* 取景角:框住一张图、一片地图区域、一个焦点,标的是"范围",不是选中。
|
|
3877
|
+
* 里面放被框住的内容;压在深色画面上时给它加 `data-theme="dark"`。
|
|
3878
|
+
*
|
|
3879
|
+
* 只用在展示性的画面上,一屏一处。取景角与准星是装饰,读数是真实信息、读屏读得到。
|
|
3880
|
+
*/
|
|
3881
|
+
function Viewfinder({ size = "sm", crosshair = false, animate = false, readouts, className, children, ...props }) {
|
|
3882
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3883
|
+
...props,
|
|
3884
|
+
className: cn("relative text-ink", className),
|
|
3885
|
+
children: [
|
|
3886
|
+
children,
|
|
3887
|
+
/* @__PURE__ */ jsx("span", {
|
|
3888
|
+
"aria-hidden": "true",
|
|
3889
|
+
className: cn(decor, "corner-brackets absolute inset-3 [--bracket-color:currentColor] [--bracket-offset:0px] [--bracket-width:1.5px]", armClass[size], animate && "after:animate-bracket-in after:[animation-delay:var(--bracket-delay,0s)]")
|
|
3890
|
+
}),
|
|
3891
|
+
crosshair && /* @__PURE__ */ jsx("svg", {
|
|
3892
|
+
"aria-hidden": "true",
|
|
3893
|
+
focusable: "false",
|
|
3894
|
+
viewBox: "0 0 12 12",
|
|
3895
|
+
fill: "none",
|
|
3896
|
+
stroke: "currentColor",
|
|
3897
|
+
strokeWidth: 1.5,
|
|
3898
|
+
className: cn("pointer-events-none select-none forced-colors:hidden print:hidden", "absolute top-1/2 left-1/2 size-3 -translate-1/2 text-accent-ink"),
|
|
3899
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 0v12M0 6h12" })
|
|
3900
|
+
}),
|
|
3901
|
+
readouts && Object.keys(readoutPosition).map((corner) => readouts[corner] !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
3902
|
+
"data-corner": corner,
|
|
3903
|
+
className: cn("pointer-events-none absolute font-tech text-xs leading-none text-ink-secondary tabular-nums", readoutPosition[corner]),
|
|
3904
|
+
children: readouts[corner]
|
|
3905
|
+
}, corner))
|
|
3906
|
+
]
|
|
3907
|
+
});
|
|
3908
|
+
}
|
|
3909
|
+
//#endregion
|
|
3910
|
+
//#region src/components/image-viewer/ImageViewer.tsx
|
|
3911
|
+
const ratioClass$2 = {
|
|
3912
|
+
"16/9": "aspect-video",
|
|
3913
|
+
"4/3": "aspect-4/3",
|
|
3914
|
+
"1/1": "aspect-square",
|
|
3915
|
+
"3/4": "aspect-3/4"
|
|
3916
|
+
};
|
|
3917
|
+
const ItemContext = createContext(null);
|
|
3918
|
+
/**
|
|
3919
|
+
* 图片查看:一组缩略图,点一张放大到整屏看,前后翻。
|
|
3920
|
+
* 大图那一层在亮暗两个主题下都是深色;翻页用的是媒体轮播的那条轨道,触屏可以左右滑。
|
|
3921
|
+
*
|
|
3922
|
+
* `className` 和其余属性给缩略图的列表。
|
|
3923
|
+
*/
|
|
3924
|
+
function ImageViewer({ "aria-label": label, open: openProp, onOpenChange, index: indexProp, defaultIndex = 0, onIndexChange, loop = false, ratio = "4/3", viewLabel = "查看大图", previousLabel = "上一张", nextLabel = "下一张", closeLabel = "关闭", className, children, ...props }) {
|
|
3925
|
+
const items = Children.toArray(children).filter((child) => isValidElement(child));
|
|
3926
|
+
const count = items.length;
|
|
3927
|
+
const last = Math.max(0, count - 1);
|
|
3928
|
+
const [open, setOpen] = useControllableState({
|
|
3929
|
+
value: openProp,
|
|
3930
|
+
defaultValue: false,
|
|
3931
|
+
onChange: onOpenChange
|
|
3932
|
+
});
|
|
3933
|
+
const [rawIndex, setIndex] = useControllableState({
|
|
3934
|
+
value: indexProp,
|
|
3935
|
+
defaultValue: defaultIndex,
|
|
3936
|
+
onChange: onIndexChange
|
|
3937
|
+
});
|
|
3938
|
+
const index = Math.min(Math.max(rawIndex, 0), last);
|
|
3939
|
+
const thumbs = useRef([]);
|
|
3940
|
+
const openAt = (position) => {
|
|
3941
|
+
setIndex(position);
|
|
3942
|
+
setOpen(true);
|
|
3943
|
+
};
|
|
3944
|
+
const register = (position, node) => {
|
|
3945
|
+
thumbs.current[position] = node;
|
|
3946
|
+
};
|
|
3947
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("ul", {
|
|
3948
|
+
...props,
|
|
3949
|
+
"aria-label": label,
|
|
3950
|
+
"data-ratio": ratio,
|
|
3951
|
+
className: cn("grid grid-cols-[repeat(auto-fill,minmax(7rem,1fr))] gap-2 text-ink", className),
|
|
3952
|
+
children: items.map((item, position) => /* @__PURE__ */ jsx(ItemContext, {
|
|
3953
|
+
value: {
|
|
3954
|
+
position,
|
|
3955
|
+
count,
|
|
3956
|
+
ratio,
|
|
3957
|
+
viewLabel,
|
|
3958
|
+
open: openAt,
|
|
3959
|
+
register
|
|
3960
|
+
},
|
|
3961
|
+
children: item
|
|
3962
|
+
}, position))
|
|
3963
|
+
}), /* @__PURE__ */ jsx(Dialog$1.Root, {
|
|
3964
|
+
open,
|
|
3965
|
+
onOpenChange: (next) => setOpen(next),
|
|
3966
|
+
children: /* @__PURE__ */ jsx(Dialog$1.Portal, { children: /* @__PURE__ */ jsx(Dialog$1.Viewport, {
|
|
3967
|
+
className: "fixed inset-0 z-(--z-overlay)",
|
|
3968
|
+
children: /* @__PURE__ */ jsx(Stage, {
|
|
3969
|
+
label,
|
|
3970
|
+
items,
|
|
3971
|
+
index,
|
|
3972
|
+
setIndex,
|
|
3973
|
+
loop,
|
|
3974
|
+
previousLabel,
|
|
3975
|
+
nextLabel,
|
|
3976
|
+
closeLabel,
|
|
3977
|
+
thumbs,
|
|
3978
|
+
onClose: () => setOpen(false)
|
|
3979
|
+
})
|
|
3980
|
+
}) })
|
|
3981
|
+
})] });
|
|
3982
|
+
}
|
|
3983
|
+
/** 大图层。只在打开的时候挂着:轨道一挂上就直接对到被点的那一张 */
|
|
3984
|
+
function Stage({ label, items, index, setIndex, loop, previousLabel, nextLabel, closeLabel, thumbs, onClose }) {
|
|
3985
|
+
const count = items.length;
|
|
3986
|
+
const last = Math.max(0, count - 1);
|
|
3987
|
+
const { trackRef, onScroll } = useSnapTrack({
|
|
3988
|
+
index,
|
|
3989
|
+
count,
|
|
3990
|
+
onSettle: setIndex
|
|
3991
|
+
});
|
|
3992
|
+
const popupRef = useRef(null);
|
|
3993
|
+
const indexRef = useRef(index);
|
|
3994
|
+
indexRef.current = index;
|
|
3995
|
+
const go = (next) => {
|
|
3996
|
+
if (count === 0) return;
|
|
3997
|
+
const target = loop ? (next + count) % count : Math.min(Math.max(next, 0), last);
|
|
3998
|
+
setIndex(target);
|
|
3999
|
+
if (!loop && (target === 0 || target === last) && document.activeElement instanceof HTMLButtonElement && document.activeElement.hasAttribute("data-image-viewer-nav")) popupRef.current?.focus({ preventScroll: true });
|
|
4000
|
+
};
|
|
4001
|
+
const onKeyDown = (event) => {
|
|
4002
|
+
if (event.defaultPrevented) return;
|
|
4003
|
+
if (event.target.closest("video, audio, input, textarea")) return;
|
|
4004
|
+
const next = {
|
|
4005
|
+
ArrowLeft: index - 1,
|
|
4006
|
+
ArrowRight: index + 1,
|
|
4007
|
+
Home: 0,
|
|
4008
|
+
End: last
|
|
4009
|
+
}[event.key];
|
|
4010
|
+
if (next === void 0) return;
|
|
4011
|
+
event.preventDefault();
|
|
4012
|
+
go(next);
|
|
4013
|
+
};
|
|
4014
|
+
const current = items[index]?.props;
|
|
4015
|
+
const many = count > 1;
|
|
4016
|
+
const captioned = Boolean(current?.title || current?.description);
|
|
4017
|
+
return /* @__PURE__ */ jsxs(Dialog$1.Popup, {
|
|
4018
|
+
"data-theme": "dark",
|
|
4019
|
+
"data-image-viewer": "",
|
|
4020
|
+
ref: popupRef,
|
|
4021
|
+
initialFocus: popupRef,
|
|
4022
|
+
finalFocus: () => thumbs.current[indexRef.current] ?? true,
|
|
4023
|
+
onKeyDown,
|
|
4024
|
+
className: cn("flex size-full flex-col bg-surface text-ink outline-none", "transition-opacity duration-(--duration-fast) ease-standard", "data-ending-style:opacity-0 data-starting-style:opacity-0"),
|
|
4025
|
+
children: [
|
|
4026
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4027
|
+
className: cn(overlayHeader, "h-14 px-4"),
|
|
4028
|
+
children: [
|
|
4029
|
+
/* @__PURE__ */ jsx(Dialog$1.Title, {
|
|
4030
|
+
className: "sr-only",
|
|
4031
|
+
children: label
|
|
4032
|
+
}),
|
|
4033
|
+
many && /* @__PURE__ */ jsx("span", {
|
|
4034
|
+
"aria-hidden": "true",
|
|
4035
|
+
"data-count": "",
|
|
4036
|
+
className: "font-tech text-sm text-ink-secondary tabular-nums",
|
|
4037
|
+
children: `${index + 1} / ${count}`
|
|
4038
|
+
}),
|
|
4039
|
+
/* @__PURE__ */ jsx(Dialog$1.Close, {
|
|
4040
|
+
"aria-label": closeLabel,
|
|
4041
|
+
className: overlayClose,
|
|
4042
|
+
children: /* @__PURE__ */ jsx(Close, { size: 24 })
|
|
4043
|
+
})
|
|
4044
|
+
]
|
|
4045
|
+
}),
|
|
4046
|
+
/* @__PURE__ */ jsx(Viewfinder, {
|
|
4047
|
+
size: "md",
|
|
4048
|
+
animate: true,
|
|
4049
|
+
className: "min-h-0 flex-1 [--bracket-delay:var(--duration-fast)]",
|
|
4050
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
4051
|
+
ref: trackRef,
|
|
4052
|
+
"data-image-viewer-track": "",
|
|
4053
|
+
onScroll,
|
|
4054
|
+
className: "absolute inset-0 flex snap-x snap-mandatory overflow-x-auto overscroll-x-contain [scrollbar-width:none]",
|
|
4055
|
+
children: items.map((item, position) => /* @__PURE__ */ jsx(Slide, {
|
|
4056
|
+
label: `第 ${position + 1} 张,共 ${count} 张`,
|
|
4057
|
+
current: position === index,
|
|
4058
|
+
onClose,
|
|
4059
|
+
children: Math.abs(position - index) <= 1 ? item.props.children : null
|
|
4060
|
+
}, position))
|
|
4061
|
+
})
|
|
4062
|
+
}),
|
|
4063
|
+
(many || captioned) && /* @__PURE__ */ jsxs("div", {
|
|
4064
|
+
className: "flex shrink-0 flex-wrap items-start gap-x-6 gap-y-3 border-t border-line px-4 py-4",
|
|
4065
|
+
children: [many && /* @__PURE__ */ jsxs("div", {
|
|
4066
|
+
className: "flex shrink-0 gap-2",
|
|
4067
|
+
children: [/* @__PURE__ */ jsx(IconButton, {
|
|
4068
|
+
variant: "floating",
|
|
4069
|
+
"aria-label": previousLabel,
|
|
4070
|
+
"data-image-viewer-nav": "",
|
|
4071
|
+
disabled: !loop && index <= 0,
|
|
4072
|
+
onClick: () => go(index - 1),
|
|
4073
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {})
|
|
4074
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
|
4075
|
+
variant: "floating",
|
|
4076
|
+
"aria-label": nextLabel,
|
|
4077
|
+
"data-image-viewer-nav": "",
|
|
4078
|
+
disabled: !loop && index >= last,
|
|
4079
|
+
onClick: () => go(index + 1),
|
|
4080
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
4081
|
+
})]
|
|
4082
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
4083
|
+
"aria-live": "polite",
|
|
4084
|
+
className: "flex min-w-0 flex-1 basis-48 flex-col gap-1",
|
|
4085
|
+
children: [
|
|
4086
|
+
many && /* @__PURE__ */ jsx("span", {
|
|
4087
|
+
className: "sr-only",
|
|
4088
|
+
children: `第 ${index + 1} 张,共 ${count} 张`
|
|
4089
|
+
}),
|
|
4090
|
+
current?.title && /* @__PURE__ */ jsx("p", {
|
|
4091
|
+
className: "text-base font-bold wrap-anywhere",
|
|
4092
|
+
children: current.title
|
|
4093
|
+
}),
|
|
4094
|
+
current?.description && /* @__PURE__ */ jsx("p", {
|
|
4095
|
+
className: "max-h-24 overflow-y-auto text-sm wrap-anywhere text-ink-secondary",
|
|
4096
|
+
children: current.description
|
|
4097
|
+
})
|
|
4098
|
+
]
|
|
4099
|
+
})]
|
|
4100
|
+
})
|
|
4101
|
+
]
|
|
4102
|
+
});
|
|
4103
|
+
}
|
|
4104
|
+
/**
|
|
4105
|
+
* 轨道上的一张。图是使用方给的元素,这里不碰它,只在外面听它的 `load`:
|
|
4106
|
+
* 还没取到的图是透明的,取到了淡入——不是"啪"地一下出现。
|
|
4107
|
+
* 只管直接放进来的 `<img>`;视频、别的内容不等,和原来一样直接在
|
|
4108
|
+
*/
|
|
4109
|
+
function Slide({ label, current, onClose, children }) {
|
|
4110
|
+
const [loaded, setLoaded] = useState(false);
|
|
4111
|
+
const settle = () => setLoaded(true);
|
|
4112
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4113
|
+
role: "group",
|
|
4114
|
+
"aria-roledescription": "幻灯片",
|
|
4115
|
+
"aria-label": label,
|
|
4116
|
+
inert: !current,
|
|
4117
|
+
"data-current": current ? "" : void 0,
|
|
4118
|
+
"data-loaded": loaded ? "" : void 0,
|
|
4119
|
+
onLoadCapture: settle,
|
|
4120
|
+
onErrorCapture: settle,
|
|
4121
|
+
onClick: (event) => {
|
|
4122
|
+
if (event.target === event.currentTarget) onClose();
|
|
4123
|
+
},
|
|
4124
|
+
className: cn("flex size-full shrink-0 snap-start items-center justify-center p-6 sm:p-10", "*:max-h-full *:max-w-full [&>img]:object-contain [&>video]:object-contain", "[&>img]:transition-opacity [&>img]:duration-(--duration-fast) [&>img]:ease-standard not-data-loaded:[&>img]:opacity-0"),
|
|
4125
|
+
children
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
/** 图片查看里的一张。直接放在 `ImageViewer` 里。 */
|
|
4129
|
+
function ImageViewerItem({ title, description: _description, thumbnail, "aria-label": ariaLabel, className, children, ...props }) {
|
|
4130
|
+
const item = useContext(ItemContext);
|
|
4131
|
+
if (!item) throw new Error("ImageViewerItem 要直接放在 ImageViewer 里面");
|
|
4132
|
+
const name = ariaLabel ?? `${item.viewLabel}:${typeof title === "string" ? title : `第 ${item.position + 1} 张,共 ${item.count} 张`}`;
|
|
4133
|
+
return /* @__PURE__ */ jsx("li", {
|
|
4134
|
+
...props,
|
|
4135
|
+
className: cn("min-w-0", className),
|
|
4136
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
4137
|
+
type: "button",
|
|
4138
|
+
ref: (node) => item.register(item.position, node),
|
|
4139
|
+
"aria-label": name,
|
|
4140
|
+
"aria-haspopup": "dialog",
|
|
4141
|
+
onClick: () => item.open(item.position),
|
|
4142
|
+
className: cn("group/thumb relative block w-full overflow-clip rounded-md bg-surface-muted", "[&>img]:size-full [&>img]:object-cover [&>svg]:size-full [&>video]:size-full [&>video]:object-cover", focusRing, ratioClass$2[item.ratio]),
|
|
4143
|
+
children: [thumbnail ?? children, /* @__PURE__ */ jsx("span", {
|
|
4144
|
+
"aria-hidden": "true",
|
|
4145
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 h-1 origin-left scale-x-0 bg-action transition-transform duration-(--duration-base) ease-standard group-hover/thumb:scale-x-100 group-focus-visible/thumb:scale-x-100"
|
|
4146
|
+
})]
|
|
4147
|
+
})
|
|
4148
|
+
});
|
|
4149
|
+
}
|
|
4150
|
+
//#endregion
|
|
3756
4151
|
//#region src/components/input/Input.tsx
|
|
3757
4152
|
/**
|
|
3758
4153
|
* 单行输入框。`className` 给外框,其余属性与 `ref` 给里面的 `<input>`。
|
|
@@ -4050,7 +4445,7 @@ function ItemSlot({ name, label, count, rarity, ratio = "1/1", selected = false,
|
|
|
4050
4445
|
...props,
|
|
4051
4446
|
"data-selected": selected ? "" : void 0,
|
|
4052
4447
|
"data-rarity": rarity,
|
|
4053
|
-
className: cn("relative", ratioClass$1[ratio], selected && "corner-brackets", className),
|
|
4448
|
+
className: cn("relative", ratioClass$1[ratio], selected && "corner-brackets after:animate-bracket-in", className),
|
|
4054
4449
|
children: node
|
|
4055
4450
|
});
|
|
4056
4451
|
}
|
|
@@ -4548,8 +4943,13 @@ function NavMenu({ trigger, open, defaultOpen = false, onOpenChange, title, bran
|
|
|
4548
4943
|
})]
|
|
4549
4944
|
});
|
|
4550
4945
|
}
|
|
4946
|
+
const itemEnter = [
|
|
4947
|
+
"animate-shift-in [--shift-x:calc(var(--motion-shift-lg)*-1)]",
|
|
4948
|
+
"[animation-delay:100ms] nth-2:[animation-delay:150ms] nth-3:[animation-delay:200ms]",
|
|
4949
|
+
"nth-4:[animation-delay:250ms] nth-5:[animation-delay:300ms] nth-[n+6]:[animation-delay:350ms]"
|
|
4950
|
+
].join(" ");
|
|
4551
4951
|
/** 全屏菜单里的一个栏目。 */
|
|
4552
|
-
function NavMenuItem({ icon, current = false, disabled = false, closeOnClick = true, href, target, rel, render, onClick, children, ...props }) {
|
|
4952
|
+
function NavMenuItem({ icon, current = false, disabled = false, closeOnClick = true, href, target, rel, render, onClick, className, children, ...props }) {
|
|
4553
4953
|
const close = useContext(NavMenuContext);
|
|
4554
4954
|
const isLink = href !== void 0 || render !== void 0;
|
|
4555
4955
|
const handleClick = (event) => {
|
|
@@ -4586,6 +4986,7 @@ function NavMenuItem({ icon, current = false, disabled = false, closeOnClick = t
|
|
|
4586
4986
|
] });
|
|
4587
4987
|
return /* @__PURE__ */ jsx("li", {
|
|
4588
4988
|
...props,
|
|
4989
|
+
className: cn(itemEnter, className),
|
|
4589
4990
|
children: isLink ? /* @__PURE__ */ jsx(LinkElement, {
|
|
4590
4991
|
render,
|
|
4591
4992
|
href: disabled ? void 0 : href,
|
|
@@ -4683,6 +5084,174 @@ function Navigator({ items, index, defaultIndex = 0, onIndexChange, loop = false
|
|
|
4683
5084
|
});
|
|
4684
5085
|
}
|
|
4685
5086
|
//#endregion
|
|
5087
|
+
//#region src/components/otp-input/OtpInput.tsx
|
|
5088
|
+
const slotSize = {
|
|
5089
|
+
sm: "h-8 w-8 text-base",
|
|
5090
|
+
md: "h-10 w-10 text-lg",
|
|
5091
|
+
lg: "h-14 w-14 text-2xl"
|
|
5092
|
+
};
|
|
5093
|
+
const slotFrame = {
|
|
5094
|
+
sunken: "border-b-2",
|
|
5095
|
+
outline: "border"
|
|
5096
|
+
};
|
|
5097
|
+
const slotFill = {
|
|
5098
|
+
sunken: "bg-surface-sunken",
|
|
5099
|
+
outline: "bg-surface"
|
|
5100
|
+
};
|
|
5101
|
+
const gapClass = {
|
|
5102
|
+
sm: "gap-1.5",
|
|
5103
|
+
md: "gap-2",
|
|
5104
|
+
lg: "gap-2"
|
|
5105
|
+
};
|
|
5106
|
+
const defaultSlotLabel = (position, length) => `第 ${position} 位,共 ${length} 位`;
|
|
5107
|
+
const toUpperCase = (value) => value.toUpperCase();
|
|
5108
|
+
/**
|
|
5109
|
+
* 验证码输入:一格一位的短码。每一格是一个真的输入框——打一位跳一格,
|
|
5110
|
+
* 粘贴一整串会分到各格,整组在 Tab 顺序里只停一次。
|
|
5111
|
+
*
|
|
5112
|
+
* 每个都要有标签:放进 `Field`,或者自己传 `aria-label`。
|
|
5113
|
+
* `className` 与 `ref` 给外面那一排。
|
|
5114
|
+
*/
|
|
5115
|
+
function OtpInput({ length = 6, value, defaultValue, onValueChange, onComplete, type = "numeric", uppercase = false, mask = false, groupSize, size = "md", variant = "sunken", invalid: invalidProp, id: idProp, disabled: disabledProp, required: requiredProp, readOnly = false, name, form, autoSubmit = false, autoFocus = false, slotLabel = defaultSlotLabel, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": describedByProp, className, ...props }) {
|
|
5116
|
+
const fieldContext = useFieldContext();
|
|
5117
|
+
const { id, disabled, required, invalid, ...field } = useFieldControl({
|
|
5118
|
+
id: idProp,
|
|
5119
|
+
disabled: disabledProp,
|
|
5120
|
+
required: requiredProp,
|
|
5121
|
+
invalid: invalidProp,
|
|
5122
|
+
"aria-describedby": describedByProp
|
|
5123
|
+
});
|
|
5124
|
+
const describedBy = field["aria-describedby"];
|
|
5125
|
+
const labelId = useId();
|
|
5126
|
+
const ownLabel = ariaLabelledBy ?? (ariaLabel ? labelId : void 0);
|
|
5127
|
+
const groupLabel = ownLabel ?? fieldContext?.labelId;
|
|
5128
|
+
const slotClass = cn("min-w-0 shrink text-center font-tech font-medium tabular-nums text-transparent caret-ink", "transition-colors duration-(--duration-fast) ease-standard", disabled ? "data-filled:text-ink-disabled" : "data-filled:text-ink", focusRing, slotSize[size], slotFrame[variant], readOnly ? "border-transparent bg-transparent" : slotFill[variant], disabled ? "cursor-not-allowed border-line" : invalid ? "border-danger" : !readOnly && "border-line-strong hover:not-focus:border-ink-secondary focus:border-ink");
|
|
5129
|
+
const group = groupSize !== void 0 && groupSize > 0 ? groupSize : 0;
|
|
5130
|
+
return /* @__PURE__ */ jsxs(OTPField.Root, {
|
|
5131
|
+
...props,
|
|
5132
|
+
id,
|
|
5133
|
+
length,
|
|
5134
|
+
value,
|
|
5135
|
+
defaultValue,
|
|
5136
|
+
onValueChange: onValueChange && ((next) => onValueChange(next)),
|
|
5137
|
+
onValueComplete: onComplete && ((next) => onComplete(next)),
|
|
5138
|
+
validationType: type,
|
|
5139
|
+
normalizeValue: uppercase ? toUpperCase : void 0,
|
|
5140
|
+
mask,
|
|
5141
|
+
disabled,
|
|
5142
|
+
readOnly,
|
|
5143
|
+
required,
|
|
5144
|
+
name,
|
|
5145
|
+
form,
|
|
5146
|
+
autoSubmit,
|
|
5147
|
+
"aria-labelledby": groupLabel,
|
|
5148
|
+
"aria-describedby": describedBy,
|
|
5149
|
+
"data-size": size,
|
|
5150
|
+
"data-variant": variant,
|
|
5151
|
+
"data-invalid": invalid ? "" : void 0,
|
|
5152
|
+
className: cn("flex max-w-full min-w-0 items-center", gapClass[size], className),
|
|
5153
|
+
children: [ariaLabel && !ariaLabelledBy && /* @__PURE__ */ jsx("span", {
|
|
5154
|
+
id: labelId,
|
|
5155
|
+
hidden: true,
|
|
5156
|
+
children: ariaLabel
|
|
5157
|
+
}), Array.from({ length }, (_, index) => /* @__PURE__ */ jsxs(Fragment, { children: [group > 0 && index > 0 && index % group === 0 && /* @__PURE__ */ jsx("span", {
|
|
5158
|
+
"aria-hidden": "true",
|
|
5159
|
+
"data-separator": "",
|
|
5160
|
+
className: "h-0.5 w-2 shrink-0 bg-line-strong"
|
|
5161
|
+
}), /* @__PURE__ */ jsx(OTPField.Input, {
|
|
5162
|
+
...index === 0 ? {
|
|
5163
|
+
"aria-labelledby": ownLabel,
|
|
5164
|
+
"aria-describedby": describedBy
|
|
5165
|
+
} : { "aria-label": slotLabel(index + 1, length) },
|
|
5166
|
+
"aria-invalid": invalid || void 0,
|
|
5167
|
+
autoFocus: autoFocus && index === 0,
|
|
5168
|
+
autoCapitalize: uppercase ? "characters" : "none",
|
|
5169
|
+
className: slotClass
|
|
5170
|
+
})] }, index))]
|
|
5171
|
+
});
|
|
5172
|
+
}
|
|
5173
|
+
//#endregion
|
|
5174
|
+
//#region src/icons/ArrowLeft.tsx
|
|
5175
|
+
const ArrowLeft = createIcon("ArrowLeft", /* @__PURE__ */ jsx("path", { d: "M20 12H5M11 5l-7 7 7 7" }));
|
|
5176
|
+
//#endregion
|
|
5177
|
+
//#region src/components/page-header/PageHeader.tsx
|
|
5178
|
+
/**
|
|
5179
|
+
* 页头:这是哪一页、怎么回去、在这一页能做什么。一页一个。
|
|
5180
|
+
* 按自身的宽度响应:行动区放不下时折到标题下面。
|
|
5181
|
+
*/
|
|
5182
|
+
function PageHeader({ title, level = 1, meta, description, breadcrumb, back, actions, divider = false, sticky = false, className, children, ...props }) {
|
|
5183
|
+
const Heading = `h${level}`;
|
|
5184
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
5185
|
+
...props,
|
|
5186
|
+
"data-sticky": sticky ? "" : void 0,
|
|
5187
|
+
className: cn("@container flex flex-col gap-4 text-ink", divider && !sticky && "border-b border-line pb-6", sticky && "sticky top-0 z-(--z-float) border-b border-line bg-surface py-4", className),
|
|
5188
|
+
children: [
|
|
5189
|
+
breadcrumb,
|
|
5190
|
+
/* @__PURE__ */ jsxs("div", {
|
|
5191
|
+
className: "flex flex-col gap-1",
|
|
5192
|
+
children: [meta && /* @__PURE__ */ jsx("p", {
|
|
5193
|
+
className: "font-tech text-xs text-ink-secondary",
|
|
5194
|
+
children: meta
|
|
5195
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
5196
|
+
className: "flex flex-wrap items-start justify-between gap-x-6 gap-y-4",
|
|
5197
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
5198
|
+
className: "flex min-w-0 flex-1 basis-64 items-start gap-4",
|
|
5199
|
+
children: [back && /* @__PURE__ */ jsx("div", {
|
|
5200
|
+
className: "flex h-10 shrink-0 items-center @md:h-12",
|
|
5201
|
+
children: back
|
|
5202
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
5203
|
+
className: "flex min-w-0 flex-col gap-1",
|
|
5204
|
+
children: [/* @__PURE__ */ jsx(Heading, {
|
|
5205
|
+
className: "min-w-0 text-2xl leading-10 font-bold wrap-anywhere @md:text-3xl @md:leading-12",
|
|
5206
|
+
children: title
|
|
5207
|
+
}), description && /* @__PURE__ */ jsx("p", {
|
|
5208
|
+
className: "max-w-prose text-sm text-ink-secondary",
|
|
5209
|
+
children: description
|
|
5210
|
+
})]
|
|
5211
|
+
})]
|
|
5212
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
5213
|
+
"data-actions": "",
|
|
5214
|
+
className: "flex min-h-10 flex-wrap items-center gap-2 @md:min-h-12",
|
|
5215
|
+
children: actions
|
|
5216
|
+
})]
|
|
5217
|
+
})]
|
|
5218
|
+
}),
|
|
5219
|
+
children
|
|
5220
|
+
]
|
|
5221
|
+
});
|
|
5222
|
+
}
|
|
5223
|
+
/**
|
|
5224
|
+
* 页头里的返回:40px 的深灰斜纹方块加一个左箭头,`back` 按钮收成的图标形态。
|
|
5225
|
+
* 放进 `PageHeader` 的 `back`。
|
|
5226
|
+
*/
|
|
5227
|
+
function PageHeaderBack({ "aria-label": label = "返回", href, target, rel, render, onClick, disabled = false, className, ...props }) {
|
|
5228
|
+
const classes = cn("relative inline-flex size-10 shrink-0 items-center justify-center", "transition-colors duration-(--duration-fast) ease-standard", focusRing, disabled ? "cursor-not-allowed bg-disabled text-on-disabled" : "hatch hatch-mid bg-control text-white shadow-xs hover:bg-neutral-600 active:bg-control-pressed", className);
|
|
5229
|
+
const icon = /* @__PURE__ */ jsx(ArrowLeft, { size: 20 });
|
|
5230
|
+
if (href !== void 0 || render !== void 0) return /* @__PURE__ */ jsx(LinkElement, {
|
|
5231
|
+
render,
|
|
5232
|
+
href: disabled ? void 0 : href,
|
|
5233
|
+
role: disabled ? "link" : void 0,
|
|
5234
|
+
"aria-disabled": disabled || void 0,
|
|
5235
|
+
"aria-label": label,
|
|
5236
|
+
target,
|
|
5237
|
+
rel,
|
|
5238
|
+
onClick: disabled ? (event) => event.preventDefault() : onClick,
|
|
5239
|
+
"data-page-header-back": "",
|
|
5240
|
+
className: classes,
|
|
5241
|
+
children: icon
|
|
5242
|
+
});
|
|
5243
|
+
return /* @__PURE__ */ jsx("button", {
|
|
5244
|
+
...props,
|
|
5245
|
+
type: "button",
|
|
5246
|
+
"aria-label": label,
|
|
5247
|
+
disabled,
|
|
5248
|
+
onClick,
|
|
5249
|
+
"data-page-header-back": "",
|
|
5250
|
+
className: classes,
|
|
5251
|
+
children: icon
|
|
5252
|
+
});
|
|
5253
|
+
}
|
|
5254
|
+
//#endregion
|
|
4686
5255
|
//#region src/components/pagination/Pagination.tsx
|
|
4687
5256
|
const barSize = {
|
|
4688
5257
|
md: "h-12",
|
|
@@ -4867,7 +5436,7 @@ function Popover({ trigger, title, description, side = "bottom", align = "start"
|
|
|
4867
5436
|
className: "z-(--z-overlay) outline-none",
|
|
4868
5437
|
children: /* @__PURE__ */ jsxs(Popover$1.Popup, {
|
|
4869
5438
|
...props,
|
|
4870
|
-
className: cn(
|
|
5439
|
+
className: cn(popoverPanel, className),
|
|
4871
5440
|
children: [(title || description) && /* @__PURE__ */ jsxs("div", {
|
|
4872
5441
|
className: "flex flex-col gap-1",
|
|
4873
5442
|
children: [title && /* @__PURE__ */ jsx(Popover$1.Title, {
|
|
@@ -5343,6 +5912,64 @@ function ScheduleItem({ start, end, title, type, variant = "banner", media, href
|
|
|
5343
5912
|
});
|
|
5344
5913
|
}
|
|
5345
5914
|
//#endregion
|
|
5915
|
+
//#region src/components/scroll-area/ScrollArea.tsx
|
|
5916
|
+
const barClass = {
|
|
5917
|
+
vertical: "w-3 before:absolute before:inset-y-0 before:left-1/2 before:w-px before:-translate-x-1/2 before:bg-line",
|
|
5918
|
+
horizontal: "h-3 before:absolute before:inset-x-0 before:top-1/2 before:h-px before:-translate-y-1/2 before:bg-line"
|
|
5919
|
+
};
|
|
5920
|
+
const thumbClass = {
|
|
5921
|
+
vertical: "w-full before:inset-y-0 before:left-1/2 before:w-[3px] before:-translate-x-1/2",
|
|
5922
|
+
horizontal: "h-full before:inset-x-0 before:top-1/2 before:h-[3px] before:-translate-y-1/2"
|
|
5923
|
+
};
|
|
5924
|
+
function Scrollbar({ orientation }) {
|
|
5925
|
+
return /* @__PURE__ */ jsx(ScrollArea$1.Scrollbar, {
|
|
5926
|
+
orientation,
|
|
5927
|
+
className: cn("group/bar flex", barClass[orientation]),
|
|
5928
|
+
children: /* @__PURE__ */ jsx(ScrollArea$1.Thumb, { className: cn("relative before:absolute before:bg-ink-secondary before:content-['']", "before:transition-colors before:duration-(--duration-fast) before:ease-standard", "group-hover/bar:before:bg-ink active:before:bg-ink", "forced-colors:before:bg-[CanvasText]", thumbClass[orientation]) })
|
|
5929
|
+
});
|
|
5930
|
+
}
|
|
5931
|
+
/**
|
|
5932
|
+
* 滚动区:一块自己能滚的区域,滚动条是画出来的。高度(或高度上限)由 `className` 给。
|
|
5933
|
+
* 只有真的溢出时才有滚动条,也才是一个能聚焦的区域。
|
|
5934
|
+
*
|
|
5935
|
+
* 整页的滚动留给浏览器,不要把页面主体包进来。
|
|
5936
|
+
* `className` 与 `ref` 给外框,其余属性(`onScroll` 等)给真正在滚的可视区。
|
|
5937
|
+
*/
|
|
5938
|
+
function ScrollArea({ "aria-label": label, orientation = "vertical", viewportRef, className, style, ref, children, ...props }) {
|
|
5939
|
+
const vertical = orientation !== "horizontal";
|
|
5940
|
+
const horizontal = orientation !== "vertical";
|
|
5941
|
+
return /* @__PURE__ */ jsxs(ScrollArea$1.Root, {
|
|
5942
|
+
ref,
|
|
5943
|
+
style,
|
|
5944
|
+
"data-scroll-area": "",
|
|
5945
|
+
"data-orientation": orientation,
|
|
5946
|
+
className: cn("flex min-h-0 min-w-0 flex-col text-ink", className),
|
|
5947
|
+
children: [
|
|
5948
|
+
/* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
|
|
5949
|
+
...props,
|
|
5950
|
+
ref: viewportRef,
|
|
5951
|
+
style: orientation === "vertical" ? { overflowX: "hidden" } : orientation === "horizontal" ? { overflowY: "hidden" } : void 0,
|
|
5952
|
+
className: cn("min-h-0 flex-auto", focusRingInset, vertical && "data-has-overflow-y:mr-3", horizontal && "data-has-overflow-x:mb-3"),
|
|
5953
|
+
render: (viewportProps, state) => {
|
|
5954
|
+
const scrollable = vertical && state.hasOverflowY || horizontal && state.hasOverflowX;
|
|
5955
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5956
|
+
...viewportProps,
|
|
5957
|
+
...scrollable && {
|
|
5958
|
+
role: "region",
|
|
5959
|
+
"aria-label": label
|
|
5960
|
+
},
|
|
5961
|
+
tabIndex: scrollable ? 0 : -1
|
|
5962
|
+
});
|
|
5963
|
+
},
|
|
5964
|
+
children: /* @__PURE__ */ jsx(ScrollArea$1.Content, { children })
|
|
5965
|
+
}),
|
|
5966
|
+
vertical && /* @__PURE__ */ jsx(Scrollbar, { orientation: "vertical" }),
|
|
5967
|
+
horizontal && /* @__PURE__ */ jsx(Scrollbar, { orientation: "horizontal" }),
|
|
5968
|
+
orientation === "both" && /* @__PURE__ */ jsx(ScrollArea$1.Corner, {})
|
|
5969
|
+
]
|
|
5970
|
+
});
|
|
5971
|
+
}
|
|
5972
|
+
//#endregion
|
|
5346
5973
|
//#region src/components/scroll-hint/ScrollHint.tsx
|
|
5347
5974
|
/**
|
|
5348
5975
|
* 滚动提示:只在确实还能往下滚、并且还没开始滚的时候出现,滚动开始后淡出。
|
|
@@ -6112,8 +6739,9 @@ const THUMB_HALF = 6;
|
|
|
6112
6739
|
const markLeft = (ratio) => `calc(${THUMB_HALF}px + (100% - 12px) * ${ratio})`;
|
|
6113
6740
|
const thumb = [
|
|
6114
6741
|
"relative h-5 w-3 bg-surface-inverse shadow-[0_0_0_2px_var(--ef-surface)]",
|
|
6742
|
+
"transition-[inset-inline-start] duration-(--duration-fast) ease-standard data-dragging:transition-none",
|
|
6115
6743
|
"before:absolute before:-inset-x-3.5 before:-inset-y-2.5 before:content-['']",
|
|
6116
|
-
"after:absolute after:inset-y-1 after:left-1/2 after:w-0.5 after:-translate-x-1/2 after:bg-accent-ink-inverse after:opacity-0 after:content-[''] data-dragging:after:opacity-100",
|
|
6744
|
+
"after:absolute after:inset-y-1 after:left-1/2 after:w-0.5 after:-translate-x-1/2 after:bg-accent-ink-inverse after:opacity-0 after:transition-opacity after:duration-(--duration-fast) after:ease-standard after:content-[''] data-dragging:after:opacity-100",
|
|
6117
6745
|
"choice-diamond:size-3.5 choice-diamond:rotate-45 choice-diamond:after:inset-1 choice-diamond:after:w-auto choice-diamond:after:translate-x-0",
|
|
6118
6746
|
"data-disabled:bg-ink-disabled",
|
|
6119
6747
|
focusRingWithin
|
|
@@ -6168,7 +6796,7 @@ function Slider(props) {
|
|
|
6168
6796
|
className: cn("flex touch-none items-center px-1.5 select-none data-disabled:cursor-not-allowed", controlSize[size]),
|
|
6169
6797
|
children: /* @__PURE__ */ jsxs(Slider$1.Track, {
|
|
6170
6798
|
className: "h-1 w-full bg-line",
|
|
6171
|
-
children: [/* @__PURE__ */ jsx(Slider$1.Indicator, { className: "bg-ink data-disabled:bg-ink-disabled" }), (range ? [0, 1] : [0]).map((index) => /* @__PURE__ */ jsx(Slider$1.Thumb, {
|
|
6799
|
+
children: [/* @__PURE__ */ jsx(Slider$1.Indicator, { className: "bg-ink transition-[inset-inline-start,width] duration-(--duration-fast) ease-standard data-disabled:bg-ink-disabled data-dragging:transition-none" }), (range ? [0, 1] : [0]).map((index) => /* @__PURE__ */ jsx(Slider$1.Thumb, {
|
|
6172
6800
|
index,
|
|
6173
6801
|
...range ? { "aria-label": thumbLabels[index] } : {
|
|
6174
6802
|
"aria-label": ariaLabel,
|
|
@@ -6535,9 +7163,9 @@ function Step({ title, description, invalid = false, href, target, rel, render,
|
|
|
6535
7163
|
"aria-hidden": "true",
|
|
6536
7164
|
className: cn("flex shrink-0 items-center", railLayout[orientation]),
|
|
6537
7165
|
children: [/* @__PURE__ */ jsx("span", {
|
|
6538
|
-
className: cn("flex shrink-0 items-center justify-center font-tech leading-none tabular-nums", nodeSize[size], invalid ? "border border-danger text-danger" : nodeClass$1[status]),
|
|
7166
|
+
className: cn("flex shrink-0 items-center justify-center font-tech leading-none tabular-nums", "transition-colors duration-(--duration-fast) ease-standard", nodeSize[size], invalid ? "border border-danger text-danger" : nodeClass$1[status]),
|
|
6539
7167
|
children: invalid ? /* @__PURE__ */ jsx(StatusDanger, {}) : status === "done" ? /* @__PURE__ */ jsx(Check, {}) : String(index + 1).padStart(2, "0")
|
|
6540
|
-
}), /* @__PURE__ */ jsx("span", { className: cn("flex-1 group-last/step:hidden", lineLayout[orientation], status === "done" ? "
|
|
7168
|
+
}), /* @__PURE__ */ jsx("span", { className: cn("relative flex-1 overflow-clip bg-line-strong group-last/step:hidden", "before:absolute before:top-0 before:-right-4 before:-bottom-4 before:left-0 before:origin-top-left before:bg-ink before:transition-[scale] before:duration-(--duration-base) before:ease-exit before:content-['']", lineLayout[orientation], status === "done" ? "before:scale-100" : "before:scale-0") })]
|
|
6541
7169
|
}), /* @__PURE__ */ jsxs("div", {
|
|
6542
7170
|
className: cn("min-w-0 flex-1", bodyLayout[orientation]),
|
|
6543
7171
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -7528,11 +8156,11 @@ const toneText = {
|
|
|
7528
8156
|
const placementClass = {
|
|
7529
8157
|
bottom: {
|
|
7530
8158
|
viewport: "bottom-6",
|
|
7531
|
-
root: "bottom-0 translate-
|
|
8159
|
+
root: "bottom-0 transform-[translate(calc(-50%+var(--toast-swipe-movement-x,0px)),var(--toast-swipe-movement-y,0px))] data-starting-style:translate-y-(--motion-shift-lg)"
|
|
7532
8160
|
},
|
|
7533
8161
|
center: {
|
|
7534
8162
|
viewport: "top-1/2",
|
|
7535
|
-
root: "top-0 translate-
|
|
8163
|
+
root: "top-0 transform-[translate(calc(-50%+var(--toast-swipe-movement-x,0px)),calc(-50%+var(--toast-swipe-movement-y,0px)))]"
|
|
7536
8164
|
}
|
|
7537
8165
|
};
|
|
7538
8166
|
function isOptions(input) {
|
|
@@ -7579,7 +8207,7 @@ function ToastHost({ placement, label, closeLabel, children }) {
|
|
|
7579
8207
|
return /* @__PURE__ */ jsx(Toast.Root, {
|
|
7580
8208
|
toast,
|
|
7581
8209
|
"data-theme": "dark",
|
|
7582
|
-
className: cn("pointer-events-auto absolute left-1/2 w-max max-w-full
|
|
8210
|
+
className: cn("pointer-events-auto absolute left-1/2 w-max max-w-full", "border border-line bg-black/80 text-base leading-[1.4] text-white", "transition-[opacity,translate,transform] duration-[var(--duration-fast),var(--duration-base),var(--duration-fast)] ease-[var(--ease-standard),var(--ease-exit),var(--ease-standard)]", "data-ending-style:opacity-0 data-starting-style:opacity-0", "data-swiping:select-none", focusRingInset, placementClass[placement].root),
|
|
7583
8211
|
children: /* @__PURE__ */ jsxs(Toast.Content, {
|
|
7584
8212
|
className: cn("flex items-center gap-3 py-2.5 pl-5", toast.data?.dismissible ? "pr-2" : "pr-5"),
|
|
7585
8213
|
children: [
|
|
@@ -7995,7 +8623,7 @@ function Tooltip({ content, children, side = "top", align = "center", delay, arr
|
|
|
7995
8623
|
sideOffset: arrow ? 8 : 2,
|
|
7996
8624
|
className: "z-(--z-overlay)",
|
|
7997
8625
|
children: /* @__PURE__ */ jsxs(Tooltip$1.Popup, {
|
|
7998
|
-
className: cn("relative max-w-64 border border-transparent bg-surface-raised px-2.5 py-1.5 text-sm wrap-anywhere text-ink", "origin-(--transform-origin) transition-opacity duration-(--duration-fast) ease-standard", "data-ending-style:opacity-0 data-instant:transition-none data-starting-style:opacity-0", className),
|
|
8626
|
+
className: cn("relative max-w-64 border border-transparent bg-surface-raised px-2.5 py-1.5 text-sm wrap-anywhere text-ink", "origin-(--transform-origin) transition-[opacity,translate] duration-(--duration-fast) ease-standard", "data-ending-style:opacity-0 data-instant:transition-none data-starting-style:opacity-0", enterFromSide, className),
|
|
7999
8627
|
children: [arrow && /* @__PURE__ */ jsx(Tooltip$1.Arrow, {
|
|
8000
8628
|
"aria-hidden": "true",
|
|
8001
8629
|
className: cn("size-2 rotate-45 bg-surface-raised forced-colors:hidden", "data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1 data-[side=top]:-bottom-1")
|
|
@@ -8040,52 +8668,6 @@ function TopBar({ brand, tools, action, menu, className, children, ...props }) {
|
|
|
8040
8668
|
});
|
|
8041
8669
|
}
|
|
8042
8670
|
//#endregion
|
|
8043
|
-
//#region src/components/viewfinder/Viewfinder.tsx
|
|
8044
|
-
const armClass = {
|
|
8045
|
-
sm: "[--bracket-arm:16px]",
|
|
8046
|
-
md: "[--bracket-arm:24px]"
|
|
8047
|
-
};
|
|
8048
|
-
const readoutPosition = {
|
|
8049
|
-
topLeft: "top-5 left-5",
|
|
8050
|
-
topRight: "top-5 right-5 text-right",
|
|
8051
|
-
bottomLeft: "bottom-5 left-5",
|
|
8052
|
-
bottomRight: "right-5 bottom-5 text-right"
|
|
8053
|
-
};
|
|
8054
|
-
/**
|
|
8055
|
-
* 取景角:框住一张图、一片地图区域、一个焦点,标的是"范围",不是选中。
|
|
8056
|
-
* 里面放被框住的内容;压在深色画面上时给它加 `data-theme="dark"`。
|
|
8057
|
-
*
|
|
8058
|
-
* 只用在展示性的画面上,一屏一处。取景角与准星是装饰,读数是真实信息、读屏读得到。
|
|
8059
|
-
*/
|
|
8060
|
-
function Viewfinder({ size = "sm", crosshair = false, readouts, className, children, ...props }) {
|
|
8061
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
8062
|
-
...props,
|
|
8063
|
-
className: cn("relative text-ink", className),
|
|
8064
|
-
children: [
|
|
8065
|
-
children,
|
|
8066
|
-
/* @__PURE__ */ jsx("span", {
|
|
8067
|
-
"aria-hidden": "true",
|
|
8068
|
-
className: cn(decor, "corner-brackets absolute inset-3 [--bracket-color:currentColor] [--bracket-offset:0px] [--bracket-width:1.5px]", armClass[size])
|
|
8069
|
-
}),
|
|
8070
|
-
crosshair && /* @__PURE__ */ jsx("svg", {
|
|
8071
|
-
"aria-hidden": "true",
|
|
8072
|
-
focusable: "false",
|
|
8073
|
-
viewBox: "0 0 12 12",
|
|
8074
|
-
fill: "none",
|
|
8075
|
-
stroke: "currentColor",
|
|
8076
|
-
strokeWidth: 1.5,
|
|
8077
|
-
className: cn("pointer-events-none select-none forced-colors:hidden print:hidden", "absolute top-1/2 left-1/2 size-3 -translate-1/2 text-accent-ink"),
|
|
8078
|
-
children: /* @__PURE__ */ jsx("path", { d: "M6 0v12M0 6h12" })
|
|
8079
|
-
}),
|
|
8080
|
-
readouts && Object.keys(readoutPosition).map((corner) => readouts[corner] !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
8081
|
-
"data-corner": corner,
|
|
8082
|
-
className: cn("pointer-events-none absolute font-tech text-xs leading-none text-ink-secondary tabular-nums", readoutPosition[corner]),
|
|
8083
|
-
children: readouts[corner]
|
|
8084
|
-
}, corner))
|
|
8085
|
-
]
|
|
8086
|
-
});
|
|
8087
|
-
}
|
|
8088
|
-
//#endregion
|
|
8089
8671
|
//#region src/hooks/useTheme.ts
|
|
8090
8672
|
const STORAGE_KEY = "ef-theme";
|
|
8091
8673
|
const DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
@@ -8172,11 +8754,8 @@ function useTheme() {
|
|
|
8172
8754
|
};
|
|
8173
8755
|
}
|
|
8174
8756
|
//#endregion
|
|
8175
|
-
//#region src/icons/ArrowLeft.tsx
|
|
8176
|
-
const ArrowLeft = createIcon("ArrowLeft", /* @__PURE__ */ jsx("path", { d: "M20 12H5M11 5l-7 7 7 7" }));
|
|
8177
|
-
//#endregion
|
|
8178
8757
|
//#region src/icons/Menu.tsx
|
|
8179
8758
|
/** 三条横线:打开菜单 */
|
|
8180
8759
|
const Menu = createIcon("Menu", /* @__PURE__ */ jsx("path", { d: "M3 6h18M3 12h18M3 18h18" }));
|
|
8181
8760
|
//#endregion
|
|
8182
|
-
export { Accordion, AccordionItem, Alert, ArrowCorner, ArrowLeft, ArrowRight, Avatar, AvatarSwitcher, AvatarSwitcherItem, BackToTop, Badge, BracketTitle, Breadcrumb, BreadcrumbItem, Button, ButtonGroup, Calendar, CalendarIcon, Carousel, CarouselSlide, Check, Checkbox, ChevronDown, ChevronLeft, ChevronRight, Close, Combobox, CompletionBanner, ContextMenu, CornerBrackets, Countdown, Crosshair, DashIndicator, DataRow, DataRowList, DatePicker, DateRangePicker, Dialog, DialogClose, Drawer, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuGroup, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, EmptyState, Field, FileItem, FileUpload, FilterChip, FlyoutBar, FlyoutBarItem, GhostText, Hatch, HazardStripe, IconButton, Input, ItemGrid, ItemSlot, Kbd, List, ListRow, Loader, Lock, Marquee, MediaCard, Menu, Minus, NavAction, NavMenu, NavMenuItem, Navigator, Pagination, Panel, PanelBody, PanelHeader, PanelRow, PanelRows, PlayButton, PlayMark, Plus, Popover, PopoverClose, Progress, ProgressRing, Radio, RadioGroup, RecIndicator, RegistrationStrip, ResourceChip, Schedule, ScheduleItem, ScheduleTrack, ScrollHint, SectionTitle, Segment, SegmentedControl, Select, SelectGroup, SelectItem, SelectSeparator, SideRail, SideRailGroup, SideRailItem, SideRailSub, SideRailSubItem, Skeleton, Slider, Sparkline, Spinner, Stat, StatusDanger, StatusInfo, StatusSuccess, StatusWarning, Step, Stepper, Steps, Switch, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableExpander, TableHead, TableHeaderCell, TableRow, Tabs, Tag, TagInput, TagPair, Term, Textarea, Texture, TickRing, Timeline, TimelineItem, ToastProvider, Toc, TocItem, Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator, ToolbarToggle, ToolbarToggleGroup, Tooltip, TooltipProvider, TopBar, TriangleRight, Upload, Viewfinder, cn, createIcon, useControllableState, useFieldContext, useFieldControl, useInView, usePortalScope, useTheme, useToast };
|
|
8761
|
+
export { Accordion, AccordionItem, Alert, ArrowCorner, ArrowLeft, ArrowRight, Avatar, AvatarSwitcher, AvatarSwitcherItem, BackToTop, Badge, BracketTitle, Breadcrumb, BreadcrumbItem, Button, ButtonGroup, Calendar, CalendarIcon, Carousel, CarouselSlide, Check, Checkbox, ChevronDown, ChevronLeft, ChevronRight, Close, Combobox, CompletionBanner, ContextMenu, CornerBrackets, Countdown, Crosshair, DashIndicator, DataRow, DataRowList, DatePicker, DateRangePicker, Dialog, DialogClose, Drawer, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuGroup, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, EmptyState, Field, FileItem, FileUpload, FilterChip, FlyoutBar, FlyoutBarItem, GhostText, Hatch, HazardStripe, HoverCard, IconButton, ImageViewer, ImageViewerItem, Input, ItemGrid, ItemSlot, Kbd, List, ListRow, Loader, Lock, Marquee, MediaCard, Menu, Minus, NavAction, NavMenu, NavMenuItem, Navigator, OtpInput, PageHeader, PageHeaderBack, Pagination, Panel, PanelBody, PanelHeader, PanelRow, PanelRows, PlayButton, PlayMark, Plus, Popover, PopoverClose, Progress, ProgressRing, Radio, RadioGroup, RecIndicator, RegistrationStrip, ResourceChip, Schedule, ScheduleItem, ScheduleTrack, ScrollArea, ScrollHint, SectionTitle, Segment, SegmentedControl, Select, SelectGroup, SelectItem, SelectSeparator, SideRail, SideRailGroup, SideRailItem, SideRailSub, SideRailSubItem, Skeleton, Slider, Sparkline, Spinner, Stat, StatusDanger, StatusInfo, StatusSuccess, StatusWarning, Step, Stepper, Steps, Switch, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableExpander, TableHead, TableHeaderCell, TableRow, Tabs, Tag, TagInput, TagPair, Term, Textarea, Texture, TickRing, Timeline, TimelineItem, ToastProvider, Toc, TocItem, Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator, ToolbarToggle, ToolbarToggleGroup, Tooltip, TooltipProvider, TopBar, TriangleRight, Upload, Viewfinder, cn, createIcon, useControllableState, useFieldContext, useFieldControl, useInView, usePortalScope, useTheme, useToast };
|