@antfu/design 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/a11y/cli.ts +73 -0
  4. package/a11y/index.ts +13 -0
  5. package/a11y/scan.ts +127 -0
  6. package/components/Action/ActionButton.stories.ts +56 -0
  7. package/components/Action/ActionButton.vue +57 -0
  8. package/components/Action/ActionDarkToggle.stories.ts +31 -0
  9. package/components/Action/ActionDarkToggle.vue +87 -0
  10. package/components/Action/ActionIconButton.stories.ts +47 -0
  11. package/components/Action/ActionIconButton.vue +47 -0
  12. package/components/Display/DisplayAvatar.stories.ts +36 -0
  13. package/components/Display/DisplayAvatar.vue +58 -0
  14. package/components/Display/DisplayBadge.stories.ts +31 -0
  15. package/components/Display/DisplayBadge.vue +98 -0
  16. package/components/Display/DisplayBytes.stories.ts +28 -0
  17. package/components/Display/DisplayBytes.vue +30 -0
  18. package/components/Display/DisplayDate.stories.ts +37 -0
  19. package/components/Display/DisplayDate.vue +29 -0
  20. package/components/Display/DisplayDonut.stories.ts +26 -0
  21. package/components/Display/DisplayDonut.vue +46 -0
  22. package/components/Display/DisplayDuration.stories.ts +28 -0
  23. package/components/Display/DisplayDuration.vue +28 -0
  24. package/components/Display/DisplayFileIcon.stories.ts +27 -0
  25. package/components/Display/DisplayFileIcon.vue +30 -0
  26. package/components/Display/DisplayFilePath.stories.ts +30 -0
  27. package/components/Display/DisplayFilePath.vue +61 -0
  28. package/components/Display/DisplayKbd.stories.ts +26 -0
  29. package/components/Display/DisplayKbd.vue +27 -0
  30. package/components/Display/DisplayKeyValue.stories.ts +56 -0
  31. package/components/Display/DisplayKeyValue.vue +51 -0
  32. package/components/Display/DisplayLabel.stories.ts +27 -0
  33. package/components/Display/DisplayLabel.vue +33 -0
  34. package/components/Display/DisplayNumber.stories.ts +27 -0
  35. package/components/Display/DisplayNumber.vue +24 -0
  36. package/components/Display/DisplayNumberBadge.stories.ts +26 -0
  37. package/components/Display/DisplayNumberBadge.vue +22 -0
  38. package/components/Display/DisplayPackageName.stories.ts +26 -0
  39. package/components/Display/DisplayPackageName.vue +49 -0
  40. package/components/Display/DisplayProgressBar.stories.ts +29 -0
  41. package/components/Display/DisplayProgressBar.vue +90 -0
  42. package/components/Display/DisplayProportionBar.stories.ts +40 -0
  43. package/components/Display/DisplayProportionBar.vue +43 -0
  44. package/components/Display/DisplaySafeImage.stories.ts +43 -0
  45. package/components/Display/DisplaySafeImage.vue +30 -0
  46. package/components/Display/DisplayStatusPill.stories.ts +34 -0
  47. package/components/Display/DisplayStatusPill.vue +42 -0
  48. package/components/Display/DisplayTree.stories.ts +76 -0
  49. package/components/Display/DisplayTree.vue +102 -0
  50. package/components/Display/DisplayVersion.stories.ts +25 -0
  51. package/components/Display/DisplayVersion.vue +21 -0
  52. package/components/Feedback/FeedbackEmptyState.stories.ts +38 -0
  53. package/components/Feedback/FeedbackEmptyState.vue +21 -0
  54. package/components/Feedback/FeedbackLoading.stories.ts +23 -0
  55. package/components/Feedback/FeedbackLoading.vue +21 -0
  56. package/components/Feedback/FeedbackSpinner.stories.ts +25 -0
  57. package/components/Feedback/FeedbackSpinner.vue +22 -0
  58. package/components/Feedback/FeedbackTip.stories.ts +34 -0
  59. package/components/Feedback/FeedbackTip.vue +29 -0
  60. package/components/Feedback/FeedbackToasts.stories.ts +40 -0
  61. package/components/Feedback/FeedbackToasts.vue +105 -0
  62. package/components/Form/FormCheckbox.stories.ts +36 -0
  63. package/components/Form/FormCheckbox.vue +30 -0
  64. package/components/Form/FormCombobox.stories.ts +35 -0
  65. package/components/Form/FormCombobox.vue +83 -0
  66. package/components/Form/FormField.stories.ts +56 -0
  67. package/components/Form/FormField.vue +36 -0
  68. package/components/Form/FormNumberInput.stories.ts +47 -0
  69. package/components/Form/FormNumberInput.vue +85 -0
  70. package/components/Form/FormRadioGroup.stories.ts +47 -0
  71. package/components/Form/FormRadioGroup.vue +43 -0
  72. package/components/Form/FormSearchField.stories.ts +22 -0
  73. package/components/Form/FormSearchField.vue +32 -0
  74. package/components/Form/FormSelect.stories.ts +47 -0
  75. package/components/Form/FormSelect.vue +56 -0
  76. package/components/Form/FormSwitch.stories.ts +36 -0
  77. package/components/Form/FormSwitch.vue +26 -0
  78. package/components/Form/FormTextInput.stories.ts +39 -0
  79. package/components/Form/FormTextInput.vue +51 -0
  80. package/components/Form/FormTextarea.stories.ts +47 -0
  81. package/components/Form/FormTextarea.vue +32 -0
  82. package/components/Layout/LayoutBreadcrumb.stories.ts +54 -0
  83. package/components/Layout/LayoutBreadcrumb.vue +54 -0
  84. package/components/Layout/LayoutCard.stories.ts +31 -0
  85. package/components/Layout/LayoutCard.vue +21 -0
  86. package/components/Layout/LayoutDataTable.stories.ts +77 -0
  87. package/components/Layout/LayoutDataTable.vue +145 -0
  88. package/components/Layout/LayoutExpandableList.stories.ts +28 -0
  89. package/components/Layout/LayoutExpandableList.vue +94 -0
  90. package/components/Layout/LayoutPanelGrids.stories.ts +28 -0
  91. package/components/Layout/LayoutPanelGrids.vue +26 -0
  92. package/components/Layout/LayoutSectionBlock.stories.ts +37 -0
  93. package/components/Layout/LayoutSectionBlock.vue +37 -0
  94. package/components/Layout/LayoutSideNav.stories.ts +33 -0
  95. package/components/Layout/LayoutSideNav.vue +48 -0
  96. package/components/Layout/LayoutSplitPane.stories.ts +44 -0
  97. package/components/Layout/LayoutSplitPane.vue +30 -0
  98. package/components/Layout/LayoutTabs.stories.ts +43 -0
  99. package/components/Layout/LayoutTabs.vue +56 -0
  100. package/components/Layout/LayoutToolbar.stories.ts +60 -0
  101. package/components/Layout/LayoutToolbar.vue +28 -0
  102. package/components/Layout/LayoutVirtualList.stories.ts +30 -0
  103. package/components/Layout/LayoutVirtualList.vue +82 -0
  104. package/components/Overlay/OverlayDrawer.stories.ts +47 -0
  105. package/components/Overlay/OverlayDrawer.vue +58 -0
  106. package/components/Overlay/OverlayDropdown.stories.ts +25 -0
  107. package/components/Overlay/OverlayDropdown.vue +30 -0
  108. package/components/Overlay/OverlayDropdownItem.stories.ts +26 -0
  109. package/components/Overlay/OverlayDropdownItem.vue +31 -0
  110. package/components/Overlay/OverlayDropdownLabel.vue +9 -0
  111. package/components/Overlay/OverlayDropdownSeparator.vue +7 -0
  112. package/components/Overlay/OverlayModal.stories.ts +33 -0
  113. package/components/Overlay/OverlayModal.vue +48 -0
  114. package/components/Overlay/OverlayTooltip.stories.ts +33 -0
  115. package/components/Overlay/OverlayTooltip.vue +38 -0
  116. package/composables/colorScheme.ts +58 -0
  117. package/composables/toast.ts +81 -0
  118. package/package.json +99 -0
  119. package/skills/antfu-design/SKILL.md +65 -0
  120. package/skills/antfu-design/references/advanced-patterns.md +39 -0
  121. package/skills/antfu-design/references/best-practices.md +54 -0
  122. package/skills/antfu-design/references/core-components.md +72 -0
  123. package/skills/antfu-design/references/core-setup.md +56 -0
  124. package/skills/antfu-design/references/core-tokens.md +100 -0
  125. package/skills/antfu-design/references/features-data-presentation.md +27 -0
  126. package/splitpanes.d.ts +70 -0
  127. package/styles/animations.css +47 -0
  128. package/styles/base.css +31 -0
  129. package/styles/floating-vue.css +28 -0
  130. package/styles/index.css +7 -0
  131. package/styles/reka-ui.css +112 -0
  132. package/styles/scrollbar.css +24 -0
  133. package/styles/splitpanes.css +61 -0
  134. package/unocss/colors.ts +127 -0
  135. package/unocss/index.ts +99 -0
  136. package/unocss/options.ts +31 -0
  137. package/unocss/patterns.ts +38 -0
  138. package/unocss/rules.ts +26 -0
  139. package/unocss/severity.ts +16 -0
  140. package/unocss/shortcuts.ts +68 -0
  141. package/utils/color.ts +328 -0
  142. package/utils/contrast.ts +118 -0
  143. package/utils/format.ts +389 -0
  144. package/utils/icon.ts +200 -0
  145. package/utils/index.ts +13 -0
  146. package/utils/keybinding.ts +199 -0
  147. package/utils/misc.ts +141 -0
  148. package/utils/path.ts +243 -0
  149. package/utils/semver.ts +147 -0
  150. package/utils/tree.ts +89 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Lightweight, dependency-free semver helpers. Enough to parse, normalize and
3
+ * compare versions and `||`-joined ranges for display — unifying the version
4
+ * parsing duplicated across the source projects without pulling in `semver`.
5
+ */
6
+
7
+ export interface ParsedSemver {
8
+ valid: boolean
9
+ raw: string
10
+ highest?: string
11
+ lowest?: string
12
+ parts?: string[]
13
+ bare?: string[]
14
+ }
15
+
16
+ const SEMVER_RE = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([\w.-]+))?/
17
+
18
+ function parseVersion(version: string): [number, number, number, string] | null {
19
+ const m = version.trim().match(SEMVER_RE)
20
+ if (!m)
21
+ return null
22
+ return [Number(m[1] ?? 0), Number(m[2] ?? 0), Number(m[3] ?? 0), m[4] ?? '']
23
+ }
24
+
25
+ /**
26
+ * Compare two versions: `-1` / `0` / `1`. A release outranks its prerelease.
27
+ *
28
+ * Compares major/minor/patch numerically, then breaks ties so that a version
29
+ * without a prerelease tag sorts above one with (e.g. `1.0.0` > `1.0.0-beta`).
30
+ * Unparseable inputs compare as equal (`0`). Suitable as an `Array#sort`
31
+ * comparator.
32
+ *
33
+ * @param a - The first version string (a leading `v` is allowed).
34
+ * @param b - The second version string (a leading `v` is allowed).
35
+ * @returns `-1` if `a < b`, `1` if `a > b`, `0` if equal.
36
+ *
37
+ * @example
38
+ * compareSemver('1.2.3', '1.2.4') // → -1
39
+ * compareSemver('2.0.0', '1.9.9') // → 1
40
+ * compareSemver('1.0.0', '1.0.0-beta') // → 1
41
+ * compareSemver('1.0.0', '1.0.0') // → 0
42
+ */
43
+ export function compareSemver(a: string, b: string): number {
44
+ if (a === b)
45
+ return 0
46
+ const pa = parseVersion(a)
47
+ const pb = parseVersion(b)
48
+ if (!pa || !pb)
49
+ return 0
50
+ for (let i = 0; i < 3; i++) {
51
+ if (pa[i] !== pb[i])
52
+ return pa[i] < pb[i] ? -1 : 1
53
+ }
54
+ // Equal core: a version without prerelease is greater than one with.
55
+ if (pa[3] === pb[3])
56
+ return 0
57
+ if (!pa[3])
58
+ return 1
59
+ if (!pb[3])
60
+ return -1
61
+ return pa[3] < pb[3] ? -1 : 1
62
+ }
63
+
64
+ const rangeCache = new Map<string, ParsedSemver>()
65
+
66
+ /**
67
+ * Parse a (possibly `||`-joined) range into a normalized, sorted summary.
68
+ *
69
+ * Splits on `||`, strips range operators and `.x`/`.*` wildcards, pads each part
70
+ * to a full `x.y.z`, sorts the results, and reports the `lowest`/`highest`
71
+ * bounds. Results are memoized by input string. A range with no parseable parts
72
+ * yields `{ valid: false }`.
73
+ *
74
+ * @param range - The version range string to parse.
75
+ * @returns A {@link ParsedSemver} summary; `valid` is `false` when nothing parses.
76
+ *
77
+ * @example
78
+ * const r = parseSemverRange('^1.2.0 || ^2.0.0')
79
+ * r.valid // → true
80
+ * r.lowest // → '1.2.0'
81
+ * r.highest // → '2.0.0'
82
+ * r.parts // → ['^1.2.0', '^2.0.0'] (length 2)
83
+ */
84
+ export function parseSemverRange(range: string): ParsedSemver {
85
+ const cached = rangeCache.get(range)
86
+ if (cached)
87
+ return cached
88
+
89
+ const result: ParsedSemver = { valid: false, raw: range }
90
+ rangeCache.set(range, result)
91
+
92
+ const parts = range
93
+ .split(/\|\|/g)
94
+ .map(i => i.replace(/\s+/g, ''))
95
+ .filter(Boolean)
96
+
97
+ if (!parts.length)
98
+ return result
99
+
100
+ const bare = parts
101
+ .map(i => i.replace(/^[\^~>=<]+/, '').replace(/\.[*x]$/i, '').trim())
102
+ .map((i) => {
103
+ const seg = i.split('.')
104
+ if (seg.length === 1)
105
+ return `${i}.0.0`
106
+ if (seg.length === 2)
107
+ return `${i}.0`
108
+ return i
109
+ })
110
+ .filter(i => parseVersion(i) != null)
111
+ .sort(compareSemver)
112
+
113
+ if (!bare.length)
114
+ return result
115
+
116
+ result.valid = true
117
+ result.parts = parts
118
+ result.bare = bare
119
+ result.lowest = bare[0]
120
+ result.highest = bare[bare.length - 1]
121
+ return result
122
+ }
123
+
124
+ /**
125
+ * Compare two ranges by their lowest bound, then by breadth.
126
+ *
127
+ * Orders so that ranges with a higher lower bound sort first (descending);
128
+ * ties are broken by the number of `||`-joined parts. Useful for sorting
129
+ * dependency ranges newest-first.
130
+ *
131
+ * @param a - The first range string. Defaults to `'*'`.
132
+ * @param b - The second range string. Defaults to `'*'`.
133
+ * @returns A negative number if `a` sorts before `b`, positive if after, `0` if equal.
134
+ *
135
+ * @example
136
+ * compareSemverRange('^2.0.0', '^1.0.0') // → -1 (less than 0; the higher range sorts first)
137
+ */
138
+ export function compareSemverRange(a = '*', b = '*'): number {
139
+ if (a === b)
140
+ return 0
141
+ const pa = parseSemverRange(a)
142
+ const pb = parseSemverRange(b)
143
+ const cmp = compareSemver(pb.lowest || '0.0.0', pa.lowest || '0.0.0')
144
+ if (cmp !== 0)
145
+ return cmp
146
+ return (pb.parts?.length || 0) - (pa.parts?.length || 0)
147
+ }
package/utils/tree.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Build a nested tree from a flat list of `/`-delimited paths, with
3
+ * single-child-chain flattening (so `a/b/c` with no siblings renders as one
4
+ * node `a/b/c` rather than three). Generalized from the inspectors' file trees.
5
+ */
6
+
7
+ export interface TreeNode<T> {
8
+ /** The display name for this segment (may be a flattened chain like `a/b`). */
9
+ name: string
10
+ /** Full path up to and including this node. */
11
+ path: string
12
+ /** The original item, present on leaf nodes. */
13
+ item?: T
14
+ children: TreeNode<T>[]
15
+ }
16
+
17
+ export interface ToTreeOptions {
18
+ /** Separator between path segments. Default `/`. */
19
+ separator?: string
20
+ /** Collapse chains of single-child nodes into one. Default `true`. */
21
+ flatten?: boolean
22
+ }
23
+
24
+ function flattenChain<T>(node: TreeNode<T>, separator: string): void {
25
+ while (node.children.length === 1 && node.item == null) {
26
+ const child = node.children[0]
27
+ node.name = `${node.name}${separator}${child.name}`
28
+ node.path = child.path
29
+ node.item = child.item
30
+ node.children = child.children
31
+ }
32
+ for (const child of node.children)
33
+ flattenChain(child, separator)
34
+ }
35
+
36
+ /**
37
+ * Convert a flat list into a nested tree keyed by `getPath(item)`.
38
+ *
39
+ * Each item's path is split on `separator` into segments that become nested
40
+ * {@link TreeNode}s, with the original item attached to the leaf. When `flatten`
41
+ * is enabled, chains of single-child intermediate nodes are merged into one node
42
+ * whose `name` joins the segments (so `a/b/c` with no siblings becomes a single
43
+ * `a/b/c` node).
44
+ *
45
+ * @param items - The flat list of items to nest.
46
+ * @param getPath - Maps an item to its `separator`-delimited path string.
47
+ * @param options - Tree-building options.
48
+ * @param options.separator - Segment separator. Defaults to `'/'`.
49
+ * @param options.flatten - Whether to collapse single-child chains. Defaults to `true`.
50
+ * @returns The top-level {@link TreeNode}s of the built tree.
51
+ *
52
+ * @example
53
+ * const tree = toTree([{ p: 'a/b/c' }, { p: 'a/b/d' }], i => i.p)
54
+ * tree.length // → 1
55
+ * tree[0].name // → 'a/b' (shared single-child chain flattened)
56
+ * tree[0].children.map(c => c.name).sort() // → ['c', 'd']
57
+ */
58
+ export function toTree<T>(
59
+ items: T[],
60
+ getPath: (item: T) => string,
61
+ options: ToTreeOptions = {},
62
+ ): TreeNode<T>[] {
63
+ const { separator = '/', flatten = true } = options
64
+ const root: TreeNode<T> = { name: '', path: '', children: [] }
65
+
66
+ for (const item of items) {
67
+ const segments = getPath(item).split(separator).filter(Boolean)
68
+ let current = root
69
+ let acc = ''
70
+ segments.forEach((segment, i) => {
71
+ acc = acc ? `${acc}${separator}${segment}` : segment
72
+ let next = current.children.find(c => c.name === segment && c.item == null)
73
+ if (!next) {
74
+ next = { name: segment, path: acc, children: [] }
75
+ current.children.push(next)
76
+ }
77
+ if (i === segments.length - 1)
78
+ next.item = item
79
+ current = next
80
+ })
81
+ }
82
+
83
+ if (flatten) {
84
+ for (const child of root.children)
85
+ flattenChain(child, separator)
86
+ }
87
+
88
+ return root.children
89
+ }