@rimelight/ui 0.0.43 → 0.0.44
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 +14 -12
- package/package.json +11 -14
- package/src/components/avatar/RLAAvatar.astro +4 -12
- package/src/components/badge/RLABadge.astro +2 -1
- package/src/components/badge/badge.theme.ts +0 -4
- package/src/components/banner/RLABanner.astro +1 -1
- package/src/components/banner/banner.theme.ts +1 -1
- package/src/components/breadcrumb/RLABreadcrumb.astro +2 -2
- package/src/components/button/RLAButton.astro +4 -1
- package/src/components/button/button.theme.ts +1 -5
- package/src/components/callout/RLACallout.astro +7 -5
- package/src/components/callout/callout.theme.ts +1 -5
- package/src/components/card/card.theme.ts +3 -3
- package/src/components/carousel/RLACarousel.astro +2 -2
- package/src/components/carousel/carousel.theme.ts +2 -2
- package/src/components/chart/RLAChart.astro +1 -1
- package/src/components/checkbox/RLACheckbox.astro +2 -1
- package/src/components/checkbox/checkbox.theme.ts +0 -4
- package/src/components/chip/RLAChip.astro +2 -1
- package/src/components/chip/chip.theme.ts +0 -4
- package/src/components/collapsible/RLACollapsible.astro +1 -1
- package/src/components/color-area/RLAColorArea.astro +1 -0
- package/src/components/color-area/color-area.theme.ts +1 -5
- package/src/components/color-field/RLAColorField.astro +1 -0
- package/src/components/color-field/color-field.theme.ts +1 -5
- package/src/components/color-slider/RLAColorSlider.astro +1 -0
- package/src/components/color-slider/color-slider.theme.ts +1 -5
- package/src/components/dashboard-group/RLADashboardGroup.astro +5 -7
- package/src/components/dashboard-group/dashboard-group.theme.ts +1 -1
- package/src/components/dashboard-group/dashboard-group.ts +1 -1
- package/src/components/dashboard-navbar/RLADashboardNavbar.astro +4 -4
- package/src/components/dashboard-navbar/dashboard-navbar.theme.ts +3 -3
- package/src/components/dashboard-navbar/dashboard-navbar.ts +1 -1
- package/src/components/dashboard-panel/RLADashboardPanel.astro +9 -8
- package/src/components/dashboard-panel/dashboard-panel.theme.ts +1 -1
- package/src/components/dashboard-sidebar/RLADashboardSidebar.astro +7 -4
- package/src/components/dashboard-sidebar/dashboard-sidebar.theme.ts +6 -5
- package/src/components/dashboard-sidebar-collapse/RLADashboardSidebarCollapse.astro +1 -0
- package/src/components/drawer/RLADrawer.astro +1 -1
- package/src/components/dropdown-menu/dropdown-menu.theme.ts +2 -2
- package/src/components/footer/RLAFooter.astro +2 -0
- package/src/components/footer/footer.theme.ts +0 -8
- package/src/components/gamepad/RLAGamepad.astro +1 -1
- package/src/components/head/UIHead.astro +1 -2
- package/src/components/header/header.theme.ts +1 -1
- package/src/components/icon/RLAIcon.astro +2 -5
- package/src/components/icon/RLSIcon.tsx +2 -2
- package/src/components/icon/RLVIcon.vue +18 -6
- package/src/components/icon/icon.theme.ts +1 -1
- package/src/components/image/image.theme.ts +1 -1
- package/src/components/input/RLAInput.astro +2 -1
- package/src/components/input/input.theme.ts +0 -4
- package/src/components/input-menu/RLAInputMenu.astro +2 -2
- package/src/components/input-menu/input-menu.theme.ts +1 -1
- package/src/components/input-rating/input-rating.theme.ts +2 -2
- package/src/components/kbd/RLAKbd.astro +2 -1
- package/src/components/kbd/kbd.theme.ts +0 -4
- package/src/components/keyboard/RLAKeyboard.astro +1 -1
- package/src/components/link/RLALink.astro +1 -0
- package/src/components/link/link.theme.ts +0 -4
- package/src/components/link-group/RLALinkGroup.astro +1 -1
- package/src/components/link-group/link-group.theme.ts +1 -10
- package/src/components/locale-selector/RLALocaleSelector.astro +4 -36
- package/src/components/locale-selector/locale-selector.theme.ts +3 -11
- package/src/components/logo/RLALogo.astro +1 -2
- package/src/components/modal/RLAModal.astro +1 -1
- package/src/components/modal/RLSModal.tsx +2 -2
- package/src/components/navigation-menu/RLANavigationMenu.astro +17 -21
- package/src/components/navigation-menu/navigation-menu.theme.ts +3 -7
- package/src/components/pagination/RLAPagination.astro +2 -2
- package/src/components/pagination/pagination.theme.ts +1 -1
- package/src/components/popover/RLAPopover.astro +39 -0
- package/src/components/progress/RLAProgress.astro +2 -0
- package/src/components/progress/progress.theme.ts +0 -4
- package/src/components/scroll-area/scroll-area.theme.ts +1 -1
- package/src/components/scroll-to-top/RLAScrollToTop.astro +6 -5
- package/src/components/scroll-to-top/RLVScrollToTop.vue +7 -7
- package/src/components/scroll-to-top/scroll-to-top.theme.ts +3 -7
- package/src/components/search/RLASearch.astro +3 -3
- package/src/components/select/RLASelect.astro +96 -528
- package/src/components/select/RLSSelect.tsx +68 -14
- package/src/components/select/RLVSelect.vue +57 -0
- package/src/components/select/select.theme.ts +11 -96
- package/src/components/shortcuts/RLAShortcuts.astro +9 -9
- package/src/components/sidebar/RLASidebar.astro +3 -3
- package/src/components/slideover/RLASlideover.astro +1 -1
- package/src/components/slider/RLASlider.astro +2 -1
- package/src/components/slider/slider.theme.ts +1 -5
- package/src/components/spinner/RLASpinner.astro +2 -1
- package/src/components/spinner/spinner.theme.ts +1 -5
- package/src/components/splitter/splitter.theme.ts +1 -1
- package/src/components/stepper/RLAStepper.astro +8 -8
- package/src/components/stepper/stepper.theme.ts +6 -7
- package/src/components/steps/RLASteps.astro +3 -3
- package/src/components/switch/RLASwitch.astro +2 -1
- package/src/components/switch/switch.theme.ts +0 -4
- package/src/components/table/table.theme.ts +1 -1
- package/src/components/table-of-contents/RLATableOfContents.astro +5 -5
- package/src/components/theme-selector/RLAThemeSelector.astro +16 -14
- package/src/components/theme-selector/theme-selector.theme.ts +2 -2
- package/src/components/toast/RLAToast.astro +6 -6
- package/src/components/toast/store.ts +1 -1
- package/src/components/tooltip/tooltip.theme.ts +1 -1
- package/src/icons.raw.ts +67 -0
- package/src/icons.ts +31 -64
- package/src/index.ts +19 -18
- package/src/preset.ts +110 -108
- package/src/resolver.ts +127 -15
- package/src/shortcuts.ts +103 -84
- package/src/stores.ts +22 -0
- package/src/types.ts +27 -9
- package/src/virtual.d.ts +4 -0
- package/src/components/editor/PageEditor.vue +0 -459
- package/src/components/editor/PageMention.vue +0 -87
- package/src/components/editor/PagePropertiesEditor.vue +0 -358
- package/src/components/editor/PagePropertiesRenderer.vue +0 -285
- package/src/components/editor/PageRenderer.vue +0 -155
- package/src/components/editor/PageSurround.vue +0 -130
- package/src/components/editor/PageVersionSelector.vue +0 -371
- package/src/components/editor/blocks/AddBlockModal.vue +0 -196
- package/src/components/editor/blocks/Block.vue +0 -142
- package/src/components/editor/blocks/BlockEditRenderer.vue +0 -26
- package/src/components/editor/blocks/BlockEditor.vue +0 -141
- package/src/components/editor/blocks/BlockViewRenderer.vue +0 -71
- package/src/components/editor/blocks/TextRenderer.vue +0 -31
- package/src/components/editor/blocks/editor/CalloutBlockEditor.vue +0 -53
- package/src/components/editor/blocks/editor/ImageBlockEditor.vue +0 -55
- package/src/components/editor/blocks/editor/ParagraphBlockEditor.vue +0 -31
- package/src/components/editor/blocks/editor/SectionBlockEditor.vue +0 -65
- package/src/components/editor/blocks/nodes/LinkNode.vue +0 -28
- package/src/components/editor/blocks/nodes/TextNode.vue +0 -22
- package/src/components/editor/blocks/renderer/CalloutBlockRenderer.vue +0 -65
- package/src/components/editor/blocks/renderer/ImageBlockRenderer.vue +0 -20
- package/src/components/editor/blocks/renderer/ParagraphBlockRenderer.vue +0 -21
- package/src/components/editor/blocks/renderer/SectionBlockRenderer.vue +0 -55
- package/src/components/editor/composables/injectionKeys.ts +0 -3
- package/src/components/editor/composables/useBlockEditor.ts +0 -261
- package/src/components/editor/composables/usePageEditor.ts +0 -97
- package/src/components/editor/modals/CreatePageModal.vue +0 -160
- package/src/components/editor/modals/DeletePageModal.vue +0 -116
- package/src/components/editor/modals/PageTreeModal.vue +0 -275
- package/src/components/editor/types/blocks.ts +0 -100
- package/src/components/editor/types/pages.ts +0 -89
- package/src/components/editor/types/schemas.ts +0 -36
- package/src/components/editor/utils/blocks.ts +0 -42
- package/src/components/editor/utils/editorHelpers.ts +0 -24
- package/src/components/editor/utils/page.ts +0 -219
- package/src/components/editor/utils/richTextHelpers.ts +0 -35
- package/src/components/editor/views/PageEditView.vue +0 -195
- package/src/components/editor/views/PageLiveView.vue +0 -90
- package/src/components/editor/views/PageReviewView.vue +0 -308
- package/src/components/icon/resolver.ts +0 -23
package/src/preset.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { presetIcons, presetTypography, presetWind4, type Preset } from "unocss"
|
|
2
|
-
import type { ColorModeInput,
|
|
2
|
+
import type { ColorModeInput, UIConfig } from "./types"
|
|
3
|
+
import { getIconSvg } from "./icons.raw"
|
|
3
4
|
|
|
4
|
-
export const
|
|
5
|
+
export const PALETTES = {
|
|
5
6
|
neutral: [0, 0],
|
|
6
7
|
primary: [0.21, 260],
|
|
7
8
|
secondary: [0.18, 70],
|
|
@@ -14,132 +15,133 @@ export const P = {
|
|
|
14
15
|
source: [0.13, 215]
|
|
15
16
|
} as const
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
18
|
+
const SHADES = {
|
|
19
|
+
"50": [0.985, 0.1, 0.145, 0.2],
|
|
20
|
+
"100": [0.965, 0.25, 0.2, 0.35],
|
|
21
|
+
"200": [0.925, 0.45, 0.28, 0.55],
|
|
22
|
+
"300": [0.85, 0.7, 0.35, 0.75],
|
|
23
|
+
"400": [0.72, 0.9, 0.44, 0.9],
|
|
24
|
+
"500": [0.55, 1, 0.53, 1],
|
|
25
|
+
"600": [0.44, 0.9, 0.61, 0.85],
|
|
26
|
+
"700": [0.35, 0.75, 0.69, 0.7],
|
|
27
|
+
"800": [0.27, 0.6, 0.77, 0.55],
|
|
28
|
+
"900": [0.2, 0.45, 0.86, 0.4],
|
|
29
|
+
"950": [0.145, 0.3, 0.93, 0.25]
|
|
30
|
+
} as const
|
|
31
|
+
|
|
32
|
+
const SHADE_ENTRIES = Object.entries(SHADES)
|
|
33
|
+
|
|
34
|
+
const INTENTS: Record<string, Record<string, string>> = {
|
|
35
|
+
default: { text: "700", bg: "50", border: "200", ring: "300" },
|
|
36
|
+
muted: { text: "600", bg: "100", border: "200", ring: "300" },
|
|
37
|
+
elevated: { text: "800", bg: "100", border: "200", ring: "400" },
|
|
38
|
+
accented: { text: "900", bg: "200", border: "300", ring: "500" },
|
|
39
|
+
dimmed: { text: "500", bg: "50", border: "300", ring: "400" },
|
|
40
|
+
toned: { text: "800", bg: "700", border: "600", ring: "900" },
|
|
41
|
+
highlighted: { text: "950", bg: "200", border: "800", ring: "950" },
|
|
42
|
+
inverted: { text: "50", bg: "950", border: "800", ring: "700" }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const createThemeShades = (name: string) => ({
|
|
46
|
+
DEFAULT: `var(--rl-${name}-500)`,
|
|
47
|
+
...Object.fromEntries(SHADE_ENTRIES.map(([s]) => [s, `var(--rl-${name}-${s})`]))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const createOklchShadeGetter = ([c, h]: readonly [number, number]) => {
|
|
51
|
+
const oklch = (l: number, factor: number) => `oklch(${l} ${(factor * c).toFixed(3)} ${h})`
|
|
52
|
+
return (_: string, [l1, c1, l2, c2]: readonly [number, number, number, number]) => [
|
|
53
|
+
oklch(l1, c1),
|
|
54
|
+
oklch(l2, c2)
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const createCustomShadeGetter = (val: any) => {
|
|
59
|
+
const isSplit = "light" in val || "dark" in val
|
|
60
|
+
const light = (isSplit ? val.light : val) ?? {}
|
|
61
|
+
const dark = (isSplit ? val.dark : light) ?? light
|
|
62
|
+
const fallback = light["500"] ?? Object.values(light)[0] ?? "oklch(0.55 0.15 0)"
|
|
63
|
+
return (s: string) => [light[s] ?? fallback, dark[s] ?? fallback]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveTokens(custom: Record<string, ColorModeInput> = {}) {
|
|
67
|
+
const themeColors: Record<string, Record<string, string>> = {}
|
|
68
|
+
const tokenVarLines: string[] = []
|
|
69
|
+
|
|
70
|
+
for (const [name, val] of Object.entries({ ...PALETTES, ...custom })) {
|
|
71
|
+
themeColors[name] = createThemeShades(name)
|
|
72
|
+
const getShade = Array.isArray(val) ? createOklchShadeGetter(val) : createCustomShadeGetter(val)
|
|
73
|
+
|
|
74
|
+
for (const [s, coords] of SHADE_ENTRIES) {
|
|
75
|
+
const [light, dark] = getShade(s, coords as any)
|
|
76
|
+
tokenVarLines.push(`--rl-${name}-${s}: light-dark(${light}, ${dark});`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { themeColors, tokenVarLines }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default function rimelightUiPreset({ colors, icons, presets = {} }: UIConfig = {}): Preset {
|
|
84
|
+
const { themeColors, tokenVarLines } = resolveTokens(colors)
|
|
61
85
|
|
|
62
|
-
export default function rimelightUiPreset(options: PresetOptions = {}): Preset {
|
|
63
|
-
const allColors = Object.keys({ ...P, ...options.colors })
|
|
64
86
|
return {
|
|
65
87
|
name: "rimelight-ui",
|
|
66
88
|
outputToCssLayers: true,
|
|
67
89
|
safelist: [
|
|
68
|
-
...
|
|
69
|
-
...Object.values(
|
|
90
|
+
...Object.keys(themeColors).map((c) => `rl-color-${c}`),
|
|
91
|
+
...Object.values(icons ?? {}).filter(
|
|
70
92
|
(v): v is string => typeof v === "string" && v.startsWith("i-")
|
|
71
93
|
)
|
|
72
94
|
],
|
|
73
95
|
presets: [
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
presets.wind !== false && presetWind4(),
|
|
97
|
+
presets.typography !== false && presetTypography(),
|
|
98
|
+
presets.icons !== false && presetIcons({ collections: { rl: getIconSvg } }),
|
|
99
|
+
{
|
|
100
|
+
name: "rimelight-theme",
|
|
101
|
+
theme: {
|
|
102
|
+
spacing: { md: "1rem" },
|
|
103
|
+
colors: themeColors
|
|
104
|
+
}
|
|
105
|
+
}
|
|
77
106
|
].filter(Boolean) as Preset[],
|
|
107
|
+
shortcuts: [
|
|
108
|
+
[
|
|
109
|
+
/^(text|bg|ring|border)-([a-z]+)(?:\/(\d+))?$/,
|
|
110
|
+
([, p, n, o]) => {
|
|
111
|
+
const s = INTENTS[n!]?.[p!]
|
|
112
|
+
if (!s) return
|
|
113
|
+
const base = `${p}-neutral-${s}${o ? `/${o}` : ""}`
|
|
114
|
+
return p === "bg" && n === "inverted" ? `${base} text-neutral-50` : base
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
],
|
|
78
118
|
rules: [
|
|
79
119
|
[
|
|
80
120
|
/^rl-color-([\w-]+)$/,
|
|
81
|
-
([, c]) =>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"--rl-border": `var(--color-${c}-500)`,
|
|
85
|
-
"--rl-bg-hover": `var(--color-${c}-600)`,
|
|
86
|
-
"--rl-text": `var(--color-${c}-600)`
|
|
87
|
-
})
|
|
88
|
-
],
|
|
89
|
-
[
|
|
90
|
-
/^(text|bg|ring|border|outline|divide)-([a-z]+)(?:\/(\d+))?$/,
|
|
91
|
-
([, p, n, o]) => {
|
|
92
|
-
const prop = CSS_PROPS[p!],
|
|
93
|
-
s = INTENTS[n!]?.[p === "divide" ? "border" : p === "outline" ? "ring" : p!]
|
|
94
|
-
if (!prop || !s) return
|
|
95
|
-
const b = `var(--color-neutral-${s})`
|
|
121
|
+
([, c]) => {
|
|
122
|
+
const v500 = `var(--rl-${c}-500)`
|
|
123
|
+
const v600 = `var(--rl-${c}-600)`
|
|
96
124
|
return {
|
|
97
|
-
|
|
98
|
-
|
|
125
|
+
"--rl-bg": v500,
|
|
126
|
+
"--rl-ring": v500,
|
|
127
|
+
"--rl-border": v500,
|
|
128
|
+
"--rl-bg-hover": v600,
|
|
129
|
+
"--rl-text": v600
|
|
99
130
|
}
|
|
100
131
|
}
|
|
101
|
-
],
|
|
102
|
-
[
|
|
103
|
-
/^rl-select$/,
|
|
104
|
-
() => `
|
|
105
|
-
select.rl-select, select.rl-select::picker(select) { appearance: base-select; }
|
|
106
|
-
select.rl-select::picker-icon { display: none; }
|
|
107
|
-
select.rl-select::picker(select) { border: 1px solid var(--color-neutral-200, rgba(255, 255, 255, 0.1)); border-radius: 0.5rem; background-color: var(--color-neutral-100, #18181b); color: var(--color-neutral-950, #f4f4f5); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.5); padding: 0.25rem; margin-top: 0.25rem; }
|
|
108
|
-
select.rl-select option { padding: 0.375rem 0.625rem; border-radius: 0.375rem; background-color: var(--color-neutral-100, #18181b); color: var(--color-neutral-950, #f4f4f5); font-size: 0.875rem; cursor: pointer; transition: background-color 0.15s ease, color 0.15s ease; }
|
|
109
|
-
select.rl-select option:hover, select.rl-select option:focus { background-color: var(--color-neutral-200, #27272a); color: var(--color-neutral-950, #ffffff); }
|
|
110
|
-
select.rl-select option:checked { background-color: var(--color-primary-500, #3b82f6); color: #ffffff; font-weight: 500; }
|
|
111
|
-
`
|
|
112
132
|
]
|
|
113
133
|
],
|
|
114
|
-
theme: {
|
|
115
|
-
spacing: Object.fromEntries(
|
|
116
|
-
"6xs:0.125,5xs:0.25,4xs:0.375,3xs:0.5,2xs:0.625,xs:0.75,sm:0.875,md:1,lg:1.25,xl:1.5,2xl:2,3xl:3,4xl:4,5xl:6,6xl:8"
|
|
117
|
-
.split(",")
|
|
118
|
-
.map((s) => s.split(":") as [string, string])
|
|
119
|
-
.map(([k, v]) => [k, `${v}rem`])
|
|
120
|
-
),
|
|
121
|
-
radius: Object.fromEntries(
|
|
122
|
-
"xs:- 0.375,sm:- 0.25,md:- 0.125,lg:,xl:+ 0.25,2xl:+ 0.5,3xl:+ 1"
|
|
123
|
-
.split(",")
|
|
124
|
-
.map((s) => s.split(":") as [string, string])
|
|
125
|
-
.map(([k, v]) => [k, v ? `calc(var(--radius) ${v}rem)` : "var(--radius)"])
|
|
126
|
-
),
|
|
127
|
-
colors: Object.fromEntries(
|
|
128
|
-
allColors.map((c) => [
|
|
129
|
-
c,
|
|
130
|
-
{
|
|
131
|
-
DEFAULT: `var(--${c})`,
|
|
132
|
-
foreground: `var(--${c}-foreground)`,
|
|
133
|
-
accent: `var(--${c}-accent)`,
|
|
134
|
-
...Object.fromEntries(S.map(([s]) => [s, `var(--color-${c}-${s})`]))
|
|
135
|
-
}
|
|
136
|
-
])
|
|
137
|
-
)
|
|
138
|
-
},
|
|
139
134
|
preflights: [
|
|
140
135
|
{
|
|
141
|
-
getCSS: () =>
|
|
142
|
-
|
|
136
|
+
getCSS: () => `:root {
|
|
137
|
+
color-scheme: light dark;
|
|
138
|
+
--rl-header-height: 4rem;
|
|
139
|
+
--rl-container: 90rem;
|
|
140
|
+
${tokenVarLines.join("\n ")}
|
|
141
|
+
}
|
|
142
|
+
[data-theme="dark"] { color-scheme: dark; }
|
|
143
|
+
[data-theme="light"] { color-scheme: light; }
|
|
144
|
+
body { background-color: var(--rl-neutral-50); color: var(--rl-neutral-950); }`
|
|
143
145
|
}
|
|
144
146
|
]
|
|
145
147
|
}
|
package/src/resolver.ts
CHANGED
|
@@ -1,20 +1,132 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import uiConfig from "virtual:rimelight/ui"
|
|
2
|
+
import type { ClassValue, ThemeConfig } from "./types"
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export function cx(...args: ClassValue[]): string {
|
|
5
|
+
let str = ""
|
|
6
|
+
for (let i = 0; i < args.length; i++) {
|
|
7
|
+
const val = args[i]
|
|
8
|
+
if (!val) continue
|
|
9
|
+
if (typeof val === "string" || typeof val === "number") {
|
|
10
|
+
str += (str ? " " : "") + val
|
|
11
|
+
} else if (Array.isArray(val)) {
|
|
12
|
+
if (val.length) {
|
|
13
|
+
const inner = cx(...val)
|
|
14
|
+
if (inner) str += (str ? " " : "") + inner
|
|
15
|
+
}
|
|
16
|
+
} else if (typeof val === "object") {
|
|
17
|
+
for (const k in val) {
|
|
18
|
+
if (val[k]) str += (str ? " " : "") + k
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return str
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function defineTheme<const S extends string>(name: string, themeConfig: ThemeConfig<S>) {
|
|
26
|
+
const custom = uiConfig?.components?.themes?.[name]
|
|
27
|
+
const slots = (custom?.slots ?? themeConfig.slots ?? ["root" as S]) as readonly S[]
|
|
28
|
+
const rootSlot = (slots.includes("root" as S) ? "root" : slots[0] || "root") as S
|
|
29
|
+
|
|
30
|
+
if (uiConfig?.unstyled) {
|
|
31
|
+
return (props: any = {}): Record<S, string> => {
|
|
32
|
+
const res = {} as Record<S, string>
|
|
33
|
+
const hasClass = props.class || props.className
|
|
34
|
+
for (let i = 0; i < slots.length; i++) {
|
|
35
|
+
const slot = slots[i]!
|
|
36
|
+
const base = slot === rootSlot && hasClass ? cx(props.class, props.className) : ""
|
|
37
|
+
const fn = props.ui?.[slot]
|
|
38
|
+
res[slot] = typeof fn === "function" ? fn(base) : cx(base, fn)
|
|
39
|
+
}
|
|
40
|
+
return res
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const autoColor = (custom?.variants ?? themeConfig.variants)?.color === "auto"
|
|
45
|
+
const defaultVariants = { ...themeConfig.defaultVariants, ...custom?.defaultVariants }
|
|
46
|
+
const compoundVariants = [
|
|
47
|
+
...(themeConfig.compoundVariants ?? []),
|
|
48
|
+
...(custom?.compoundVariants ?? [])
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
const compiledCV = compoundVariants.map((cv) => {
|
|
52
|
+
const { classNames, class: cls, ...rest } = cv
|
|
53
|
+
const matchers: Array<{ key: string; exp: any }> = []
|
|
54
|
+
for (const k in rest) {
|
|
55
|
+
matchers.push({ key: k, exp: rest[k] })
|
|
56
|
+
}
|
|
57
|
+
return { matchers, cls: classNames || cls }
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const baseMap: Record<string, string> = {}
|
|
61
|
+
for (let i = 0; i < slots.length; i++) {
|
|
62
|
+
const s = slots[i]!
|
|
63
|
+
baseMap[s] = cx(themeConfig.base?.[s], custom?.base?.[s])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const variants: Record<string, Record<string, any>> = { ...themeConfig.variants }
|
|
67
|
+
if (custom?.variants) {
|
|
68
|
+
for (const k in custom.variants) variants[k] = { ...variants[k], ...custom.variants[k] }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const variantKeys = Object.keys(variants).filter((k) => !(k === "color" && autoColor))
|
|
5
72
|
|
|
6
|
-
export const defineTheme = <const S extends string>(name: string, theme: any) => {
|
|
7
|
-
const root = (theme.slots.includes("root") ? "root" : theme.slots[0]) as S,
|
|
8
|
-
autoColor = theme.variants?.color === "auto",
|
|
9
|
-
scvFn = scv(autoColor ? { ...theme, variants: { ...theme.variants, color: {} } } : theme),
|
|
10
|
-
globalSlots = uiConfig.components?.themes?.[name]?.slots
|
|
11
73
|
return (props: any = {}): Record<S, string> => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
74
|
+
const res = {} as Record<S, string>
|
|
75
|
+
for (let i = 0; i < slots.length; i++) {
|
|
76
|
+
res[slots[i]!] = baseMap[slots[i]!] || ""
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const values = { ...defaultVariants, ...props }
|
|
80
|
+
const color = autoColor ? (props.color ?? defaultVariants.color) : undefined
|
|
81
|
+
|
|
82
|
+
for (let k = 0; k < variantKeys.length; k++) {
|
|
83
|
+
const key = variantKeys[k]!
|
|
84
|
+
const selected = values[key]
|
|
85
|
+
if (selected != null && variants[key]?.[selected]) {
|
|
86
|
+
const layer = variants[key][selected]
|
|
87
|
+
for (let i = 0; i < slots.length; i++) {
|
|
88
|
+
const s = slots[i]!
|
|
89
|
+
const l = layer[s]
|
|
90
|
+
if (l) res[s] = res[s] ? `${res[s]} ${cx(l)}` : cx(l)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (compiledCV.length) {
|
|
96
|
+
for (let c = 0; c < compiledCV.length; c++) {
|
|
97
|
+
const { matchers, cls } = compiledCV[c]!
|
|
98
|
+
if (!cls) continue
|
|
99
|
+
let match = true
|
|
100
|
+
for (let m = 0; m < matchers.length; m++) {
|
|
101
|
+
const { key, exp } = matchers[m]!
|
|
102
|
+
const actual = values[key]
|
|
103
|
+
if (Array.isArray(exp) ? !exp.includes(actual) : actual !== exp) {
|
|
104
|
+
match = false
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (match) {
|
|
109
|
+
for (let i = 0; i < slots.length; i++) {
|
|
110
|
+
const s = slots[i]!
|
|
111
|
+
const l = cls[s]
|
|
112
|
+
if (l) res[s] = res[s] ? `${res[s]} ${cx(l)}` : cx(l)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < slots.length; i++) {
|
|
119
|
+
const slot = slots[i]!
|
|
120
|
+
let slotClass = res[slot]!
|
|
121
|
+
if (slot === rootSlot) {
|
|
122
|
+
if (color) slotClass = slotClass ? `${slotClass} rl-color-${color}` : `rl-color-${color}`
|
|
123
|
+
const extra = cx(props.class, props.className)
|
|
124
|
+
if (extra) slotClass = slotClass ? `${slotClass} ${extra}` : extra
|
|
125
|
+
}
|
|
126
|
+
const override = props.ui?.[slot]
|
|
127
|
+
res[slot] = typeof override === "function" ? override(slotClass) : cx(slotClass, override)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return res
|
|
19
131
|
}
|
|
20
132
|
}
|
package/src/shortcuts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { atom } from "
|
|
1
|
+
import { atom } from "./stores"
|
|
2
2
|
|
|
3
3
|
export type Handler = (e: KeyboardEvent) => void
|
|
4
4
|
export interface ShortcutConfig {
|
|
@@ -23,86 +23,92 @@ export interface RegisteredShortcut {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
interface Shortcut {
|
|
26
|
-
key: string
|
|
27
26
|
handler: Handler
|
|
28
|
-
mods: number
|
|
29
27
|
usingInput?: boolean | string | undefined
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
const isMac = typeof navigator !== "undefined" && /Mac|iP/.test(navigator.userAgent),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
])
|
|
31
|
+
KEYS: Record<string, string> = {
|
|
32
|
+
ctrl: "Ctrl",
|
|
33
|
+
meta: isMac ? "⌘" : "Ctrl",
|
|
34
|
+
alt: isMac ? "⌥" : "Alt",
|
|
35
|
+
shift: isMac ? "⇧" : "Shift",
|
|
36
|
+
enter: "↵",
|
|
37
|
+
delete: "⌦",
|
|
38
|
+
backspace: "⌫",
|
|
39
|
+
escape: "Esc",
|
|
40
|
+
tab: "⇥",
|
|
41
|
+
space: "Space",
|
|
42
|
+
arrowup: "↑",
|
|
43
|
+
arrowdown: "↓",
|
|
44
|
+
arrowleft: "←",
|
|
45
|
+
arrowright: "→"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const MODS: Record<string, string> = {
|
|
49
|
+
cmd: "meta",
|
|
50
|
+
command: "meta",
|
|
51
|
+
control: "ctrl",
|
|
52
|
+
opt: "alt",
|
|
53
|
+
option: "alt"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const normalizeModifier = (m: string) => MODS[m.toLowerCase()] || m.toLowerCase()
|
|
60
57
|
|
|
61
58
|
export const formatShortcutKeys = (k: string): string[] =>
|
|
62
|
-
k.split(/[_-]/).map((p) =>
|
|
59
|
+
k.split(/[_-]/).map((p) => {
|
|
60
|
+
const norm = normalizeModifier(p)
|
|
61
|
+
return KEYS[norm] ?? (p ? p[0]!.toUpperCase() + p.slice(1) : "")
|
|
62
|
+
})
|
|
63
63
|
|
|
64
64
|
export const activeShortcutsStore = atom<RegisteredShortcut[]>([])
|
|
65
65
|
|
|
66
66
|
const isUsingInput = (el: Element | null): boolean | string => {
|
|
67
|
-
|
|
68
|
-
return
|
|
69
|
-
?
|
|
67
|
+
const target = (el?.shadowRoot?.activeElement || el) as HTMLElement | null
|
|
68
|
+
return target && (/INPUT|TEXTAREA|SELECT/.test(target.tagName) || target.isContentEditable)
|
|
69
|
+
? target.getAttribute("name") || true
|
|
70
70
|
: false
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const isEnabled = (s: Shortcut, input: boolean | string) =>
|
|
74
74
|
s.usingInput === true || (s.usingInput ? input === s.usingInput : !input)
|
|
75
75
|
|
|
76
|
+
const normalizeCombo = (combo: string) => {
|
|
77
|
+
const parts = combo.toLowerCase().split("_")
|
|
78
|
+
let meta = ""
|
|
79
|
+
let ctrl = ""
|
|
80
|
+
let alt = ""
|
|
81
|
+
let shift = ""
|
|
82
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
83
|
+
const m = MODS[parts[i]!] || parts[i]!
|
|
84
|
+
if (m === "meta") meta = "meta_"
|
|
85
|
+
else if (m === "ctrl") ctrl = "ctrl_"
|
|
86
|
+
else if (m === "alt") alt = "alt_"
|
|
87
|
+
else if (m === "shift") shift = "shift_"
|
|
88
|
+
}
|
|
89
|
+
return `${meta}${ctrl}${alt}${shift}${parts[parts.length - 1] || ""}`
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
export function defineShortcuts(
|
|
77
93
|
config: ShortcutsConfig,
|
|
78
94
|
options: ShortcutsOptions = {}
|
|
79
95
|
): () => void {
|
|
80
96
|
if (typeof window === "undefined") return () => {}
|
|
81
97
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
reg: RegisteredShortcut[] = []
|
|
85
|
-
let maxChain = 2
|
|
98
|
+
const map = new Map<string, Shortcut>()
|
|
99
|
+
const reg: RegisteredShortcut[] = []
|
|
86
100
|
|
|
87
101
|
for (const [k, cfg] of Object.entries(config)) {
|
|
88
102
|
if (!cfg) continue
|
|
89
|
-
const obj = typeof cfg === "object" ? cfg : null
|
|
90
|
-
|
|
103
|
+
const obj = typeof cfg === "object" ? cfg : null
|
|
104
|
+
const handler = obj ? obj.handler : cfg
|
|
91
105
|
if (typeof handler !== "function") continue
|
|
92
106
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const nonMods = parts.filter((p) => (MODS[p] ? !(mods |= MODS[p]!) : true)),
|
|
97
|
-
base = chain ? k.toLowerCase() : nonMods.join("_"),
|
|
98
|
-
item: Shortcut = { key: base, handler, mods, usingInput: obj?.usingInput }
|
|
107
|
+
const key =
|
|
108
|
+
k.includes("-") && k !== "-" && !k.includes("_") ? k.toLowerCase() : normalizeCombo(k)
|
|
109
|
+
map.set(key, { handler, usingInput: obj?.usingInput })
|
|
99
110
|
|
|
100
|
-
if (
|
|
101
|
-
maxChain = Math.max(maxChain, parts.length)
|
|
102
|
-
chained.set(base, item)
|
|
103
|
-
} else standard.push(item)
|
|
104
|
-
|
|
105
|
-
if (obj?.label || obj?.description)
|
|
111
|
+
if (obj?.label || obj?.description) {
|
|
106
112
|
reg.push({
|
|
107
113
|
id: `${obj.category || "system"}-${k}`,
|
|
108
114
|
keys: formatShortcutKeys(k),
|
|
@@ -111,62 +117,75 @@ export function defineShortcuts(
|
|
|
111
117
|
description: obj.description,
|
|
112
118
|
order: obj.order
|
|
113
119
|
})
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
const ids = new Set(reg.map((i) => i.id))
|
|
117
|
-
if (ids.size)
|
|
124
|
+
if (ids.size) {
|
|
118
125
|
activeShortcutsStore.set(
|
|
119
126
|
activeShortcutsStore
|
|
120
127
|
.get()
|
|
121
128
|
.filter((i) => !ids.has(i.id))
|
|
122
129
|
.concat(reg)
|
|
123
130
|
)
|
|
131
|
+
}
|
|
124
132
|
|
|
125
|
-
const
|
|
133
|
+
const buffer: string[] = []
|
|
126
134
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
127
135
|
|
|
128
136
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
129
|
-
if (!e.key) return
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
if (!e.key || e.isComposing) return
|
|
138
|
+
const key = e.key === " " ? "space" : e.key.toLowerCase()
|
|
139
|
+
const codeKey = e.code
|
|
140
|
+
? e.code
|
|
141
|
+
.replace(/^Key/, "")
|
|
142
|
+
.replace(/^Digit/, "")
|
|
143
|
+
.toLowerCase()
|
|
144
|
+
: ""
|
|
145
|
+
const input = isUsingInput(typeof document !== "undefined" ? document.activeElement : null)
|
|
146
|
+
|
|
147
|
+
buffer.push(key)
|
|
148
|
+
if (buffer.length > 2) buffer.shift()
|
|
139
149
|
clearTimeout(timer)
|
|
140
150
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
// Chained sequence check (e.g. g-d)
|
|
152
|
+
if (buffer.length === 2) {
|
|
153
|
+
const chainedMatch = map.get(buffer.join("-"))
|
|
154
|
+
if (chainedMatch && isEnabled(chainedMatch, input)) {
|
|
155
|
+
e.preventDefault()
|
|
156
|
+
chainedMatch.handler(e)
|
|
157
|
+
buffer.length = 0
|
|
158
|
+
return
|
|
145
159
|
}
|
|
160
|
+
}
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
// Unified event normalization
|
|
163
|
+
const keysToCheck = Array.from(new Set([key, codeKey].filter(Boolean)))
|
|
164
|
+
for (const k of keysToCheck) {
|
|
165
|
+
const sig = `${e.metaKey ? "meta_" : ""}${e.ctrlKey ? "ctrl_" : ""}${e.altKey ? "alt_" : ""}${e.shiftKey ? "shift_" : ""}${k}`
|
|
166
|
+
const primarySig =
|
|
167
|
+
!isMac && e.ctrlKey && !e.metaKey
|
|
168
|
+
? `meta_${e.altKey ? "alt_" : ""}${e.shiftKey ? "shift_" : ""}${k}`
|
|
169
|
+
: sig
|
|
170
|
+
|
|
171
|
+
const match = map.get(sig) || map.get(primarySig) || map.get(k)
|
|
172
|
+
if (match && isEnabled(match, input)) {
|
|
173
|
+
e.preventDefault()
|
|
174
|
+
match.handler(e)
|
|
175
|
+
buffer.length = 0
|
|
176
|
+
return
|
|
177
|
+
}
|
|
161
178
|
}
|
|
162
179
|
|
|
163
|
-
timer = setTimeout(() => void (
|
|
180
|
+
timer = setTimeout(() => void (buffer.length = 0), options.chainDelay ?? 800)
|
|
164
181
|
}
|
|
165
182
|
|
|
166
183
|
window.addEventListener("keydown", onKeyDown)
|
|
167
184
|
return () => {
|
|
168
185
|
window.removeEventListener("keydown", onKeyDown)
|
|
169
186
|
clearTimeout(timer)
|
|
170
|
-
if (ids.size)
|
|
187
|
+
if (ids.size) {
|
|
188
|
+
activeShortcutsStore.set(activeShortcutsStore.get().filter((i) => !ids.has(i.id)))
|
|
189
|
+
}
|
|
171
190
|
}
|
|
172
191
|
}
|