@endfield-ui/react 0.2.0 → 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/NOTICE.md +2 -2
- package/README.md +103 -8
- package/dist/index.d.ts +78 -17
- package/dist/index.js +1184 -264
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/README.md +11 -2
- package/src/components/alert/Alert.tsx +63 -1
- package/src/components/avatar/Avatar.tsx +5 -2
- package/src/components/bracket-title/BracketTitle.tsx +32 -3
- package/src/components/button/Button.tsx +37 -5
- package/src/components/calendar/Calendar.tsx +49 -5
- package/src/components/checkbox/Checkbox.tsx +1 -0
- package/src/components/checkbox/choice-style.ts +13 -5
- package/src/components/chip/capsule-style.ts +18 -3
- package/src/components/combobox/Combobox.tsx +10 -0
- package/src/components/completion-banner/CompletionBanner.tsx +30 -2
- package/src/components/corner-brackets/CornerBrackets.tsx +2 -1
- package/src/components/dash-indicator/DashIndicator.tsx +5 -2
- package/src/components/data-row/DataRow.tsx +2 -1
- package/src/components/date-picker/DateField.tsx +5 -1
- package/src/components/dialog/Dialog.tsx +4 -2
- package/src/components/dropdown-menu/MenuCheck.tsx +4 -2
- package/src/components/dropdown-menu/menu-style.ts +22 -5
- package/src/components/empty-state/EmptyState.tsx +5 -0
- package/src/components/field/Field.tsx +39 -7
- package/src/components/file-upload/FileUpload.tsx +79 -20
- package/src/components/icon-button/IconButton.tsx +18 -5
- package/src/components/image-viewer/ImageViewer.tsx +58 -18
- package/src/components/input/control-box.ts +23 -2
- package/src/components/input/value-chip.ts +3 -2
- package/src/components/item-slot/ItemSlot.tsx +2 -1
- package/src/components/loader/Loader.tsx +12 -41
- package/src/components/nav-menu/NavMenu.tsx +17 -3
- package/src/components/otp-input/OtpInput.tsx +13 -4
- package/src/components/page-header/PageHeader.tsx +39 -3
- package/src/components/pagination/Pagination.tsx +2 -0
- package/src/components/progress/Progress.tsx +10 -3
- package/src/components/progress/ProgressRing.tsx +19 -4
- package/src/components/radio/Radio.tsx +2 -0
- package/src/components/rolling-number/RollingNumber.tsx +125 -0
- package/src/components/rolling-number/index.ts +1 -0
- package/src/components/schedule/Schedule.tsx +5 -3
- package/src/components/segmented-control/SegmentedControl.tsx +25 -4
- package/src/components/skeleton/Skeleton.tsx +4 -1
- package/src/components/slider/Slider.tsx +26 -5
- package/src/components/sparkline/Sparkline.tsx +29 -5
- package/src/components/spinner/Spinner.tsx +6 -2
- package/src/components/stepper/Stepper.tsx +4 -0
- package/src/components/steps/Steps.tsx +30 -9
- package/src/components/switch/Switch.tsx +26 -2
- package/src/components/table/Table.tsx +33 -9
- package/src/components/tabs/Tabs.tsx +55 -9
- package/src/components/tag-input/TagInput.tsx +82 -43
- package/src/components/textarea/Textarea.tsx +2 -1
- package/src/components/timeline/Timeline.tsx +4 -3
- package/src/components/toast/Toast.tsx +18 -5
- package/src/components/toc/Toc.tsx +89 -7
- package/src/components/toolbar/Toolbar.tsx +24 -0
- package/src/components/tooltip/Tooltip.tsx +5 -1
- package/src/components/viewfinder/Viewfinder.tsx +9 -0
- package/src/hooks/README.md +8 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddedKeys.ts +26 -0
- package/src/hooks/useFlip.ts +211 -0
- package/src/hooks/useIndicator.ts +135 -0
- package/src/hooks/useLingering.ts +122 -0
- package/src/hooks/usePresence.ts +100 -0
- package/src/hooks/useReducedMotion.ts +26 -0
- package/src/hooks/useTheme.ts +44 -4
- package/src/index.ts +1 -0
- package/src/lib/README.md +2 -0
- package/src/lib/cn.ts +3 -0
- package/src/lib/inline-style.ts +12 -0
- package/src/lib/motion.ts +90 -0
- package/src/styles/theme.css +81 -2
- package/src/styles/utilities.css +2 -1
package/src/lib/README.md
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
| `cn.ts` | `cn()`:合并类名(`clsx` + `tailwind-merge`)。里面登记了 [theme.css](../styles/theme.css) 的自定义令牌名,否则 `text-micro` 会被当成颜色、与 `text-ink` 互相覆盖 |
|
|
8
8
|
| `focus-ring.ts` | `focusRing`、`focusRingInset`、`focusRingWithin`:全库统一的焦点环类名 |
|
|
9
9
|
| `merge-refs.ts` | `mergeRefs()`:组件自己要用节点、同时还得把它交给使用方时,把几个 ref 合成一个 |
|
|
10
|
+
| `inline-style.ts` | `dropEmptyStyle()`:往元素上临时写过内联样式、又都撤掉之后,把空下来的 `style` 属性拿掉。浏览器是懒着同步这个属性的,直接删会落空、回头又冒出一个空的 |
|
|
10
11
|
| `defined.ts` | `defined()`:去掉值是 `undefined` 的属性。把控件的属性合并到使用方给的元素上(`render`)之前用,免得把那个元素自己写的盖掉 |
|
|
11
12
|
| `decor.ts` | `decor`:装饰层共用的类 |
|
|
13
|
+
| `motion.ts` | 几处共用的动效类名。`enterFromSide`:浮层面板进场的方向位移——按基元给的 `data-side`,起点往触发元素那一侧偏 `--motion-shift`;`collapse` / `collapseEnter` / `collapseInner`:按高度收放;`fadeInFast`:小件的淡入;`indicator` / `indicatorBox`:画在容器 `::after` 上的选中指示,位置来自 `useIndicator`;`flipBox` / `flipLeaving`:补位(`useFlip`)的那一排,和里面走的那一项。见 [动效](../../../../docs/design/foundations/motion.md#补的几条约定) |
|
|
12
14
|
| `date.ts` | 日期的纯函数:加减日和月、比较、把两天排成先后、夹到范围内、月历的六行七列。日期一律是 `YYYY-MM-DD` 的字符串,全部按 UTC 算,不碰本地时区。`Calendar`、`DatePicker`、`Schedule` 共用,不对外导出 |
|
|
13
15
|
| `file.ts` | 文件的纯函数:按 `accept` 认类型(扩展名、MIME、`image/*` 通配)、把字节数写成 `1.2 MB`、取扩展名、判断是不是同一个文件。`FileUpload` 用,不对外导出 |
|
|
14
16
|
|
package/src/lib/cn.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 往元素上临时写过内联样式、又都撤掉之后调:原来没有 `style` 属性的,
|
|
3
|
+
* 不留一个空的在上面。
|
|
4
|
+
*
|
|
5
|
+
* 先问一句"有没有这个属性"不是多余的:浏览器是懒着把内联样式同步成属性的,
|
|
6
|
+
* 不问就去删,删的那一下落空,回头它又冒出来一个空的 `style=""`。
|
|
7
|
+
*/
|
|
8
|
+
export function dropEmptyStyle(node: HTMLElement) {
|
|
9
|
+
if (node.style.length === 0 && node.hasAttribute("style")) {
|
|
10
|
+
node.removeAttribute("style");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浮层从触发它的那一侧来:进场的起点往触发处偏 `--motion-shift`,走完归位。
|
|
3
|
+
* 用在基元的浮层面板上——它们带着 `data-side`(面板在触发元素的哪一侧)。
|
|
4
|
+
*
|
|
5
|
+
* 位移只写在 `data-starting-style` 上:退场不走位移,只淡出。
|
|
6
|
+
* 用的地方自己给 `translate` 加过渡。`inline-start` / `inline-end` 按从左往右的排版算。
|
|
7
|
+
*/
|
|
8
|
+
export const enterFromSide = [
|
|
9
|
+
"data-starting-style:data-[side=bottom]:-translate-y-(--motion-shift)",
|
|
10
|
+
"data-starting-style:data-[side=top]:translate-y-(--motion-shift)",
|
|
11
|
+
"data-starting-style:data-[side=right]:-translate-x-(--motion-shift)",
|
|
12
|
+
"data-starting-style:data-[side=left]:translate-x-(--motion-shift)",
|
|
13
|
+
"data-starting-style:data-[side=inline-end]:-translate-x-(--motion-shift)",
|
|
14
|
+
"data-starting-style:data-[side=inline-start]:translate-x-(--motion-shift)",
|
|
15
|
+
].join(" ");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 按高度收放,不量高度:外面这一层是只有一行的网格,行高在 `0fr` 和 `1fr` 之间过渡;
|
|
19
|
+
* 里面再套一层 `collapseInner`,裁掉还放不下的那一截。表格的明细行、字段下面的错误文字
|
|
20
|
+
* 都这么收放——内容后来变高了也跟着走。
|
|
21
|
+
*
|
|
22
|
+
* 展开 300ms、收起 200ms。收起由 `data-leaving` 触发,配 `usePresence`:收完再卸载。
|
|
23
|
+
*/
|
|
24
|
+
export const collapse = [
|
|
25
|
+
"grid grid-rows-[1fr]",
|
|
26
|
+
"transition-[grid-template-rows] duration-(--duration-base) ease-standard",
|
|
27
|
+
"data-leaving:grid-rows-[0fr] data-leaving:duration-(--duration-fast)",
|
|
28
|
+
].join(" ");
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 展开的起点,写在 `@starting-style` 里:元素一挂上就从 0 长出来,展开到一半又收起时
|
|
32
|
+
* 过渡从半路折回去。只给后来才出现的加(`usePresence` 的 `entered`)——一开始就展开着的,
|
|
33
|
+
* 不该在载入时播一遍。不支持它的旧浏览器只是没有展开的过程。
|
|
34
|
+
*/
|
|
35
|
+
export const collapseEnter = "starting:grid-rows-[0fr]";
|
|
36
|
+
|
|
37
|
+
/** 只裁纵向:横向照旧,里面的东西该多宽还是多宽 */
|
|
38
|
+
export const collapseInner = "min-h-0 min-w-0 overflow-y-clip";
|
|
39
|
+
|
|
40
|
+
/** 小件的淡入,200ms:切过来的页签面板、新加的标签、新选的文件 */
|
|
41
|
+
export const fadeInFast =
|
|
42
|
+
"animate-fade-in [animation-duration:var(--duration-fast)]";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 选中指示:画在容器的 `::after` 上的一块东西,压在容器的底色之上、各项之下。
|
|
46
|
+
* 位置和尺寸来自 `useIndicator` 写在容器上的变量;换了一项时滑过去(200ms,
|
|
47
|
+
* 和各项换字色同一拍),没有当前项时原地淡出。
|
|
48
|
+
*
|
|
49
|
+
* 容器上没有 `data-indicator`(量不到)时不画——各项照旧自己画选中态,
|
|
50
|
+
* 所以给各项的选中底加一个 `in-data-indicator:` 的变体把它让出来。
|
|
51
|
+
* 这里只管定位和过渡:颜色、圆角由用的地方给,几何用 `indicatorBox` 或者自己写。
|
|
52
|
+
*/
|
|
53
|
+
export const indicator = [
|
|
54
|
+
"relative isolate",
|
|
55
|
+
"after:pointer-events-none after:absolute after:top-0 after:left-0 after:-z-1",
|
|
56
|
+
"after:hidden data-indicator:after:block",
|
|
57
|
+
"after:opacity-0 data-[indicator=on]:after:opacity-100",
|
|
58
|
+
"after:transition-[translate,width,height,opacity] after:duration-(--duration-fast) after:ease-standard",
|
|
59
|
+
// 没有当前项的时候只留淡入淡出:下一次亮起来是原地出现,不是从上一个位置滑来
|
|
60
|
+
"data-[indicator=off]:after:transition-[opacity]",
|
|
61
|
+
"data-indicator-instant:after:transition-none",
|
|
62
|
+
].join(" ");
|
|
63
|
+
|
|
64
|
+
/** 指示盖住当前项的整个盒子:分段选择的墨块、胶囊页签的底 */
|
|
65
|
+
export const indicatorBox = [
|
|
66
|
+
"after:h-(--indicator-h) after:w-(--indicator-w)",
|
|
67
|
+
"after:translate-x-(--indicator-x) after:translate-y-(--indicator-y)",
|
|
68
|
+
].join(" ");
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 补位(`useFlip`):一排里有一项走了,其余的从旧位置滑到新位置。
|
|
72
|
+
*
|
|
73
|
+
* `flipBox` 给那一排——走的那一项以它为参照钉在原处,并且压在别的项下面。
|
|
74
|
+
* 少了一行它就矮了:高度由钩子量着收(200ms),这期间还在滑的项不露到它外面去;
|
|
75
|
+
* 各行靠上排,撑着的那一段空在底下。
|
|
76
|
+
* `flipLeaving` 给会走的每一项:带上 `data-leaving` 之后脱离排版(位置由钩子给)、
|
|
77
|
+
* 原地淡出、点不到。
|
|
78
|
+
*
|
|
79
|
+
* 过渡由用的地方自己写:各项的 `translate`(滑回原位)、走的那一项的 `opacity`,
|
|
80
|
+
* 都是 200ms——这几项多半本来就带着换色的过渡,得写进同一份清单里。
|
|
81
|
+
*/
|
|
82
|
+
export const flipBox = [
|
|
83
|
+
"relative isolate content-start overflow-y-clip",
|
|
84
|
+
"transition-[min-height] duration-(--duration-fast) ease-standard",
|
|
85
|
+
].join(" ");
|
|
86
|
+
|
|
87
|
+
export const flipLeaving = [
|
|
88
|
+
"data-leaving:pointer-events-none data-leaving:absolute data-leaving:-z-1",
|
|
89
|
+
"data-leaving:opacity-0",
|
|
90
|
+
].join(" ");
|
package/src/styles/theme.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* endfield-ui
|
|
2
|
+
* endfield-ui 设计令牌
|
|
3
3
|
*
|
|
4
4
|
* 用法:
|
|
5
5
|
* @import "tailwindcss";
|
|
6
|
-
* @import "
|
|
6
|
+
* @import "@endfield-ui/react/theme.css";
|
|
7
7
|
*
|
|
8
8
|
* 结构:
|
|
9
9
|
* 1. 语义变量 随主题变化;亮色在 :root 与 [data-theme="light"],
|
|
@@ -336,6 +336,13 @@
|
|
|
336
336
|
--duration-slow: 400ms;
|
|
337
337
|
--duration-slower: 600ms;
|
|
338
338
|
|
|
339
|
+
/*
|
|
340
|
+
* 进场的方向位移:浮层从触发它的那一侧来。只是提示方向,不是滑行。
|
|
341
|
+
* 大的那一档给大面积的和贴着视口边缘出来的(全屏菜单的栏目、轻提示)
|
|
342
|
+
*/
|
|
343
|
+
--motion-shift: 4px;
|
|
344
|
+
--motion-shift-lg: 8px;
|
|
345
|
+
|
|
339
346
|
/* —— 母题尺寸 —— */
|
|
340
347
|
--cut-sm: 6px;
|
|
341
348
|
--cut-md: 10px;
|
|
@@ -368,6 +375,9 @@
|
|
|
368
375
|
--animate-fade-in: ef-fade-in 300ms ease-out both;
|
|
369
376
|
--animate-indeterminate: ef-indeterminate 1.2s linear infinite alternate;
|
|
370
377
|
--animate-pulse: ef-pulse 2s ease-in-out infinite;
|
|
378
|
+
--animate-bracket-in: ef-bracket-in 200ms ease-out both;
|
|
379
|
+
--animate-shift-in: ef-shift-in 300ms ease-out both;
|
|
380
|
+
--animate-wipe-in: ef-wipe-in 300ms ease-out both;
|
|
371
381
|
|
|
372
382
|
/* 三次短促闪烁后点亮(官网实测的关键帧节奏) */
|
|
373
383
|
@keyframes ef-flicker {
|
|
@@ -507,6 +517,54 @@
|
|
|
507
517
|
opacity: 0.6;
|
|
508
518
|
}
|
|
509
519
|
}
|
|
520
|
+
|
|
521
|
+
/*
|
|
522
|
+
* 角括号落位:八段短线各自从角的顶点伸到满长。写成 after:animate-bracket-in,
|
|
523
|
+
* 和 corner-brackets 一起用——八层的顺序(横、竖交替)跟着 utilities.css 里那八层背景走。
|
|
524
|
+
* 只写起点:终点就是括号自己的尺寸。只动背景的尺寸,括号不会比平时多占一点地方
|
|
525
|
+
*/
|
|
526
|
+
@keyframes ef-bracket-in {
|
|
527
|
+
from {
|
|
528
|
+
background-size:
|
|
529
|
+
0 var(--_w),
|
|
530
|
+
var(--_w) 0,
|
|
531
|
+
0 var(--_w),
|
|
532
|
+
var(--_w) 0,
|
|
533
|
+
0 var(--_w),
|
|
534
|
+
var(--_w) 0,
|
|
535
|
+
0 var(--_w),
|
|
536
|
+
var(--_w) 0;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/*
|
|
541
|
+
* 位移入场:从偏着的地方淡入归位。往哪边偏、偏多少由用的地方给
|
|
542
|
+
* (--shift-x / --shift-y,量用 --motion-shift 那两档),不给就只是淡入。
|
|
543
|
+
* 只写起点:终点就是元素自己的样子
|
|
544
|
+
*/
|
|
545
|
+
@keyframes ef-shift-in {
|
|
546
|
+
from {
|
|
547
|
+
opacity: 0;
|
|
548
|
+
translate: var(--shift-x, 0) var(--shift-y, 0);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/*
|
|
553
|
+
* 擦入:从左往右露出来。两头都要写——"没有裁切"和 inset() 之间插不了值。
|
|
554
|
+
* 有东西画到盒子外面一点的(小型面积图的折线有半个线宽在外面),用 --wipe-bleed
|
|
555
|
+
* 把另外几边往外让,不然走完之后那一圈是被裁掉的
|
|
556
|
+
*/
|
|
557
|
+
@keyframes ef-wipe-in {
|
|
558
|
+
from {
|
|
559
|
+
clip-path: inset(
|
|
560
|
+
calc(0px - var(--wipe-bleed, 0px)) 100%
|
|
561
|
+
calc(0px - var(--wipe-bleed, 0px)) calc(0px - var(--wipe-bleed, 0px))
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
to {
|
|
565
|
+
clip-path: inset(calc(0px - var(--wipe-bleed, 0px)));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
510
568
|
}
|
|
511
569
|
|
|
512
570
|
/* ------------------------------------------------------------------ */
|
|
@@ -552,6 +610,24 @@
|
|
|
552
610
|
--color-info: var(--ef-info);
|
|
553
611
|
}
|
|
554
612
|
|
|
613
|
+
/* ------------------------------------------------------------------ */
|
|
614
|
+
/* 主题切换 */
|
|
615
|
+
/* ------------------------------------------------------------------ */
|
|
616
|
+
|
|
617
|
+
/*
|
|
618
|
+
* useTheme 切亮暗时整页交叉淡化一次(浏览器支持视图过渡时)。切的那一下它在 <html> 上标
|
|
619
|
+
* data-theme-switching,这里把根的那几层换成"颜色、透明度"那一档的时长。
|
|
620
|
+
* 只在这个标记下生效:使用方自己的视图过渡不受影响。三层要一起给——只改新旧两层的话,
|
|
621
|
+
* 组那一层还是浏览器默认的 250ms,整个过渡要到那时候才算完。
|
|
622
|
+
* "减少动态效果"不在这里管:开了的时候 useTheme 根本不走视图过渡
|
|
623
|
+
*/
|
|
624
|
+
:root[data-theme-switching]::view-transition-group(root),
|
|
625
|
+
:root[data-theme-switching]::view-transition-old(root),
|
|
626
|
+
:root[data-theme-switching]::view-transition-new(root) {
|
|
627
|
+
animation-duration: var(--duration-fast);
|
|
628
|
+
animation-timing-function: var(--ease-standard);
|
|
629
|
+
}
|
|
630
|
+
|
|
555
631
|
/* ------------------------------------------------------------------ */
|
|
556
632
|
/* 减少动态效果 */
|
|
557
633
|
/* ------------------------------------------------------------------ */
|
|
@@ -563,6 +639,9 @@
|
|
|
563
639
|
animation-duration: 0.01ms !important;
|
|
564
640
|
animation-iteration-count: 1 !important;
|
|
565
641
|
transition-duration: 0.01ms !important;
|
|
642
|
+
/* 延迟也归零:不然错开的入场会变成一项一项蹦出来 */
|
|
643
|
+
animation-delay: 0s !important;
|
|
644
|
+
transition-delay: 0s !important;
|
|
566
645
|
scroll-behavior: auto !important;
|
|
567
646
|
}
|
|
568
647
|
}
|
package/src/styles/utilities.css
CHANGED
|
@@ -121,7 +121,8 @@
|
|
|
121
121
|
/* ------------------------------------------------------------------ */
|
|
122
122
|
/* 角括号:四角各一个 L 形短线,用 ::after 的背景画,不占用 outline。 */
|
|
123
123
|
/* 宿主要自己定位(relative 或 absolute);括号画在宿主之外, */
|
|
124
|
-
/* 所以宿主和它的裁切祖先之间要留出 --bracket-offset
|
|
124
|
+
/* 所以宿主和它的裁切祖先之间要留出 --bracket-offset 的空隙。 */
|
|
125
|
+
/* 要它出现时"落位",再加 after:animate-bracket-in(关键帧在 theme.css) */
|
|
125
126
|
/* ------------------------------------------------------------------ */
|
|
126
127
|
|
|
127
128
|
@utility corner-brackets {
|