@sentropic/design-system-svelte 0.12.0 → 0.14.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/dist/Autosave.svelte +185 -0
- package/dist/Autosave.svelte.d.ts +25 -0
- package/dist/Autosave.svelte.d.ts.map +1 -0
- package/dist/Calendar.svelte +406 -0
- package/dist/Calendar.svelte.d.ts +31 -0
- package/dist/Calendar.svelte.d.ts.map +1 -0
- package/dist/ForceGraph.svelte +65 -14
- package/dist/ForceGraph.svelte.d.ts +12 -0
- package/dist/ForceGraph.svelte.d.ts.map +1 -1
- package/dist/Rating.svelte +189 -0
- package/dist/Rating.svelte.d.ts +24 -0
- package/dist/Rating.svelte.d.ts.map +1 -0
- package/dist/SlideIndicator.svelte +173 -0
- package/dist/SlideIndicator.svelte.d.ts +19 -0
- package/dist/SlideIndicator.svelte.d.ts.map +1 -0
- package/dist/TimePicker.svelte +335 -0
- package/dist/TimePicker.svelte.d.ts +25 -0
- package/dist/TimePicker.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/package.json +1 -1
package/dist/ForceGraph.svelte
CHANGED
|
@@ -237,6 +237,18 @@
|
|
|
237
237
|
* by `edgeCurve * dist * factor`. Defaults to a light 0.15.
|
|
238
238
|
*/
|
|
239
239
|
edgeCurve?: number;
|
|
240
|
+
/**
|
|
241
|
+
* Repulsion multiplier controlling how spread out the layout is.
|
|
242
|
+
* >1 = graphe plus aéré, <1 = plus compact ; multiplie la force de
|
|
243
|
+
* répulsion sans toucher au fit-to-content. Defaults to 1. Clamped to
|
|
244
|
+
* [0.1, 10] internally to avoid layout explosions/collapses.
|
|
245
|
+
*/
|
|
246
|
+
repulsion?: number;
|
|
247
|
+
/**
|
|
248
|
+
* Called when the user hovers (or keyboard-focuses) a node, and again with
|
|
249
|
+
* null when the hover/focus ends. Intended for syncing an external panel.
|
|
250
|
+
*/
|
|
251
|
+
onNodeHover?: (node: ForceGraphNode | null) => void;
|
|
240
252
|
class?: string;
|
|
241
253
|
};
|
|
242
254
|
|
|
@@ -256,6 +268,8 @@
|
|
|
256
268
|
onEdgeHover,
|
|
257
269
|
legend,
|
|
258
270
|
edgeCurve = 0.15,
|
|
271
|
+
repulsion = 1,
|
|
272
|
+
onNodeHover,
|
|
259
273
|
class: className
|
|
260
274
|
}: ForceGraphProps = $props();
|
|
261
275
|
|
|
@@ -311,7 +325,8 @@
|
|
|
311
325
|
es: ForceGraphEdge[],
|
|
312
326
|
w: number,
|
|
313
327
|
h: number,
|
|
314
|
-
ticks: number
|
|
328
|
+
ticks: number,
|
|
329
|
+
repulsionFactor: number
|
|
315
330
|
): Map<string, { x: number; y: number }> {
|
|
316
331
|
const cx = w / 2;
|
|
317
332
|
const cy = h / 2;
|
|
@@ -339,7 +354,11 @@
|
|
|
339
354
|
|
|
340
355
|
const area = w * h;
|
|
341
356
|
const k = Math.sqrt(area / Math.max(ns.length, 1)); // ideal node distance
|
|
342
|
-
|
|
357
|
+
// Clamp the caller-supplied factor so extreme values can't explode or
|
|
358
|
+
// collapse the layout. >1 spreads nodes out, <1 packs them tighter; the
|
|
359
|
+
// fit-to-content viewBox is recomputed afterwards so spacing just fills space.
|
|
360
|
+
const clampedRepulsion = Math.min(Math.max(repulsionFactor, 0.1), 10);
|
|
361
|
+
const repulsion = k * k * 0.9 * clampedRepulsion;
|
|
343
362
|
const restLength = k * 0.8;
|
|
344
363
|
const springK = 0.04;
|
|
345
364
|
const gravity = 0.012;
|
|
@@ -426,7 +445,7 @@
|
|
|
426
445
|
// honouring the motion preference (no rAF loop, no jitter).
|
|
427
446
|
const layout = $derived.by(() => {
|
|
428
447
|
const ticks = Math.max(1, Math.round(iterations));
|
|
429
|
-
return runSimulation(nodes, edges, width, height, ticks);
|
|
448
|
+
return runSimulation(nodes, edges, width, height, ticks, repulsion);
|
|
430
449
|
});
|
|
431
450
|
|
|
432
451
|
const positionedNodes = $derived.by(() =>
|
|
@@ -584,6 +603,38 @@
|
|
|
584
603
|
return !(srcActive || tgtActive);
|
|
585
604
|
}
|
|
586
605
|
|
|
606
|
+
// ---------------------------------------------------------------------------
|
|
607
|
+
// Hover-connexe (demand 7): hovering a node fades the rest of the graph the
|
|
608
|
+
// same way selection does — the hovered node and its direct neighbours stay
|
|
609
|
+
// full, every other node dims, and only edges incident to the hovered node
|
|
610
|
+
// keep their opacity. Composes with selection (predicates OR'd together).
|
|
611
|
+
// ---------------------------------------------------------------------------
|
|
612
|
+
const hoveredNodeId = $derived(
|
|
613
|
+
hoveredNodeIndex != null ? (positionedNodes[hoveredNodeIndex]?.node.id ?? null) : null
|
|
614
|
+
);
|
|
615
|
+
const hoverActiveSet = $derived.by(() => {
|
|
616
|
+
const set = new Set<string>();
|
|
617
|
+
if (hoveredNodeId == null) return set;
|
|
618
|
+
set.add(hoveredNodeId);
|
|
619
|
+
const nb = adjacency.get(hoveredNodeId);
|
|
620
|
+
if (nb) for (const n of nb) set.add(n);
|
|
621
|
+
return set;
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// A node is dimmed by hover when a node is hovered and this one is neither
|
|
625
|
+
// the hovered node nor one of its direct neighbours.
|
|
626
|
+
function isHoverDimmedNode(id: string): boolean {
|
|
627
|
+
if (hoveredNodeId == null) return false;
|
|
628
|
+
return !hoverActiveSet.has(id);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// An edge is dimmed by hover when a node is hovered and the edge is not
|
|
632
|
+
// incident to it (keep only the hovered node's own edges full).
|
|
633
|
+
function isHoverDimmedEdge(e: ForceGraphEdge): boolean {
|
|
634
|
+
if (hoveredNodeId == null) return false;
|
|
635
|
+
return e.source !== hoveredNodeId && e.target !== hoveredNodeId;
|
|
636
|
+
}
|
|
637
|
+
|
|
587
638
|
// Keyboard handler for a node circle: Space/Enter → onSelect, Enter → onOpenEntity.
|
|
588
639
|
function handleNodeKeydown(id: string, e: KeyboardEvent) {
|
|
589
640
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -734,7 +785,7 @@
|
|
|
734
785
|
class:st-forceGraph__edge--weak={e.edge.weak}
|
|
735
786
|
class:st-forceGraph__edge--emphasis={e.edge.emphasis}
|
|
736
787
|
class:st-forceGraph__edge--hovered={hoveredEdgeIndex === e.i}
|
|
737
|
-
class:st-forceGraph__edge--dim={isEdgeSelectionDimmed(e.edge)}
|
|
788
|
+
class:st-forceGraph__edge--dim={isEdgeSelectionDimmed(e.edge) || isHoverDimmedEdge(e.edge)}
|
|
738
789
|
d={e.path}
|
|
739
790
|
fill="none"
|
|
740
791
|
stroke-dasharray={e.dashArray}
|
|
@@ -747,7 +798,7 @@
|
|
|
747
798
|
class:st-forceGraph__edge--weak={e.edge.weak}
|
|
748
799
|
class:st-forceGraph__edge--emphasis={e.edge.emphasis}
|
|
749
800
|
class:st-forceGraph__edge--hovered={hoveredEdgeIndex === e.i}
|
|
750
|
-
class:st-forceGraph__edge--dim={isEdgeSelectionDimmed(e.edge)}
|
|
801
|
+
class:st-forceGraph__edge--dim={isEdgeSelectionDimmed(e.edge) || isHoverDimmedEdge(e.edge)}
|
|
751
802
|
x1={e.x1}
|
|
752
803
|
y1={e.y1}
|
|
753
804
|
x2={e.x2}
|
|
@@ -764,7 +815,7 @@
|
|
|
764
815
|
{#each positionedNodes as p (p.node.id)}
|
|
765
816
|
<g
|
|
766
817
|
class="st-forceGraph__node st-forceGraph__node--{p.tone}"
|
|
767
|
-
class:st-forceGraph__node--dim={(
|
|
818
|
+
class:st-forceGraph__node--dim={isHoverDimmedNode(p.node.id) || isSelectionDimmed(p.node.id)}
|
|
768
819
|
class:st-forceGraph__node--selected={selectedSet.has(p.node.id)}
|
|
769
820
|
class:st-forceGraph__node--focus={focusId === p.node.id}
|
|
770
821
|
transform="translate({p.x} {p.y})"
|
|
@@ -777,10 +828,10 @@
|
|
|
777
828
|
role="button"
|
|
778
829
|
aria-label="{p.title}{p.node.group !== undefined ? `: ${p.node.group}` : ''}"
|
|
779
830
|
aria-pressed={selectedSet.has(p.node.id)}
|
|
780
|
-
onmouseenter={() =>
|
|
781
|
-
onmouseleave={() =>
|
|
782
|
-
onfocus={() =>
|
|
783
|
-
onblur={() =>
|
|
831
|
+
onmouseenter={() => { hoveredNodeIndex = p.i; onNodeHover?.(p.node); }}
|
|
832
|
+
onmouseleave={() => { hoveredNodeIndex = null; onNodeHover?.(null); }}
|
|
833
|
+
onfocus={() => { hoveredNodeIndex = p.i; onNodeHover?.(p.node); }}
|
|
834
|
+
onblur={() => { hoveredNodeIndex = null; onNodeHover?.(null); }}
|
|
784
835
|
onclick={() => onSelect?.(p.node.id)}
|
|
785
836
|
ondblclick={() => onOpenEntity?.(p.node.id)}
|
|
786
837
|
onkeydown={(e) => handleNodeKeydown(p.node.id, e)}
|
|
@@ -793,10 +844,10 @@
|
|
|
793
844
|
role="button"
|
|
794
845
|
aria-label="{p.title}{p.node.group !== undefined ? `: ${p.node.group}` : ''}"
|
|
795
846
|
aria-pressed={selectedSet.has(p.node.id)}
|
|
796
|
-
onmouseenter={() =>
|
|
797
|
-
onmouseleave={() =>
|
|
798
|
-
onfocus={() =>
|
|
799
|
-
onblur={() =>
|
|
847
|
+
onmouseenter={() => { hoveredNodeIndex = p.i; onNodeHover?.(p.node); }}
|
|
848
|
+
onmouseleave={() => { hoveredNodeIndex = null; onNodeHover?.(null); }}
|
|
849
|
+
onfocus={() => { hoveredNodeIndex = p.i; onNodeHover?.(p.node); }}
|
|
850
|
+
onblur={() => { hoveredNodeIndex = null; onNodeHover?.(null); }}
|
|
800
851
|
onclick={() => onSelect?.(p.node.id)}
|
|
801
852
|
ondblclick={() => onOpenEntity?.(p.node.id)}
|
|
802
853
|
onkeydown={(e) => handleNodeKeydown(p.node.id, e)}
|
|
@@ -126,6 +126,18 @@ type ForceGraphProps = {
|
|
|
126
126
|
* by `edgeCurve * dist * factor`. Defaults to a light 0.15.
|
|
127
127
|
*/
|
|
128
128
|
edgeCurve?: number;
|
|
129
|
+
/**
|
|
130
|
+
* Repulsion multiplier controlling how spread out the layout is.
|
|
131
|
+
* >1 = graphe plus aéré, <1 = plus compact ; multiplie la force de
|
|
132
|
+
* répulsion sans toucher au fit-to-content. Defaults to 1. Clamped to
|
|
133
|
+
* [0.1, 10] internally to avoid layout explosions/collapses.
|
|
134
|
+
*/
|
|
135
|
+
repulsion?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Called when the user hovers (or keyboard-focuses) a node, and again with
|
|
138
|
+
* null when the hover/focus ends. Intended for syncing an external panel.
|
|
139
|
+
*/
|
|
140
|
+
onNodeHover?: (node: ForceGraphNode | null) => void;
|
|
129
141
|
class?: string;
|
|
130
142
|
};
|
|
131
143
|
declare const ForceGraph: import("svelte").Component<ForceGraphProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ForceGraph.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ForceGraph.svelte.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ForceGraph.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ForceGraph.svelte.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type RatingSize = "sm" | "md" | "lg";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { Star, StarHalf } from "@lucide/svelte";
|
|
7
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
8
|
+
|
|
9
|
+
type RatingProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange"> & {
|
|
10
|
+
/** Note courante (0..max). Pas de 1, ou 0.5 si `allowHalf`. */
|
|
11
|
+
value?: number;
|
|
12
|
+
/** Nombre d'étoiles. */
|
|
13
|
+
max?: number;
|
|
14
|
+
/** Appelé avec la nouvelle note au clic ou au clavier. */
|
|
15
|
+
onChange?: (value: number) => void;
|
|
16
|
+
/** Affichage seul : ni clic ni clavier n'émettent. */
|
|
17
|
+
readonly?: boolean;
|
|
18
|
+
/** Autorise les demi-étoiles (sélection au demi-point). */
|
|
19
|
+
allowHalf?: boolean;
|
|
20
|
+
size?: RatingSize;
|
|
21
|
+
/** Attribut name (utile dans un formulaire / pour la sémantique radio). */
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Étiquette accessible du groupe. */
|
|
24
|
+
label?: string;
|
|
25
|
+
class?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
value = 0,
|
|
30
|
+
max = 5,
|
|
31
|
+
onChange,
|
|
32
|
+
readonly = false,
|
|
33
|
+
allowHalf = false,
|
|
34
|
+
size = "md",
|
|
35
|
+
name,
|
|
36
|
+
label,
|
|
37
|
+
class: className,
|
|
38
|
+
...rest
|
|
39
|
+
}: RatingProps = $props();
|
|
40
|
+
|
|
41
|
+
const iconSize = $derived(size === "sm" ? 16 : size === "lg" ? 28 : 22);
|
|
42
|
+
|
|
43
|
+
const classes = $derived(
|
|
44
|
+
["st-rating", `st-rating--${size}`, readonly && "st-rating--readonly", className]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join(" ")
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const stars = $derived(Array.from({ length: max }, (_, i) => i + 1));
|
|
50
|
+
|
|
51
|
+
// L'étoile « focusable » (tabindex 0) suit la valeur ; à 0 c'est la première.
|
|
52
|
+
const focusedStar = $derived(value > 0 ? Math.ceil(value) : 1);
|
|
53
|
+
|
|
54
|
+
function fill(star: number): "full" | "half" | "empty" {
|
|
55
|
+
if (value >= star) return "full";
|
|
56
|
+
if (allowHalf && value >= star - 0.5) return "half";
|
|
57
|
+
return "empty";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function commit(next: number) {
|
|
61
|
+
if (readonly) return;
|
|
62
|
+
const clamped = Math.max(0, Math.min(max, next));
|
|
63
|
+
value = clamped;
|
|
64
|
+
onChange?.(clamped);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onStarClick(event: MouseEvent, star: number) {
|
|
68
|
+
if (readonly) return;
|
|
69
|
+
let next = star;
|
|
70
|
+
if (allowHalf) {
|
|
71
|
+
const target = event.currentTarget as HTMLElement;
|
|
72
|
+
const rect = target.getBoundingClientRect();
|
|
73
|
+
const isLeftHalf = event.clientX - rect.left < rect.width / 2;
|
|
74
|
+
next = isLeftHalf ? star - 0.5 : star;
|
|
75
|
+
}
|
|
76
|
+
// Re-cliquer la valeur déjà sélectionnée remet à zéro.
|
|
77
|
+
if (next === value) {
|
|
78
|
+
commit(0);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
commit(next);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function onKeyDown(event: KeyboardEvent) {
|
|
85
|
+
if (readonly) return;
|
|
86
|
+
const step = allowHalf ? 0.5 : 1;
|
|
87
|
+
let handled = true;
|
|
88
|
+
switch (event.key) {
|
|
89
|
+
case "ArrowRight":
|
|
90
|
+
case "ArrowUp":
|
|
91
|
+
commit(Math.min(max, value + step));
|
|
92
|
+
break;
|
|
93
|
+
case "ArrowLeft":
|
|
94
|
+
case "ArrowDown":
|
|
95
|
+
commit(Math.max(0, value - step));
|
|
96
|
+
break;
|
|
97
|
+
case "Home":
|
|
98
|
+
commit(0);
|
|
99
|
+
break;
|
|
100
|
+
case "End":
|
|
101
|
+
commit(max);
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
handled = false;
|
|
105
|
+
}
|
|
106
|
+
if (handled) event.preventDefault();
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<div
|
|
111
|
+
{...rest}
|
|
112
|
+
class={classes}
|
|
113
|
+
role="radiogroup"
|
|
114
|
+
aria-label={label}
|
|
115
|
+
aria-readonly={readonly ? "true" : undefined}
|
|
116
|
+
>
|
|
117
|
+
{#each stars as star (star)}
|
|
118
|
+
{@const state = fill(star)}
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
class="st-rating__star"
|
|
122
|
+
class:st-rating__star--full={state === "full"}
|
|
123
|
+
class:st-rating__star--half={state === "half"}
|
|
124
|
+
role="radio"
|
|
125
|
+
name={name}
|
|
126
|
+
aria-checked={Math.ceil(value) === star ? "true" : "false"}
|
|
127
|
+
aria-label={`${star} / ${max}`}
|
|
128
|
+
tabindex={!readonly && star === focusedStar ? 0 : -1}
|
|
129
|
+
disabled={readonly}
|
|
130
|
+
onclick={(event) => onStarClick(event, star)}
|
|
131
|
+
onkeydown={onKeyDown}
|
|
132
|
+
>
|
|
133
|
+
{#if state === "half"}
|
|
134
|
+
<StarHalf size={iconSize} strokeWidth={1.75} aria-hidden="true" />
|
|
135
|
+
{:else}
|
|
136
|
+
<Star
|
|
137
|
+
size={iconSize}
|
|
138
|
+
strokeWidth={1.75}
|
|
139
|
+
fill={state === "full" ? "currentColor" : "none"}
|
|
140
|
+
aria-hidden="true"
|
|
141
|
+
/>
|
|
142
|
+
{/if}
|
|
143
|
+
</button>
|
|
144
|
+
{/each}
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<style>
|
|
148
|
+
.st-rating {
|
|
149
|
+
align-items: center;
|
|
150
|
+
color: var(--st-semantic-text-secondary);
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
gap: var(--st-spacing-1, 0.25rem);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.st-rating__star {
|
|
156
|
+
align-items: center;
|
|
157
|
+
background: transparent;
|
|
158
|
+
border: 0;
|
|
159
|
+
border-radius: var(--st-component-control-radius, 0.375rem);
|
|
160
|
+
color: var(--st-semantic-text-muted);
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
display: inline-flex;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
line-height: 0;
|
|
165
|
+
padding: var(--st-spacing-1, 0.25rem);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.st-rating__star--full,
|
|
169
|
+
.st-rating__star--half {
|
|
170
|
+
color: var(--st-semantic-feedback-warning, var(--st-semantic-action-primary));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.st-rating__star:hover:not(:disabled) {
|
|
174
|
+
color: var(--st-semantic-feedback-warning, var(--st-semantic-action-primary));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.st-rating__star:focus-visible {
|
|
178
|
+
outline: 2px solid var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
|
|
179
|
+
outline-offset: 2px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.st-rating__star:disabled {
|
|
183
|
+
cursor: default;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.st-rating--readonly .st-rating__star {
|
|
187
|
+
cursor: default;
|
|
188
|
+
}
|
|
189
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type RatingSize = "sm" | "md" | "lg";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
type RatingProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange"> & {
|
|
4
|
+
/** Note courante (0..max). Pas de 1, ou 0.5 si `allowHalf`. */
|
|
5
|
+
value?: number;
|
|
6
|
+
/** Nombre d'étoiles. */
|
|
7
|
+
max?: number;
|
|
8
|
+
/** Appelé avec la nouvelle note au clic ou au clavier. */
|
|
9
|
+
onChange?: (value: number) => void;
|
|
10
|
+
/** Affichage seul : ni clic ni clavier n'émettent. */
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
/** Autorise les demi-étoiles (sélection au demi-point). */
|
|
13
|
+
allowHalf?: boolean;
|
|
14
|
+
size?: RatingSize;
|
|
15
|
+
/** Attribut name (utile dans un formulaire / pour la sémantique radio). */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Étiquette accessible du groupe. */
|
|
18
|
+
label?: string;
|
|
19
|
+
class?: string;
|
|
20
|
+
};
|
|
21
|
+
declare const Rating: import("svelte").Component<RatingProps, {}, "">;
|
|
22
|
+
type Rating = ReturnType<typeof Rating>;
|
|
23
|
+
export default Rating;
|
|
24
|
+
//# sourceMappingURL=Rating.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rating.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Rating.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAI9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG;IAC9E,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA2GJ,QAAA,MAAM,MAAM,iDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type SlideIndicatorVariant = "dots" | "bars";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
7
|
+
|
|
8
|
+
type SlideIndicatorProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange"> & {
|
|
9
|
+
/** Nombre total de diapositives. */
|
|
10
|
+
count: number;
|
|
11
|
+
/** Index de la diapositive courante (0-based). */
|
|
12
|
+
current?: number;
|
|
13
|
+
/** Appelé avec l'index ciblé au clic ou au clavier. */
|
|
14
|
+
onChange?: (index: number) => void;
|
|
15
|
+
size?: "sm" | "md" | "lg";
|
|
16
|
+
variant?: SlideIndicatorVariant;
|
|
17
|
+
/** Préfixe d'étiquette accessible de chaque point ("Diapositive 1"...). */
|
|
18
|
+
label?: string;
|
|
19
|
+
class?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
count,
|
|
24
|
+
current = 0,
|
|
25
|
+
onChange,
|
|
26
|
+
size = "md",
|
|
27
|
+
variant = "dots",
|
|
28
|
+
label = "Diapositive",
|
|
29
|
+
class: className,
|
|
30
|
+
...rest
|
|
31
|
+
}: SlideIndicatorProps = $props();
|
|
32
|
+
|
|
33
|
+
const classes = $derived(
|
|
34
|
+
[
|
|
35
|
+
"st-slideIndicator",
|
|
36
|
+
`st-slideIndicator--${size}`,
|
|
37
|
+
`st-slideIndicator--${variant}`,
|
|
38
|
+
className
|
|
39
|
+
]
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.join(" ")
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const items = $derived(Array.from({ length: Math.max(0, count) }, (_, i) => i));
|
|
45
|
+
|
|
46
|
+
function select(index: number) {
|
|
47
|
+
if (index < 0 || index >= count || index === current) return;
|
|
48
|
+
onChange?.(index);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function onKeyDown(event: KeyboardEvent, index: number) {
|
|
52
|
+
let target = index;
|
|
53
|
+
switch (event.key) {
|
|
54
|
+
case "ArrowRight":
|
|
55
|
+
case "ArrowDown":
|
|
56
|
+
target = Math.min(count - 1, index + 1);
|
|
57
|
+
break;
|
|
58
|
+
case "ArrowLeft":
|
|
59
|
+
case "ArrowUp":
|
|
60
|
+
target = Math.max(0, index - 1);
|
|
61
|
+
break;
|
|
62
|
+
case "Home":
|
|
63
|
+
target = 0;
|
|
64
|
+
break;
|
|
65
|
+
case "End":
|
|
66
|
+
target = count - 1;
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
select(target);
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<div {...rest} class={classes} role="tablist" aria-label={label}>
|
|
77
|
+
{#each items as index (index)}
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="st-slideIndicator__dot"
|
|
81
|
+
class:st-slideIndicator__dot--current={index === current}
|
|
82
|
+
role="tab"
|
|
83
|
+
aria-selected={index === current ? "true" : "false"}
|
|
84
|
+
aria-current={index === current ? "true" : undefined}
|
|
85
|
+
aria-label={`${label} ${index + 1}`}
|
|
86
|
+
tabindex={index === current ? 0 : -1}
|
|
87
|
+
onclick={() => select(index)}
|
|
88
|
+
onkeydown={(event) => onKeyDown(event, index)}
|
|
89
|
+
></button>
|
|
90
|
+
{/each}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<style>
|
|
94
|
+
.st-slideIndicator {
|
|
95
|
+
align-items: center;
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.st-slideIndicator__dot {
|
|
101
|
+
background: var(--st-semantic-border-strong, var(--st-semantic-text-muted));
|
|
102
|
+
border: 0;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
opacity: 0.5;
|
|
105
|
+
padding: 0;
|
|
106
|
+
transition:
|
|
107
|
+
opacity var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
|
|
108
|
+
background-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
|
|
109
|
+
width var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.st-slideIndicator__dot:hover {
|
|
113
|
+
opacity: 0.8;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.st-slideIndicator__dot:focus-visible {
|
|
117
|
+
outline: 2px solid var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
|
|
118
|
+
outline-offset: 2px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.st-slideIndicator__dot--current {
|
|
122
|
+
background: var(--st-semantic-action-primary);
|
|
123
|
+
opacity: 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Variant dots: cercles */
|
|
127
|
+
.st-slideIndicator--dots .st-slideIndicator__dot {
|
|
128
|
+
border-radius: 50%;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.st-slideIndicator--dots.st-slideIndicator--sm .st-slideIndicator__dot {
|
|
132
|
+
height: 0.375rem;
|
|
133
|
+
width: 0.375rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.st-slideIndicator--dots.st-slideIndicator--md .st-slideIndicator__dot {
|
|
137
|
+
height: 0.5rem;
|
|
138
|
+
width: 0.5rem;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.st-slideIndicator--dots.st-slideIndicator--lg .st-slideIndicator__dot {
|
|
142
|
+
height: 0.75rem;
|
|
143
|
+
width: 0.75rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Variant bars: barres, la courante s'allonge */
|
|
147
|
+
.st-slideIndicator--bars .st-slideIndicator__dot {
|
|
148
|
+
border-radius: 999px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.st-slideIndicator--bars.st-slideIndicator--sm .st-slideIndicator__dot {
|
|
152
|
+
height: 0.25rem;
|
|
153
|
+
width: 0.75rem;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.st-slideIndicator--bars.st-slideIndicator--md .st-slideIndicator__dot {
|
|
157
|
+
height: 0.3125rem;
|
|
158
|
+
width: 1rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.st-slideIndicator--bars.st-slideIndicator--lg .st-slideIndicator__dot {
|
|
162
|
+
height: 0.375rem;
|
|
163
|
+
width: 1.25rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.st-slideIndicator--bars .st-slideIndicator__dot--current {
|
|
167
|
+
width: 1.75rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.st-slideIndicator--bars.st-slideIndicator--lg .st-slideIndicator__dot--current {
|
|
171
|
+
width: 2.25rem;
|
|
172
|
+
}
|
|
173
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type SlideIndicatorVariant = "dots" | "bars";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
type SlideIndicatorProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange"> & {
|
|
4
|
+
/** Nombre total de diapositives. */
|
|
5
|
+
count: number;
|
|
6
|
+
/** Index de la diapositive courante (0-based). */
|
|
7
|
+
current?: number;
|
|
8
|
+
/** Appelé avec l'index ciblé au clic ou au clavier. */
|
|
9
|
+
onChange?: (index: number) => void;
|
|
10
|
+
size?: "sm" | "md" | "lg";
|
|
11
|
+
variant?: SlideIndicatorVariant;
|
|
12
|
+
/** Préfixe d'étiquette accessible de chaque point ("Diapositive 1"...). */
|
|
13
|
+
label?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
};
|
|
16
|
+
declare const SlideIndicator: import("svelte").Component<SlideIndicatorProps, {}, "">;
|
|
17
|
+
type SlideIndicator = ReturnType<typeof SlideIndicator>;
|
|
18
|
+
export default SlideIndicator;
|
|
19
|
+
//# sourceMappingURL=SlideIndicator.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlideIndicator.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SlideIndicator.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,MAAM,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,mBAAmB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG;IACtF,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuEJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|