@rokkit/ui 1.0.3 → 1.0.4
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Data driven UI components for Rokkit applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"dropdown"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@rokkit/core": "1.0.
|
|
47
|
-
"@rokkit/data": "1.0.
|
|
48
|
-
"@rokkit/states": "1.0.
|
|
49
|
-
"@rokkit/actions": "1.0.
|
|
46
|
+
"@rokkit/core": "1.0.4",
|
|
47
|
+
"@rokkit/data": "1.0.4",
|
|
48
|
+
"@rokkit/states": "1.0.4",
|
|
49
|
+
"@rokkit/actions": "1.0.4",
|
|
50
50
|
"marked": "^15.0.0",
|
|
51
51
|
"dompurify": "^3.0.0"
|
|
52
52
|
},
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
if (!crossfilterWrapper) return tokens.map((t) => ({ type: 'token', token: t }))
|
|
57
57
|
|
|
58
58
|
const result: Segment[] = []
|
|
59
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
59
60
|
const groupMap = new Map<string, { type: 'group'; id: string; items: Token[] }>()
|
|
60
61
|
|
|
61
62
|
for (const token of tokens) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { isIconClass } from '@rokkit/core'
|
|
2
3
|
const { proxy, showIcon = true, showSubtext = true } = $props()
|
|
3
4
|
</script>
|
|
4
5
|
|
|
@@ -6,7 +7,11 @@
|
|
|
6
7
|
{#if proxy.get('avatar')}
|
|
7
8
|
<img data-item-avatar src={proxy.get('avatar')} alt={proxy.get('tooltip') ?? proxy.label} />
|
|
8
9
|
{:else if proxy.get('icon')}
|
|
9
|
-
|
|
10
|
+
{#if isIconClass(proxy.get('icon'))}
|
|
11
|
+
<span data-item-icon class={proxy.get('icon')} aria-hidden="true"></span>
|
|
12
|
+
{:else}
|
|
13
|
+
<span data-item-icon-literal aria-hidden="true">{proxy.get('icon')}</span>
|
|
14
|
+
{/if}
|
|
10
15
|
{/if}
|
|
11
16
|
{/if}
|
|
12
17
|
{#if proxy.label || (showSubtext && proxy.get('subtext'))}
|
package/src/utils/palette.ts
CHANGED
|
@@ -20,6 +20,14 @@ export interface HSL {
|
|
|
20
20
|
l: number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface OKLCH {
|
|
24
|
+
L: number
|
|
25
|
+
C: number
|
|
26
|
+
H: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ColorSpace = 'rgb' | 'hsl' | 'oklch'
|
|
30
|
+
|
|
23
31
|
export type ShadeKey =
|
|
24
32
|
| '50'
|
|
25
33
|
| '100'
|
|
@@ -163,6 +171,106 @@ export function hslToHex(hsl: HSL): string {
|
|
|
163
171
|
return rgbToHex(hslToRgb(hsl))
|
|
164
172
|
}
|
|
165
173
|
|
|
174
|
+
// =============================================================================
|
|
175
|
+
// OKLCH Conversion Utilities
|
|
176
|
+
// =============================================================================
|
|
177
|
+
|
|
178
|
+
function srgbToLinear(c: number): number {
|
|
179
|
+
const s = c / 255
|
|
180
|
+
return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function linearToSrgb(c: number): number {
|
|
184
|
+
const s = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055
|
|
185
|
+
return Math.round(Math.max(0, Math.min(255, s * 255)))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// CSS Color Level 4 matrices (via XYZ D65) for round-trip precision.
|
|
189
|
+
const SRGB_TO_XYZ: [number, number, number][] = [
|
|
190
|
+
[0.4123907993, 0.3575843394, 0.1804807884],
|
|
191
|
+
[0.2126390059, 0.7151686788, 0.0721923154],
|
|
192
|
+
[0.0193308187, 0.1191947798, 0.9505321522]
|
|
193
|
+
]
|
|
194
|
+
const XYZ_TO_LMS: [number, number, number][] = [
|
|
195
|
+
[0.8189330101, 0.3618667424, -0.1288597137],
|
|
196
|
+
[0.0329845436, 0.9293118715, 0.0361456387],
|
|
197
|
+
[0.0482003018, 0.2643662691, 0.633851707]
|
|
198
|
+
]
|
|
199
|
+
const LMS3_TO_OKLAB: [number, number, number][] = [
|
|
200
|
+
[0.2104542553, 0.793617785, -0.0040720468],
|
|
201
|
+
[1.9779984951, -2.428592205, 0.4505937099],
|
|
202
|
+
[0.0259040371, 0.7827717662, -0.808675766]
|
|
203
|
+
]
|
|
204
|
+
const OKLAB_TO_LMS3: [number, number, number][] = [
|
|
205
|
+
[1.0, 0.3963377774, 0.2158037573],
|
|
206
|
+
[1.0, -0.1055613458, -0.0638541728],
|
|
207
|
+
[1.0, -0.0894841775, -1.291485548]
|
|
208
|
+
]
|
|
209
|
+
const LMS_TO_XYZ: [number, number, number][] = [
|
|
210
|
+
[1.2270138511, -0.5577999807, 0.281256149],
|
|
211
|
+
[-0.0405801784, 1.1122568696, -0.0716766787],
|
|
212
|
+
[-0.0763812845, -0.4214819784, 1.5861632204]
|
|
213
|
+
]
|
|
214
|
+
const XYZ_TO_SRGB: [number, number, number][] = [
|
|
215
|
+
[3.2409699419, -1.5373831776, -0.4986107603],
|
|
216
|
+
[-0.9692436363, 1.8759675015, 0.0415550574],
|
|
217
|
+
[0.0556300797, -0.2039769589, 1.0569715142]
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
function matMul(m: [number, number, number][], v: number[]): number[] {
|
|
221
|
+
return m.map((row) => row[0] * v[0] + row[1] * v[1] + row[2] * v[2])
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Convert RGB to OKLCH (via XYZ D65 for precision)
|
|
226
|
+
*/
|
|
227
|
+
export function rgbToOklch(rgb: RGB): OKLCH {
|
|
228
|
+
const lin = [srgbToLinear(rgb.r), srgbToLinear(rgb.g), srgbToLinear(rgb.b)]
|
|
229
|
+
const xyz = matMul(SRGB_TO_XYZ, lin)
|
|
230
|
+
const lms = matMul(XYZ_TO_LMS, xyz)
|
|
231
|
+
const lms3 = [Math.cbrt(lms[0]), Math.cbrt(lms[1]), Math.cbrt(lms[2])]
|
|
232
|
+
const [L, a, b] = matMul(LMS3_TO_OKLAB, lms3)
|
|
233
|
+
|
|
234
|
+
const C = Math.sqrt(a * a + b * b)
|
|
235
|
+
let H = (Math.atan2(b, a) * 180) / Math.PI
|
|
236
|
+
if (H < 0) H += 360
|
|
237
|
+
|
|
238
|
+
return { L, C, H }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Convert OKLCH to RGB (via XYZ D65 for precision)
|
|
243
|
+
*/
|
|
244
|
+
export function oklchToRgb(oklch: OKLCH): RGB {
|
|
245
|
+
const hRad = (oklch.H * Math.PI) / 180
|
|
246
|
+
const lab = [oklch.L, oklch.C * Math.cos(hRad), oklch.C * Math.sin(hRad)]
|
|
247
|
+
|
|
248
|
+
const lms3 = matMul(OKLAB_TO_LMS3, lab)
|
|
249
|
+
const lms = [lms3[0] ** 3, lms3[1] ** 3, lms3[2] ** 3]
|
|
250
|
+
const xyz = matMul(LMS_TO_XYZ, lms)
|
|
251
|
+
const lin = matMul(XYZ_TO_SRGB, xyz)
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
r: linearToSrgb(lin[0]),
|
|
255
|
+
g: linearToSrgb(lin[1]),
|
|
256
|
+
b: linearToSrgb(lin[2])
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Convert hex to OKLCH
|
|
262
|
+
*/
|
|
263
|
+
export function hexToOklch(hex: string): OKLCH {
|
|
264
|
+
return rgbToOklch(hexToRgb(hex))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Convert OKLCH to hex
|
|
269
|
+
*/
|
|
270
|
+
export function oklchToHex(oklch: OKLCH): string {
|
|
271
|
+
return rgbToHex(oklchToRgb(oklch))
|
|
272
|
+
}
|
|
273
|
+
|
|
166
274
|
// =============================================================================
|
|
167
275
|
// Shade Generation
|
|
168
276
|
// =============================================================================
|
|
@@ -523,13 +631,38 @@ export function getShades(color: string): Shades {
|
|
|
523
631
|
// Runtime Palette Application
|
|
524
632
|
// =============================================================================
|
|
525
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Format a hex color as CSS variable components for the given color space.
|
|
636
|
+
*/
|
|
637
|
+
function formatComponents(hex: string, colorSpace: ColorSpace): string {
|
|
638
|
+
switch (colorSpace) {
|
|
639
|
+
case 'hsl': {
|
|
640
|
+
const hsl = hexToHsl(hex)
|
|
641
|
+
return `${Math.round(hsl.h)} ${Math.round(hsl.s)}% ${Math.round(hsl.l)}%`
|
|
642
|
+
}
|
|
643
|
+
case 'oklch': {
|
|
644
|
+
const oklch = hexToOklch(hex)
|
|
645
|
+
const L = Math.round(oklch.L * 10000) / 10000
|
|
646
|
+
const C = Math.round(oklch.C * 10000) / 10000
|
|
647
|
+
const H = Math.round(oklch.H * 100) / 100
|
|
648
|
+
return `${L} ${C} ${H}`
|
|
649
|
+
}
|
|
650
|
+
case 'rgb':
|
|
651
|
+
default: {
|
|
652
|
+
const rgb = hexToRgb(hex)
|
|
653
|
+
return `${rgb.r},${rgb.g},${rgb.b}`
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
526
658
|
/**
|
|
527
659
|
* Apply a color palette to the document root
|
|
528
660
|
* Sets CSS variables for each color role and shade
|
|
529
661
|
*/
|
|
530
662
|
export function applyPalette(
|
|
531
663
|
mapping: Partial<Record<ColorRole, string>>,
|
|
532
|
-
element: HTMLElement = document.documentElement
|
|
664
|
+
element: HTMLElement = document.documentElement,
|
|
665
|
+
colorSpace: ColorSpace = 'rgb'
|
|
533
666
|
): void {
|
|
534
667
|
for (const [role, color] of Object.entries(mapping)) {
|
|
535
668
|
if (!color) continue
|
|
@@ -537,14 +670,11 @@ export function applyPalette(
|
|
|
537
670
|
const shades = getShades(color)
|
|
538
671
|
|
|
539
672
|
for (const [shade, hex] of Object.entries(shades)) {
|
|
540
|
-
|
|
541
|
-
// Store as RGB values (comma-separated) for rgba() compatibility
|
|
542
|
-
element.style.setProperty(`--color-${role}-${shade}`, `${rgb.r},${rgb.g},${rgb.b}`)
|
|
673
|
+
element.style.setProperty(`--color-${role}-${shade}`, formatComponents(hex, colorSpace))
|
|
543
674
|
}
|
|
544
675
|
|
|
545
676
|
// Also set the default (500) as the base color variable
|
|
546
|
-
|
|
547
|
-
element.style.setProperty(`--color-${role}`, `${baseRgb.r},${baseRgb.g},${baseRgb.b}`)
|
|
677
|
+
element.style.setProperty(`--color-${role}`, formatComponents(shades['500'], colorSpace))
|
|
548
678
|
}
|
|
549
679
|
}
|
|
550
680
|
|