@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.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/a11y/cli.ts +73 -0
- package/a11y/index.ts +13 -0
- package/a11y/scan.ts +127 -0
- package/components/Action/ActionButton.stories.ts +56 -0
- package/components/Action/ActionButton.vue +57 -0
- package/components/Action/ActionDarkToggle.stories.ts +31 -0
- package/components/Action/ActionDarkToggle.vue +87 -0
- package/components/Action/ActionIconButton.stories.ts +47 -0
- package/components/Action/ActionIconButton.vue +47 -0
- package/components/Display/DisplayAvatar.stories.ts +36 -0
- package/components/Display/DisplayAvatar.vue +58 -0
- package/components/Display/DisplayBadge.stories.ts +31 -0
- package/components/Display/DisplayBadge.vue +98 -0
- package/components/Display/DisplayBytes.stories.ts +28 -0
- package/components/Display/DisplayBytes.vue +30 -0
- package/components/Display/DisplayDate.stories.ts +37 -0
- package/components/Display/DisplayDate.vue +29 -0
- package/components/Display/DisplayDonut.stories.ts +26 -0
- package/components/Display/DisplayDonut.vue +46 -0
- package/components/Display/DisplayDuration.stories.ts +28 -0
- package/components/Display/DisplayDuration.vue +28 -0
- package/components/Display/DisplayFileIcon.stories.ts +27 -0
- package/components/Display/DisplayFileIcon.vue +30 -0
- package/components/Display/DisplayFilePath.stories.ts +30 -0
- package/components/Display/DisplayFilePath.vue +61 -0
- package/components/Display/DisplayKbd.stories.ts +26 -0
- package/components/Display/DisplayKbd.vue +27 -0
- package/components/Display/DisplayKeyValue.stories.ts +56 -0
- package/components/Display/DisplayKeyValue.vue +51 -0
- package/components/Display/DisplayLabel.stories.ts +27 -0
- package/components/Display/DisplayLabel.vue +33 -0
- package/components/Display/DisplayNumber.stories.ts +27 -0
- package/components/Display/DisplayNumber.vue +24 -0
- package/components/Display/DisplayNumberBadge.stories.ts +26 -0
- package/components/Display/DisplayNumberBadge.vue +22 -0
- package/components/Display/DisplayPackageName.stories.ts +26 -0
- package/components/Display/DisplayPackageName.vue +49 -0
- package/components/Display/DisplayProgressBar.stories.ts +29 -0
- package/components/Display/DisplayProgressBar.vue +90 -0
- package/components/Display/DisplayProportionBar.stories.ts +40 -0
- package/components/Display/DisplayProportionBar.vue +43 -0
- package/components/Display/DisplaySafeImage.stories.ts +43 -0
- package/components/Display/DisplaySafeImage.vue +30 -0
- package/components/Display/DisplayStatusPill.stories.ts +34 -0
- package/components/Display/DisplayStatusPill.vue +42 -0
- package/components/Display/DisplayTree.stories.ts +76 -0
- package/components/Display/DisplayTree.vue +102 -0
- package/components/Display/DisplayVersion.stories.ts +25 -0
- package/components/Display/DisplayVersion.vue +21 -0
- package/components/Feedback/FeedbackEmptyState.stories.ts +38 -0
- package/components/Feedback/FeedbackEmptyState.vue +21 -0
- package/components/Feedback/FeedbackLoading.stories.ts +23 -0
- package/components/Feedback/FeedbackLoading.vue +21 -0
- package/components/Feedback/FeedbackSpinner.stories.ts +25 -0
- package/components/Feedback/FeedbackSpinner.vue +22 -0
- package/components/Feedback/FeedbackTip.stories.ts +34 -0
- package/components/Feedback/FeedbackTip.vue +29 -0
- package/components/Feedback/FeedbackToasts.stories.ts +40 -0
- package/components/Feedback/FeedbackToasts.vue +105 -0
- package/components/Form/FormCheckbox.stories.ts +36 -0
- package/components/Form/FormCheckbox.vue +30 -0
- package/components/Form/FormCombobox.stories.ts +35 -0
- package/components/Form/FormCombobox.vue +83 -0
- package/components/Form/FormField.stories.ts +56 -0
- package/components/Form/FormField.vue +36 -0
- package/components/Form/FormNumberInput.stories.ts +47 -0
- package/components/Form/FormNumberInput.vue +85 -0
- package/components/Form/FormRadioGroup.stories.ts +47 -0
- package/components/Form/FormRadioGroup.vue +43 -0
- package/components/Form/FormSearchField.stories.ts +22 -0
- package/components/Form/FormSearchField.vue +32 -0
- package/components/Form/FormSelect.stories.ts +47 -0
- package/components/Form/FormSelect.vue +56 -0
- package/components/Form/FormSwitch.stories.ts +36 -0
- package/components/Form/FormSwitch.vue +26 -0
- package/components/Form/FormTextInput.stories.ts +39 -0
- package/components/Form/FormTextInput.vue +51 -0
- package/components/Form/FormTextarea.stories.ts +47 -0
- package/components/Form/FormTextarea.vue +32 -0
- package/components/Layout/LayoutBreadcrumb.stories.ts +54 -0
- package/components/Layout/LayoutBreadcrumb.vue +54 -0
- package/components/Layout/LayoutCard.stories.ts +31 -0
- package/components/Layout/LayoutCard.vue +21 -0
- package/components/Layout/LayoutDataTable.stories.ts +77 -0
- package/components/Layout/LayoutDataTable.vue +145 -0
- package/components/Layout/LayoutExpandableList.stories.ts +28 -0
- package/components/Layout/LayoutExpandableList.vue +94 -0
- package/components/Layout/LayoutPanelGrids.stories.ts +28 -0
- package/components/Layout/LayoutPanelGrids.vue +26 -0
- package/components/Layout/LayoutSectionBlock.stories.ts +37 -0
- package/components/Layout/LayoutSectionBlock.vue +37 -0
- package/components/Layout/LayoutSideNav.stories.ts +33 -0
- package/components/Layout/LayoutSideNav.vue +48 -0
- package/components/Layout/LayoutSplitPane.stories.ts +44 -0
- package/components/Layout/LayoutSplitPane.vue +30 -0
- package/components/Layout/LayoutTabs.stories.ts +43 -0
- package/components/Layout/LayoutTabs.vue +56 -0
- package/components/Layout/LayoutToolbar.stories.ts +60 -0
- package/components/Layout/LayoutToolbar.vue +28 -0
- package/components/Layout/LayoutVirtualList.stories.ts +30 -0
- package/components/Layout/LayoutVirtualList.vue +82 -0
- package/components/Overlay/OverlayDrawer.stories.ts +47 -0
- package/components/Overlay/OverlayDrawer.vue +58 -0
- package/components/Overlay/OverlayDropdown.stories.ts +25 -0
- package/components/Overlay/OverlayDropdown.vue +30 -0
- package/components/Overlay/OverlayDropdownItem.stories.ts +26 -0
- package/components/Overlay/OverlayDropdownItem.vue +31 -0
- package/components/Overlay/OverlayDropdownLabel.vue +9 -0
- package/components/Overlay/OverlayDropdownSeparator.vue +7 -0
- package/components/Overlay/OverlayModal.stories.ts +33 -0
- package/components/Overlay/OverlayModal.vue +48 -0
- package/components/Overlay/OverlayTooltip.stories.ts +33 -0
- package/components/Overlay/OverlayTooltip.vue +38 -0
- package/composables/colorScheme.ts +58 -0
- package/composables/toast.ts +81 -0
- package/package.json +99 -0
- package/skills/antfu-design/SKILL.md +65 -0
- package/skills/antfu-design/references/advanced-patterns.md +39 -0
- package/skills/antfu-design/references/best-practices.md +54 -0
- package/skills/antfu-design/references/core-components.md +72 -0
- package/skills/antfu-design/references/core-setup.md +56 -0
- package/skills/antfu-design/references/core-tokens.md +100 -0
- package/skills/antfu-design/references/features-data-presentation.md +27 -0
- package/splitpanes.d.ts +70 -0
- package/styles/animations.css +47 -0
- package/styles/base.css +31 -0
- package/styles/floating-vue.css +28 -0
- package/styles/index.css +7 -0
- package/styles/reka-ui.css +112 -0
- package/styles/scrollbar.css +24 -0
- package/styles/splitpanes.css +61 -0
- package/unocss/colors.ts +127 -0
- package/unocss/index.ts +99 -0
- package/unocss/options.ts +31 -0
- package/unocss/patterns.ts +38 -0
- package/unocss/rules.ts +26 -0
- package/unocss/severity.ts +16 -0
- package/unocss/shortcuts.ts +68 -0
- package/utils/color.ts +328 -0
- package/utils/contrast.ts +118 -0
- package/utils/format.ts +389 -0
- package/utils/icon.ts +200 -0
- package/utils/index.ts +13 -0
- package/utils/keybinding.ts +199 -0
- package/utils/misc.ts +141 -0
- package/utils/path.ts +243 -0
- package/utils/semver.ts +147 -0
- package/utils/tree.ts +89 -0
package/utils/semver.ts
ADDED
|
@@ -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
|
+
}
|