@threadlabs/looma 0.14.1 → 0.14.3
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/components/ui-meter/ui-meter.html +186 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-meter.css +171 -0
- package/vanilla/UiMeter.d.ts +20 -0
- package/vanilla/UiMeter.js +28 -0
- package/vanilla/index.js +1 -0
- package/vue/UiMeter.d.ts +20 -0
- package/vue/UiMeter.js +2 -0
- package/vue/UiMeter.vue +279 -0
- package/vue/chunks/UiMeter.js +88 -0
- package/vue/components.css +130 -0
- package/vue/index.d.ts +1 -0
- package/vue/index.js +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/* Generated by HTML Next 1.0.0-alpha.7. Do not edit. */
|
|
2
|
+
@property --ui-meter-inline-size {
|
|
3
|
+
syntax: "*";
|
|
4
|
+
inherits: false;
|
|
5
|
+
}
|
|
6
|
+
@scope ([data-component~="ui-meter"]) to ([data-component], [data-slotted]) {
|
|
7
|
+
/* How far along something is: a track, filled from the start in the tone's colour. The track is a
|
|
8
|
+
quiet border colour, so an empty meter still shows; every tone's fill keeps 3:1 against the
|
|
9
|
+
track and the page. */
|
|
10
|
+
|
|
11
|
+
:scope {
|
|
12
|
+
--_meter-fill: var(--ui-text-muted);
|
|
13
|
+
|
|
14
|
+
display: inline-block;
|
|
15
|
+
flex: none;
|
|
16
|
+
vertical-align: middle;
|
|
17
|
+
inline-size: var(--ui-meter-inline-size, 8rem);
|
|
18
|
+
max-inline-size: 100%;
|
|
19
|
+
block-size: var(--ui-space-2);
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
border-radius: var(--ui-radius-round);
|
|
22
|
+
background: var(--ui-border);
|
|
23
|
+
/* Unseen until forced colours draw it, where the track's own colour is gone. */
|
|
24
|
+
outline: 1px solid transparent;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:scope[data-ui-meter-state~="size=sm"] {
|
|
28
|
+
inline-size: var(--ui-meter-inline-size, 5rem);
|
|
29
|
+
block-size: var(--ui-space-1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.fill {
|
|
33
|
+
display: block;
|
|
34
|
+
block-size: 100%;
|
|
35
|
+
background: var(--_meter-fill);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:scope[data-ui-meter-state~="tone=accent"] {
|
|
39
|
+
--_meter-fill: var(--ui-accent-solid);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:scope[data-ui-meter-state~="tone=info"] {
|
|
43
|
+
--_meter-fill: var(--ui-info-solid);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:scope[data-ui-meter-state~="tone=success"] {
|
|
47
|
+
--_meter-fill: var(--ui-success-solid);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:scope[data-ui-meter-state~="tone=warning"] {
|
|
51
|
+
--_meter-fill: var(--ui-warning-solid);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
:scope[data-ui-meter-state~="tone=danger"] {
|
|
55
|
+
--_meter-fill: var(--ui-danger-solid);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Segments: the track and its fill show through a mask of equal segments with a gap between them.
|
|
59
|
+
A mask over the whole meter, rather than one element per segment, because the expression
|
|
60
|
+
language has no range to render N elements from, and one mask lines the fill up with the track
|
|
61
|
+
for free. Each period is a segment and the gap after it; N periods span the track plus one
|
|
62
|
+
trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
|
|
63
|
+
The count arrives as an attribute, not an inline custom property (which would add a style
|
|
64
|
+
attribute to every meter), so a continuous meter renders exactly as before, and the rules below
|
|
65
|
+
turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
|
|
66
|
+
:scope[data-ui-meter-segments="2"] {
|
|
67
|
+
--_segments: 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
:scope[data-ui-meter-segments="3"] {
|
|
71
|
+
--_segments: 3;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:scope[data-ui-meter-segments="4"] {
|
|
75
|
+
--_segments: 4;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:scope[data-ui-meter-segments="5"] {
|
|
79
|
+
--_segments: 5;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
:scope[data-ui-meter-segments="6"] {
|
|
83
|
+
--_segments: 6;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:scope[data-ui-meter-segments="7"] {
|
|
87
|
+
--_segments: 7;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:scope[data-ui-meter-segments="8"] {
|
|
91
|
+
--_segments: 8;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:scope[data-ui-meter-segments="9"] {
|
|
95
|
+
--_segments: 9;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
:scope[data-ui-meter-segments="10"] {
|
|
99
|
+
--_segments: 10;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
:scope[data-ui-meter-segments="11"] {
|
|
103
|
+
--_segments: 11;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:scope[data-ui-meter-segments="12"] {
|
|
107
|
+
--_segments: 12;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
:scope[data-ui-meter-segments] {
|
|
111
|
+
--_meter-gap: var(--ui-space-1);
|
|
112
|
+
--_meter-period: calc((100% + var(--_meter-gap)) / var(--_segments));
|
|
113
|
+
--_meter-segment: calc(var(--_meter-period) - var(--_meter-gap));
|
|
114
|
+
|
|
115
|
+
mask-image: repeating-linear-gradient(to right, #000 0 var(--_meter-segment), transparent 0 var(--_meter-period));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
:scope[data-ui-meter-state~="size=sm"][data-ui-meter-segments] {
|
|
119
|
+
--_meter-gap: var(--ui-space-0-5);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
|
|
123
|
+
@media (forced-colors: active) {
|
|
124
|
+
.fill {
|
|
125
|
+
forced-color-adjust: none;
|
|
126
|
+
background: CanvasText;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
|
|
130
|
+
top and bottom edges and the gradient the ends. */
|
|
131
|
+
:scope[data-ui-meter-segments] {
|
|
132
|
+
forced-color-adjust: none;
|
|
133
|
+
background: repeating-linear-gradient(
|
|
134
|
+
to right,
|
|
135
|
+
CanvasText 0 1px,
|
|
136
|
+
Canvas 0 calc(var(--_meter-segment) - 1px),
|
|
137
|
+
CanvasText 0 var(--_meter-segment),
|
|
138
|
+
transparent 0 var(--_meter-period)
|
|
139
|
+
);
|
|
140
|
+
box-shadow: inset 0 0 0 1px CanvasText;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
145
|
+
:scope[hidden] {
|
|
146
|
+
display: none;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
@scope ([data-component~="ui-meter"]) to ([data-component]) {
|
|
150
|
+
/* How far along something is: a track, filled from the start in the tone's colour. The track is a
|
|
151
|
+
quiet border colour, so an empty meter still shows; every tone's fill keeps 3:1 against the
|
|
152
|
+
track and the page. */
|
|
153
|
+
|
|
154
|
+
/* Segments: the track and its fill show through a mask of equal segments with a gap between them.
|
|
155
|
+
A mask over the whole meter, rather than one element per segment, because the expression
|
|
156
|
+
language has no range to render N elements from, and one mask lines the fill up with the track
|
|
157
|
+
for free. Each period is a segment and the gap after it; N periods span the track plus one
|
|
158
|
+
trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
|
|
159
|
+
The count arrives as an attribute, not an inline custom property (which would add a style
|
|
160
|
+
attribute to every meter), so a continuous meter renders exactly as before, and the rules below
|
|
161
|
+
turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
|
|
162
|
+
|
|
163
|
+
/* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
|
|
164
|
+
@media (forced-colors: active) {
|
|
165
|
+
|
|
166
|
+
/* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
|
|
167
|
+
top and bottom edges and the gradient the ends. */
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
171
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Generated by HTML Next 1.0.0-alpha.7 for Vanilla DOM. Do not edit.
|
|
2
|
+
export interface UiMeterEventMap {
|
|
3
|
+
}
|
|
4
|
+
export interface UiMeterElement extends HTMLSpanElement {
|
|
5
|
+
addEventListener<K extends keyof UiMeterEventMap>(type: K, listener: (this: UiMeterElement, event: UiMeterEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
6
|
+
removeEventListener<K extends keyof UiMeterEventMap>(type: K, listener: (this: UiMeterElement, event: UiMeterEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
|
|
7
|
+
}
|
|
8
|
+
export interface UiMeterProps {
|
|
9
|
+
label?: string | null;
|
|
10
|
+
max?: number | null;
|
|
11
|
+
segments?: number | null;
|
|
12
|
+
size?: "sm" | "md" | null;
|
|
13
|
+
tone?: "neutral" | "accent" | "info" | "success" | "warning" | "danger" | null;
|
|
14
|
+
value?: number | null;
|
|
15
|
+
valueText?: string | null;
|
|
16
|
+
attributes?: Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
17
|
+
children?: readonly (string | Node)[];
|
|
18
|
+
slots?: Readonly<Record<string, readonly (string | Node)[]>>;
|
|
19
|
+
}
|
|
20
|
+
export declare function createUiMeter(props?: UiMeterProps): UiMeterElement;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Generated by HTML Next 1.0.0-alpha.7 for Vanilla DOM. Do not edit.
|
|
2
|
+
import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
|
|
3
|
+
import "../styles/ui-meter.css";
|
|
4
|
+
|
|
5
|
+
const definition = {...{"contract":{"tag":"ui-meter","props":{"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":""},"max":{"type":"number","required":false,"target":{"attribute":"aria-valuemax"},"default":1},"segments":{"type":"number","required":false,"target":{"attribute":"segments"},"default":0},"size":{"type":{"enum":["sm","md"]},"required":false,"target":{"attribute":"size"},"default":"md"},"tone":{"type":{"enum":["neutral","accent","info","success","warning","danger"]},"required":false,"target":{"attribute":"tone"},"default":"neutral"},"value":{"type":"number","required":false,"target":{"attribute":"value"},"default":0},"valueText":{"type":"string","required":false,"target":{"attribute":"valuetext"},"default":""}}},"template":{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"role","value":"meter"},{"kind":"literal","name":"aria-valuemin","value":"0"},{"kind":"attribute","name":"aria-valuemax","expression":"max","expressionPlan":{"source":"max","ast":{"kind":"id","name":"max"},"dependencies":["max"]}},{"kind":"attribute","name":"aria-valuenow","expression":"amount","expressionPlan":{"source":"amount","ast":{"kind":"id","name":"amount"},"dependencies":["amount"]}},{"kind":"attribute","name":"aria-valuetext","expression":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","expressionPlan":{"source":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"valueText"}},{"key":"false","value":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"whole"}]}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"valueText"},"right":{"kind":"literal","value":""}}]}},"dependencies":["valueText","whole"]}},{"kind":"attribute","name":"aria-label","expression":"{ true: label, false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: label, false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"label"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"data-ui-meter-segments","expression":"{ true: segments, false: null }[format('%s', segments > 1)]","expressionPlan":{"source":"{ true: segments, false: null }[format('%s', segments > 1)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"segments"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":">","left":{"kind":"id","name":"segments"},"right":{"kind":"literal","value":1}}]}},"dependencies":["segments"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"fill"},{"kind":"attribute","name":"inline-size","expression":"format('%s%', percent)","expressionPlan":{"source":"format('%s%', percent)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"percent"}]},"dependencies":["percent"]},"target":"style"}],"children":[]}]},"declarations":[{"kind":"computed","name":"amount","expression":{"source":"clamp(0, value, max)","ast":{"kind":"call","fn":"clamp","args":[{"kind":"literal","value":0},{"kind":"id","name":"value"},{"kind":"id","name":"max"}]},"dependencies":["max","value"]}},{"kind":"computed","name":"percent","expression":{"source":"{ true: amount / max * 100, false: 0 }[format('%s', max > 0)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"binary","op":"*","left":{"kind":"binary","op":"/","left":{"kind":"id","name":"amount"},"right":{"kind":"id","name":"max"}},"right":{"kind":"literal","value":100}}},{"key":"false","value":{"kind":"literal","value":0}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":">","left":{"kind":"id","name":"max"},"right":{"kind":"literal","value":0}}]}},"dependencies":["amount","max"]}},{"kind":"computed","name":"whole","expression":{"source":"percent + 0.5 - (percent + 0.5) % 1","ast":{"kind":"binary","op":"-","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"binary","op":"%","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"literal","value":1}}},"dependencies":["percent"]}}],"root":{"kind":"native","element":"span","choices":["span"]}},source:{file:import.meta.url},css:""};
|
|
6
|
+
|
|
7
|
+
export function createUiMeter(options = {}) {
|
|
8
|
+
const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
|
|
9
|
+
const projected = [];
|
|
10
|
+
const element = document.createElement("span");
|
|
11
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
12
|
+
if (value === null || value === undefined || value === false) continue;
|
|
13
|
+
element.setAttribute(name, value === true ? "" : String(value));
|
|
14
|
+
}
|
|
15
|
+
if (!element.hasAttribute("role")) element.setAttribute("role", "meter");
|
|
16
|
+
if (!element.hasAttribute("aria-valuemin")) element.setAttribute("aria-valuemin", "0");
|
|
17
|
+
const value0 = componentProps["max"] === undefined ? 1 : componentProps["max"];
|
|
18
|
+
if (value0 === null || value0 === undefined) element.removeAttribute("aria-valuemax");
|
|
19
|
+
else element.setAttribute("aria-valuemax", String(value0));
|
|
20
|
+
element.setAttribute("data-component", "ui-meter");
|
|
21
|
+
const element0 = document.createElement("span");
|
|
22
|
+
element0.setAttribute("class", "fill");
|
|
23
|
+
element.append(element0);
|
|
24
|
+
manageComponentLifecycle(element, definition, { props: componentProps, projected,
|
|
25
|
+
});
|
|
26
|
+
return element;
|
|
27
|
+
}
|
|
28
|
+
|
package/vanilla/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export * from "./UiList.js";
|
|
|
37
37
|
export * from "./UiListItem.js";
|
|
38
38
|
export * from "./UiMenu.js";
|
|
39
39
|
export * from "./UiMenuItem.js";
|
|
40
|
+
export * from "./UiMeter.js";
|
|
40
41
|
export * from "./UiNavItem.js";
|
|
41
42
|
export * from "./UiPageHeader.js";
|
|
42
43
|
export * from "./UiPopover.js";
|
package/vue/UiMeter.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
label?: string;
|
|
3
|
+
max?: number;
|
|
4
|
+
segments?: number;
|
|
5
|
+
size?: 'sm' | 'md';
|
|
6
|
+
tone?: 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger';
|
|
7
|
+
value?: number;
|
|
8
|
+
valueText?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
11
|
+
label: string;
|
|
12
|
+
size: "sm" | "md";
|
|
13
|
+
value: number;
|
|
14
|
+
max: number;
|
|
15
|
+
tone: "neutral" | "accent" | "info" | "success" | "warning" | "danger";
|
|
16
|
+
segments: number;
|
|
17
|
+
valueText: string;
|
|
18
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
package/vue/UiMeter.js
ADDED
package/vue/UiMeter.vue
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<!-- Generated by HTML Next 1.0.0-alpha.7 for Vue 3.5. Do not edit. -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(
|
|
8
|
+
defineProps<{
|
|
9
|
+
label?: string
|
|
10
|
+
max?: number
|
|
11
|
+
segments?: number
|
|
12
|
+
size?: 'sm' | 'md'
|
|
13
|
+
tone?: 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger'
|
|
14
|
+
value?: number
|
|
15
|
+
valueText?: string
|
|
16
|
+
}>(),
|
|
17
|
+
{
|
|
18
|
+
label: '',
|
|
19
|
+
max: 1,
|
|
20
|
+
segments: 0,
|
|
21
|
+
size: 'md',
|
|
22
|
+
tone: 'neutral',
|
|
23
|
+
value: 0,
|
|
24
|
+
valueText: '',
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const amount = computed(() => Math.min(Math.max(0, props.value), props.max))
|
|
29
|
+
const percent = computed(() =>
|
|
30
|
+
({
|
|
31
|
+
true: number(
|
|
32
|
+
number(amount.value) === undefined || number(props.max) === undefined
|
|
33
|
+
? undefined
|
|
34
|
+
: number(amount.value)! / number(props.max)!,
|
|
35
|
+
) === undefined || number(100) === undefined
|
|
36
|
+
? undefined
|
|
37
|
+
: number(
|
|
38
|
+
number(amount.value) === undefined || number(props.max) === undefined
|
|
39
|
+
? undefined
|
|
40
|
+
: number(amount.value)! / number(props.max)!,
|
|
41
|
+
)! * number(100)!,
|
|
42
|
+
false: 0,
|
|
43
|
+
})[`${props.max > 0}`]
|
|
44
|
+
)
|
|
45
|
+
const whole = computed(
|
|
46
|
+
() => (number(
|
|
47
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
48
|
+
? undefined
|
|
49
|
+
: number(percent.value)! + number(0.5)!,
|
|
50
|
+
) === undefined
|
|
51
|
+
|| number(
|
|
52
|
+
number(
|
|
53
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: number(percent.value)! + number(0.5)!,
|
|
56
|
+
) === undefined || number(1) === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: number(
|
|
59
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
60
|
+
? undefined
|
|
61
|
+
: number(percent.value)! + number(0.5)!,
|
|
62
|
+
)! % number(1)!,
|
|
63
|
+
) === undefined
|
|
64
|
+
? undefined
|
|
65
|
+
: number(
|
|
66
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: number(percent.value)! + number(0.5)!,
|
|
69
|
+
)! - number(
|
|
70
|
+
number(
|
|
71
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
72
|
+
? undefined
|
|
73
|
+
: number(percent.value)! + number(0.5)!,
|
|
74
|
+
) === undefined || number(1) === undefined
|
|
75
|
+
? undefined
|
|
76
|
+
: number(
|
|
77
|
+
number(percent.value) === undefined || number(0.5) === undefined
|
|
78
|
+
? undefined
|
|
79
|
+
: number(percent.value)! + number(0.5)!,
|
|
80
|
+
)! % number(1)!,
|
|
81
|
+
)!)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
/** The values the styles' :host-state() rules test. */
|
|
85
|
+
const hostState = computed(() =>
|
|
86
|
+
[
|
|
87
|
+
props.size && `size size=${props.size}`,
|
|
88
|
+
props.tone && `tone tone=${props.tone}`,
|
|
89
|
+
].filter(Boolean).join(' ')
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
function text(value: unknown): string {
|
|
93
|
+
if (Array.isArray(value)) return value.map(text).join(' ')
|
|
94
|
+
return value === null || value === undefined || typeof value === 'object' ? '' : String(value)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function attribute(value: unknown): any {
|
|
98
|
+
if (
|
|
99
|
+
value === null || value === undefined || value === false
|
|
100
|
+
|| (typeof value === 'object' && !Array.isArray(value))
|
|
101
|
+
) return undefined
|
|
102
|
+
return value === true ? '' : text(value)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function number(value: unknown): number | undefined {
|
|
106
|
+
return typeof value === 'number' && !Number.isNaN(value) ? value : undefined
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<template>
|
|
111
|
+
<span
|
|
112
|
+
data-component="ui-meter"
|
|
113
|
+
role="meter"
|
|
114
|
+
aria-valuemin="0"
|
|
115
|
+
v-bind="$attrs"
|
|
116
|
+
:aria-valuemax="max"
|
|
117
|
+
:aria-valuenow="amount"
|
|
118
|
+
:aria-valuetext="typeof (({ true: valueText, false: `${whole ?? ''}%` })[`${valueText !== ''}`]) === 'boolean'
|
|
119
|
+
? String(({ true: valueText, false: `${whole ?? ''}%` })[`${valueText !== ''}`])
|
|
120
|
+
: attribute(({ true: valueText, false: `${whole ?? ''}%` })[`${valueText !== ''}`])"
|
|
121
|
+
:aria-label="typeof (({ true: label, false: null })[`${label !== ''}`]) === 'boolean'
|
|
122
|
+
? String(({ true: label, false: null })[`${label !== ''}`])
|
|
123
|
+
: attribute(({ true: label, false: null })[`${label !== ''}`])"
|
|
124
|
+
:data-ui-meter-segments="attribute(({ true: segments, false: null })[`${segments > 1}`])"
|
|
125
|
+
:data-ui-meter-state="hostState || undefined"
|
|
126
|
+
><span class="fill" :style="{ 'inline-size': `${text(percent)}%` }"></span></span>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<style scoped>
|
|
130
|
+
/* How far along something is: a track, filled from the start in the tone's colour. The track is a
|
|
131
|
+
quiet border colour, so an empty meter still shows; every tone's fill keeps 3:1 against the
|
|
132
|
+
track and the page. */
|
|
133
|
+
@property --ui-meter-inline-size {
|
|
134
|
+
syntax: "*";
|
|
135
|
+
inherits: false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-component~="ui-meter"] {
|
|
139
|
+
--_meter-fill: var(--ui-text-muted);
|
|
140
|
+
|
|
141
|
+
display: inline-block;
|
|
142
|
+
flex: none;
|
|
143
|
+
vertical-align: middle;
|
|
144
|
+
inline-size: var(--ui-meter-inline-size, 8rem);
|
|
145
|
+
max-inline-size: 100%;
|
|
146
|
+
block-size: var(--ui-space-2);
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
border-radius: var(--ui-radius-round);
|
|
149
|
+
background: var(--ui-border);
|
|
150
|
+
/* Unseen until forced colours draw it, where the track's own colour is gone. */
|
|
151
|
+
outline: 1px solid transparent;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
[data-component~="ui-meter"][data-ui-meter-state~="size=sm"] {
|
|
155
|
+
inline-size: var(--ui-meter-inline-size, 5rem);
|
|
156
|
+
block-size: var(--ui-space-1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.fill {
|
|
160
|
+
display: block;
|
|
161
|
+
block-size: 100%;
|
|
162
|
+
background: var(--_meter-fill);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[data-component~="ui-meter"][data-ui-meter-state~="tone=accent"] {
|
|
166
|
+
--_meter-fill: var(--ui-accent-solid);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[data-component~="ui-meter"][data-ui-meter-state~="tone=info"] {
|
|
170
|
+
--_meter-fill: var(--ui-info-solid);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
[data-component~="ui-meter"][data-ui-meter-state~="tone=success"] {
|
|
174
|
+
--_meter-fill: var(--ui-success-solid);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
[data-component~="ui-meter"][data-ui-meter-state~="tone=warning"] {
|
|
178
|
+
--_meter-fill: var(--ui-warning-solid);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[data-component~="ui-meter"][data-ui-meter-state~="tone=danger"] {
|
|
182
|
+
--_meter-fill: var(--ui-danger-solid);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Segments: the track and its fill show through a mask of equal segments with a gap between them.
|
|
186
|
+
A mask over the whole meter, rather than one element per segment, because the expression
|
|
187
|
+
language has no range to render N elements from, and one mask lines the fill up with the track
|
|
188
|
+
for free. Each period is a segment and the gap after it; N periods span the track plus one
|
|
189
|
+
trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
|
|
190
|
+
The count arrives as an attribute, not an inline custom property (which would add a style
|
|
191
|
+
attribute to every meter), so a continuous meter renders exactly as before, and the rules below
|
|
192
|
+
turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
|
|
193
|
+
[data-component~="ui-meter"][data-ui-meter-segments="2"] {
|
|
194
|
+
--_segments: 2;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[data-component~="ui-meter"][data-ui-meter-segments="3"] {
|
|
198
|
+
--_segments: 3;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
[data-component~="ui-meter"][data-ui-meter-segments="4"] {
|
|
202
|
+
--_segments: 4;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
[data-component~="ui-meter"][data-ui-meter-segments="5"] {
|
|
206
|
+
--_segments: 5;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
[data-component~="ui-meter"][data-ui-meter-segments="6"] {
|
|
210
|
+
--_segments: 6;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[data-component~="ui-meter"][data-ui-meter-segments="7"] {
|
|
214
|
+
--_segments: 7;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
[data-component~="ui-meter"][data-ui-meter-segments="8"] {
|
|
218
|
+
--_segments: 8;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
[data-component~="ui-meter"][data-ui-meter-segments="9"] {
|
|
222
|
+
--_segments: 9;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
[data-component~="ui-meter"][data-ui-meter-segments="10"] {
|
|
226
|
+
--_segments: 10;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
[data-component~="ui-meter"][data-ui-meter-segments="11"] {
|
|
230
|
+
--_segments: 11;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
[data-component~="ui-meter"][data-ui-meter-segments="12"] {
|
|
234
|
+
--_segments: 12;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
[data-component~="ui-meter"][data-ui-meter-segments] {
|
|
238
|
+
--_meter-gap: var(--ui-space-1);
|
|
239
|
+
--_meter-period: calc((100% + var(--_meter-gap)) / var(--_segments));
|
|
240
|
+
--_meter-segment: calc(var(--_meter-period) - var(--_meter-gap));
|
|
241
|
+
|
|
242
|
+
mask-image: repeating-linear-gradient(
|
|
243
|
+
to right,
|
|
244
|
+
#000 0 var(--_meter-segment),
|
|
245
|
+
transparent 0 var(--_meter-period)
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
[data-component~="ui-meter"][data-ui-meter-state~="size=sm"][data-ui-meter-segments] {
|
|
250
|
+
--_meter-gap: var(--ui-space-0-5);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
|
|
254
|
+
@media (forced-colors: active) {
|
|
255
|
+
.fill {
|
|
256
|
+
forced-color-adjust: none;
|
|
257
|
+
background: CanvasText;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
|
|
261
|
+
top and bottom edges and the gradient the ends. */
|
|
262
|
+
[data-component~="ui-meter"][data-ui-meter-segments] {
|
|
263
|
+
forced-color-adjust: none;
|
|
264
|
+
background: repeating-linear-gradient(
|
|
265
|
+
to right,
|
|
266
|
+
CanvasText 0 1px,
|
|
267
|
+
Canvas 0 calc(var(--_meter-segment) - 1px),
|
|
268
|
+
CanvasText 0 var(--_meter-segment),
|
|
269
|
+
transparent 0 var(--_meter-period)
|
|
270
|
+
);
|
|
271
|
+
box-shadow: inset 0 0 0 1px CanvasText;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
276
|
+
[data-component~="ui-meter"][hidden] {
|
|
277
|
+
display: none;
|
|
278
|
+
}
|
|
279
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper.js";
|
|
2
|
+
import { computed, createElementBlock, createElementVNode, defineComponent, mergeProps, normalizeStyle, openBlock } from "vue";
|
|
3
|
+
//#region .build/vue/UiMeter.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
+
var _hoisted_1 = [
|
|
5
|
+
"aria-valuemax",
|
|
6
|
+
"aria-valuenow",
|
|
7
|
+
"aria-valuetext",
|
|
8
|
+
"aria-label",
|
|
9
|
+
"data-ui-meter-segments",
|
|
10
|
+
"data-ui-meter-state"
|
|
11
|
+
];
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region .build/vue/UiMeter.vue
|
|
14
|
+
var UiMeter_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
15
|
+
inheritAttrs: false,
|
|
16
|
+
__name: "UiMeter",
|
|
17
|
+
props: {
|
|
18
|
+
label: { default: "" },
|
|
19
|
+
max: { default: 1 },
|
|
20
|
+
segments: { default: 0 },
|
|
21
|
+
size: { default: "md" },
|
|
22
|
+
tone: { default: "neutral" },
|
|
23
|
+
value: { default: 0 },
|
|
24
|
+
valueText: { default: "" }
|
|
25
|
+
},
|
|
26
|
+
setup(__props) {
|
|
27
|
+
const props = __props;
|
|
28
|
+
const amount = computed(() => Math.min(Math.max(0, props.value), props.max));
|
|
29
|
+
const percent = computed(() => ({
|
|
30
|
+
true: number(number(amount.value) === void 0 || number(props.max) === void 0 ? void 0 : number(amount.value) / number(props.max)) === void 0 || number(100) === void 0 ? void 0 : number(number(amount.value) === void 0 || number(props.max) === void 0 ? void 0 : number(amount.value) / number(props.max)) * number(100),
|
|
31
|
+
false: 0
|
|
32
|
+
})[`${props.max > 0}`]);
|
|
33
|
+
const whole = computed(() => number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) === void 0 || number(number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) === void 0 || number(1) === void 0 ? void 0 : number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) % number(1)) === void 0 ? void 0 : number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) - number(number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) === void 0 || number(1) === void 0 ? void 0 : number(number(percent.value) === void 0 || number(.5) === void 0 ? void 0 : number(percent.value) + number(.5)) % number(1)));
|
|
34
|
+
/** The values the styles' :host-state() rules test. */
|
|
35
|
+
const hostState = computed(() => [props.size && `size size=${props.size}`, props.tone && `tone tone=${props.tone}`].filter(Boolean).join(" "));
|
|
36
|
+
function text(value) {
|
|
37
|
+
if (Array.isArray(value)) return value.map(text).join(" ");
|
|
38
|
+
return value === null || value === void 0 || typeof value === "object" ? "" : String(value);
|
|
39
|
+
}
|
|
40
|
+
function attribute(value) {
|
|
41
|
+
if (value === null || value === void 0 || value === false || typeof value === "object" && !Array.isArray(value)) return void 0;
|
|
42
|
+
return value === true ? "" : text(value);
|
|
43
|
+
}
|
|
44
|
+
function number(value) {
|
|
45
|
+
return typeof value === "number" && !Number.isNaN(value) ? value : void 0;
|
|
46
|
+
}
|
|
47
|
+
return (_ctx, _cache) => {
|
|
48
|
+
return openBlock(), createElementBlock("span", mergeProps({
|
|
49
|
+
"data-component": "ui-meter",
|
|
50
|
+
role: "meter",
|
|
51
|
+
"aria-valuemin": "0"
|
|
52
|
+
}, _ctx.$attrs, {
|
|
53
|
+
"aria-valuemax": __props.max,
|
|
54
|
+
"aria-valuenow": amount.value,
|
|
55
|
+
"aria-valuetext": typeof {
|
|
56
|
+
true: __props.valueText,
|
|
57
|
+
false: `${whole.value ?? ""}%`
|
|
58
|
+
}[`${__props.valueText !== ""}`] === "boolean" ? String({
|
|
59
|
+
true: __props.valueText,
|
|
60
|
+
false: `${whole.value ?? ""}%`
|
|
61
|
+
}[`${__props.valueText !== ""}`]) : attribute({
|
|
62
|
+
true: __props.valueText,
|
|
63
|
+
false: `${whole.value ?? ""}%`
|
|
64
|
+
}[`${__props.valueText !== ""}`]),
|
|
65
|
+
"aria-label": typeof {
|
|
66
|
+
true: __props.label,
|
|
67
|
+
false: null
|
|
68
|
+
}[`${__props.label !== ""}`] === "boolean" ? String({
|
|
69
|
+
true: __props.label,
|
|
70
|
+
false: null
|
|
71
|
+
}[`${__props.label !== ""}`]) : attribute({
|
|
72
|
+
true: __props.label,
|
|
73
|
+
false: null
|
|
74
|
+
}[`${__props.label !== ""}`]),
|
|
75
|
+
"data-ui-meter-segments": attribute({
|
|
76
|
+
true: __props.segments,
|
|
77
|
+
false: null
|
|
78
|
+
}[`${__props.segments > 1}`]),
|
|
79
|
+
"data-ui-meter-state": hostState.value || void 0
|
|
80
|
+
}), [createElementVNode("span", {
|
|
81
|
+
class: "fill",
|
|
82
|
+
style: normalizeStyle({ "inline-size": `${text(percent.value)}%` })
|
|
83
|
+
}, null, 4)], 16, _hoisted_1);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}), [["__scopeId", "data-v-c0b7936e"]]);
|
|
87
|
+
//#endregion
|
|
88
|
+
export { UiMeter_default as t };
|