@xleddyl/443-components-vue 0.4.0 → 0.5.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.
@@ -79,7 +79,7 @@
79
79
  variant="ghost"
80
80
  tone="neutral"
81
81
  :aria-label="dismissText"
82
- @click="dismiss(entry.id)"
82
+ @click="dismissFromToast(entry.id)"
83
83
  />
84
84
  </div>
85
85
  <span
@@ -122,6 +122,7 @@ const props = defineProps({
122
122
  position: { type: String, required: false, default: "bottom-right" },
123
123
  max: { type: Number, required: false, default: 4 },
124
124
  duration: { type: Number, required: false, default: 4e3 },
125
+ actionDuration: { type: Number, required: false, default: 1e4 },
125
126
  gap: { type: Number, required: false, default: 8 },
126
127
  expand: { type: Boolean, required: false },
127
128
  progress: { type: Boolean, required: false },
@@ -274,7 +275,8 @@ function syncEntries() {
274
275
  }
275
276
  const timers = /* @__PURE__ */ new Map();
276
277
  function durationOf(item) {
277
- return item.duration ?? props.duration;
278
+ if (item.duration !== void 0) return item.duration;
279
+ return item.action ? Math.max(props.duration, props.actionDuration) : props.duration;
278
280
  }
279
281
  function hasTimer(item) {
280
282
  const duration = durationOf(item);
@@ -343,8 +345,15 @@ function resumeTimers() {
343
345
  for (const [id, timer] of timers) if (timer.handle === void 0) run(id, timer);
344
346
  }
345
347
  watch(paused, (value) => value ? pauseTimers() : resumeTimers());
348
+ function actionHint(item) {
349
+ if (!item.action || !props.hotkey) return "";
350
+ return t("toaster.actionHint", {
351
+ hotkey: ariaKeyShortcuts(props.hotkey, false),
352
+ action: item.action.label
353
+ });
354
+ }
346
355
  function announce(item) {
347
- const text = [item.title, ...descriptionLines(item)].filter(Boolean).join(". ");
356
+ const text = [item.title, ...descriptionLines(item), actionHint(item)].filter(Boolean).join(". ");
348
357
  const target = item.tone === "danger" ? assertiveMessage : politeMessage;
349
358
  target.value = "";
350
359
  nextTick(() => target.value = text);
@@ -408,9 +417,29 @@ watch(active, async (value) => {
408
417
  function dismiss(id) {
409
418
  toast.dismiss(id);
410
419
  }
420
+ let returnFocus = null;
421
+ function focusInToaster() {
422
+ const active2 = document.activeElement;
423
+ return Boolean(active2 && toasterRef.value?.contains(active2));
424
+ }
425
+ function restoreFocus() {
426
+ const target = returnFocus;
427
+ returnFocus = null;
428
+ const next = toasterRef.value?.querySelector(
429
+ '.k-toast[data-visible="true"]:not([data-removed="true"]) button'
430
+ );
431
+ if (target?.isConnected) target.focus({ preventScroll: true });
432
+ else if (next) next.focus({ preventScroll: true });
433
+ if (!focusInToaster()) focusInside.value = false;
434
+ }
435
+ function dismissFromToast(id) {
436
+ const hadFocus = focusInToaster();
437
+ dismiss(id);
438
+ if (hadFocus) nextTick(restoreFocus);
439
+ }
411
440
  function runAction(item, event) {
412
441
  item.action?.onClick(event);
413
- if (!item.action?.keepOpen) dismiss(item.id);
442
+ if (!item.action?.keepOpen) dismissFromToast(item.id);
414
443
  }
415
444
  function onPointerEnter(event) {
416
445
  if (event.pointerType === "mouse") hovered.value = true;
@@ -451,6 +480,8 @@ function onVisibilityChange() {
451
480
  useHotkey(
452
481
  () => props.hotkey ? [props.hotkey] : [],
453
482
  () => {
483
+ if (!focusInToaster())
484
+ returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
454
485
  const first = toasterRef.value?.querySelector(
455
486
  '.k-toast[data-visible="true"] button'
456
487
  );
@@ -3,6 +3,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
3
3
  position?: ToasterPosition;
4
4
  max?: number;
5
5
  duration?: number;
6
+ actionDuration?: number;
6
7
  gap?: number;
7
8
  expand?: boolean;
8
9
  progress?: boolean;
@@ -214,6 +215,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
214
215
  position?: ToasterPosition;
215
216
  max?: number;
216
217
  duration?: number;
218
+ actionDuration?: number;
217
219
  gap?: number;
218
220
  expand?: boolean;
219
221
  progress?: boolean;
@@ -426,6 +428,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
426
428
  duration: number;
427
429
  position: ToasterPosition;
428
430
  gap: number;
431
+ actionDuration: number;
429
432
  hotkey: string | false;
430
433
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
431
434
  declare const _default: typeof __VLS_export;
@@ -10,6 +10,7 @@ export interface ToasterOptions {
10
10
  position?: ToasterPosition;
11
11
  max?: number;
12
12
  duration?: number;
13
+ actionDuration?: number;
13
14
  gap?: number;
14
15
  expand?: boolean;
15
16
  progress?: boolean;
@@ -29,6 +29,6 @@ export function matchesPath(current, target, exact) {
29
29
  const from = normalize(current);
30
30
  const to = normalize(target);
31
31
  if (from === to) return true;
32
- if (exact) return false;
32
+ if (exact || !to) return false;
33
33
  return from.startsWith(to === "/" ? "/" : `${to}/`) && to !== "/";
34
34
  }
@@ -287,7 +287,8 @@ export const en = {
287
287
  },
288
288
  toaster: {
289
289
  label: "Notifications",
290
- dismiss: "Dismiss notification"
290
+ dismiss: "Dismiss notification",
291
+ actionHint: "Press {hotkey} to reach {action}"
291
292
  },
292
293
  tree: {
293
294
  label: "Tree",
@@ -287,7 +287,8 @@ export const it = {
287
287
  },
288
288
  toaster: {
289
289
  label: "Notifiche",
290
- dismiss: "Chiudi notifica"
290
+ dismiss: "Chiudi notifica",
291
+ actionHint: "Premi {hotkey} per raggiungere {action}"
291
292
  },
292
293
  tree: {
293
294
  label: "Albero",
@@ -286,6 +286,7 @@ export interface KMessages {
286
286
  toaster: {
287
287
  label: string;
288
288
  dismiss: string;
289
+ actionHint: string;
289
290
  };
290
291
  tree: {
291
292
  label: string;
@@ -292,7 +292,7 @@ function filterCollection(data, option) {
292
292
  ([name, alias]) => keep(name) && alias.parent in icons
293
293
  )
294
294
  );
295
- return { prefix: data.prefix, icons, aliases, width: data.width, height: data.height };
295
+ return { prefix: option.prefix, icons, aliases, width: data.width, height: data.height };
296
296
  }
297
297
  function collectionSvg(collections, name) {
298
298
  const index = name.indexOf(":");
@@ -2,7 +2,7 @@
2
2
  --k-icon-size: 0.875rem;
3
3
  display: flex;
4
4
  flex-direction: column;
5
- gap: 0.4375rem;
5
+ gap: 0.375rem;
6
6
  min-width: 0;
7
7
  }
8
8
 
@@ -46,7 +46,7 @@
46
46
  .k-form-field-description {
47
47
  margin-top: -0.125rem;
48
48
  font-size: 0.75rem;
49
- line-height: 1.45;
49
+ line-height: 1.4;
50
50
  color: var(--k-text-muted);
51
51
  }
52
52
 
@@ -6,7 +6,8 @@
6
6
 
7
7
  .k-description-item {
8
8
  display: grid;
9
- gap: 0.25rem;
9
+ align-content: start;
10
+ gap: 0.125rem;
10
11
  min-width: 0;
11
12
  padding-block: 0.625rem;
12
13
  animation: k-row-in 360ms var(--k-ease-out) backwards;
@@ -51,6 +52,7 @@
51
52
  .k-description-details {
52
53
  display: flex;
53
54
  flex-direction: column;
55
+ align-items: flex-start;
54
56
  gap: 0.125rem;
55
57
  min-width: 0;
56
58
  font-size: 0.8125rem;
@@ -70,7 +72,7 @@
70
72
  @container (max-width: 32rem) {
71
73
  .k-description-list-inline .k-description-item {
72
74
  grid-template-columns: minmax(0, 1fr);
73
- gap: 0.25rem;
75
+ gap: 0.125rem;
74
76
  }
75
77
 
76
78
  .k-description-list-grid {
@@ -81,7 +83,7 @@
81
83
  @media (max-width: 559px) {
82
84
  .k-description-list-inline .k-description-item {
83
85
  grid-template-columns: minmax(0, 1fr);
84
- gap: 0.25rem;
86
+ gap: 0.125rem;
85
87
  }
86
88
 
87
89
  .k-description-list-grid {
@@ -254,6 +254,7 @@
254
254
  margin-top: -0.125rem;
255
255
  border: solid var(--k-accent-contrast);
256
256
  border-width: 0 0 2px 2px;
257
+ rotate: -45deg;
257
258
  opacity: 0;
258
259
  scale: 0.4;
259
260
  transition:
@@ -103,6 +103,7 @@
103
103
 
104
104
  .k-timeline-description {
105
105
  font-size: 0.8125rem;
106
+ line-height: 1.4;
106
107
  color: var(--k-text-muted);
107
108
  }
108
109
 
@@ -78,6 +78,7 @@
78
78
  flex-direction: column;
79
79
  gap: 0.125rem;
80
80
  min-width: 0;
81
+ line-height: 1.4;
81
82
  padding-top: 0.0625rem;
82
83
  }
83
84
 
@@ -4,7 +4,7 @@
4
4
  display: flex;
5
5
  flex-direction: column;
6
6
  align-items: center;
7
- gap: 0.375rem;
7
+ gap: 0.125rem;
8
8
  padding: 3.5rem 1.5rem;
9
9
  overflow: hidden;
10
10
  text-align: center;
@@ -34,7 +34,7 @@
34
34
  }
35
35
 
36
36
  .k-empty-sm {
37
- gap: 0.25rem;
37
+ gap: 0.125rem;
38
38
  padding: 2rem 1rem;
39
39
  }
40
40
 
@@ -44,7 +44,7 @@
44
44
  place-items: center;
45
45
  width: 2.75rem;
46
46
  height: 2.75rem;
47
- margin-bottom: 0.625rem;
47
+ margin-bottom: 0.875rem;
48
48
  border-radius: var(--k-radius-lg);
49
49
  background: var(--k-surface);
50
50
  box-shadow:
@@ -57,13 +57,14 @@
57
57
  --k-icon-size: 1rem;
58
58
  width: 2.25rem;
59
59
  height: 2.25rem;
60
- margin-bottom: 0.375rem;
60
+ margin-bottom: 0.5rem;
61
61
  border-radius: var(--k-radius-md);
62
62
  }
63
63
 
64
64
  .k-empty-title {
65
65
  font-size: 0.9375rem;
66
66
  font-weight: 600;
67
+ line-height: 1.3;
67
68
  color: var(--k-text);
68
69
  }
69
70
 
@@ -74,6 +75,7 @@
74
75
  .k-empty-description {
75
76
  max-width: 28rem;
76
77
  font-size: 0.8125rem;
78
+ line-height: 1.4;
77
79
  color: var(--k-text-muted);
78
80
  }
79
81
 
@@ -100,7 +100,7 @@
100
100
  .k-accordion-description {
101
101
  font-size: 0.75rem;
102
102
  font-weight: 400;
103
- line-height: 1.45;
103
+ line-height: 1.4;
104
104
  color: var(--k-text-muted);
105
105
  }
106
106
 
@@ -47,6 +47,10 @@
47
47
  padding: var(--k-card-pad) var(--k-card-pad) 0;
48
48
  }
49
49
 
50
+ .k-card-header:last-child {
51
+ padding-bottom: var(--k-card-pad);
52
+ }
53
+
50
54
  .k-card-pad-none > .k-card-header {
51
55
  padding: 0.875rem 1.125rem;
52
56
  border-bottom: 1px solid var(--k-line);
@@ -74,15 +78,21 @@
74
78
  min-width: 0;
75
79
  }
76
80
 
81
+ .k-card-icon + .k-card-heading {
82
+ justify-content: center;
83
+ min-height: 2rem;
84
+ }
85
+
77
86
  .k-card-title {
78
87
  font-size: 0.9375rem;
79
88
  font-weight: 600;
80
- line-height: 1.35;
89
+ line-height: 1.3;
81
90
  color: var(--k-text);
82
91
  }
83
92
 
84
93
  .k-card-description {
85
94
  font-size: 0.8125rem;
95
+ line-height: 1.4;
86
96
  color: var(--k-text-muted);
87
97
  }
88
98
 
@@ -102,7 +112,7 @@
102
112
  }
103
113
 
104
114
  .k-card-header + .k-card-body {
105
- padding-top: 0.875rem;
115
+ padding-top: min(0.875rem, var(--k-card-pad));
106
116
  }
107
117
 
108
118
  .k-card-pad-none > .k-card-header + .k-card-body {
@@ -45,7 +45,7 @@
45
45
  .k-page-header-text {
46
46
  display: flex;
47
47
  flex-direction: column;
48
- gap: 0.1875rem;
48
+ gap: 0.125rem;
49
49
  min-width: 0;
50
50
  }
51
51
 
@@ -67,6 +67,7 @@
67
67
  .k-page-header-description {
68
68
  max-width: 44rem;
69
69
  font-size: 0.8125rem;
70
+ line-height: 1.4;
70
71
  color: var(--k-text-muted);
71
72
  }
72
73
 
@@ -14,7 +14,9 @@
14
14
 
15
15
  .k-drawer-bottom {
16
16
  flex-direction: column;
17
+ align-items: center;
17
18
  justify-content: flex-end;
19
+ padding: 1rem 1rem 0;
18
20
  }
19
21
 
20
22
  .k-drawer-panel {
@@ -50,10 +52,12 @@
50
52
  }
51
53
 
52
54
  .k-drawer-bottom > .k-drawer-panel {
53
- --k-drawer-hidden: translateY(calc(100% + 1rem));
54
- max-width: none;
55
+ --k-drawer-hidden: translateY(100%);
56
+ max-width: var(--k-drawer-sheet-width, 50rem);
55
57
  height: auto;
56
58
  max-height: var(--k-drawer-size);
59
+ padding-bottom: env(safe-area-inset-bottom);
60
+ border-radius: var(--k-radius-xl) var(--k-radius-xl) 0 0;
57
61
  }
58
62
 
59
63
  .k-drawer-sm > .k-drawer-panel {
@@ -74,6 +78,7 @@
74
78
 
75
79
  .k-drawer-bottom.k-drawer-sm > .k-drawer-panel {
76
80
  --k-drawer-size: 30dvh;
81
+ --k-drawer-sheet-width: 32rem;
77
82
  }
78
83
 
79
84
  .k-drawer-bottom.k-drawer-md > .k-drawer-panel {
@@ -89,6 +94,14 @@
89
94
  --k-drawer-size: 100%;
90
95
  }
91
96
 
97
+ .k-drawer-bottom.k-drawer-xl > .k-drawer-panel {
98
+ --k-drawer-sheet-width: 64rem;
99
+ }
100
+
101
+ .k-drawer-bottom.k-drawer-full > .k-drawer-panel {
102
+ --k-drawer-sheet-width: none;
103
+ }
104
+
92
105
  .k-drawer-header {
93
106
  align-items: center;
94
107
  min-height: 3.5rem;
@@ -118,4 +131,12 @@
118
131
  .k-drawer-right > .k-drawer-panel {
119
132
  max-width: calc(100% - 2rem);
120
133
  }
134
+
135
+ .k-drawer.k-drawer-bottom {
136
+ padding: 2rem 0 0;
137
+ }
138
+
139
+ .k-drawer-bottom > .k-drawer-panel {
140
+ max-width: none;
141
+ }
121
142
  }
@@ -8,7 +8,7 @@
8
8
  max-height: none;
9
9
  margin: 0;
10
10
  padding: 1rem;
11
- overflow: hidden;
11
+ overflow: clip;
12
12
  color: var(--k-text-toned);
13
13
  background: transparent;
14
14
  border: 0;
@@ -152,7 +152,7 @@
152
152
  .k-drawer-heading {
153
153
  display: flex;
154
154
  flex-direction: column;
155
- gap: 0.25rem;
155
+ gap: 0.125rem;
156
156
  min-width: 0;
157
157
  padding-top: 0.125rem;
158
158
  }
@@ -176,7 +176,7 @@
176
176
  .k-drawer-title {
177
177
  font-size: 0.9375rem;
178
178
  font-weight: 600;
179
- line-height: 1.35;
179
+ line-height: 1.3;
180
180
  color: var(--k-text);
181
181
  letter-spacing: -0.015em;
182
182
  }
@@ -184,6 +184,7 @@
184
184
  .k-modal-description,
185
185
  .k-drawer-description {
186
186
  font-size: 0.8125rem;
187
+ line-height: 1.4;
187
188
  color: var(--k-text-muted);
188
189
  }
189
190
 
@@ -70,6 +70,10 @@
70
70
  color: var(--k-text);
71
71
  }
72
72
 
73
+ .k-auth-title + .k-auth-description {
74
+ margin-top: -0.25rem;
75
+ }
76
+
73
77
  .k-auth-description {
74
78
  max-width: 20rem;
75
79
  font-size: 0.8125rem;
@@ -63,6 +63,10 @@
63
63
  color: var(--k-text);
64
64
  }
65
65
 
66
+ .k-empty-page-title + .k-empty-page-description {
67
+ margin-top: -0.375rem;
68
+ }
69
+
66
70
  .k-empty-page-description {
67
71
  max-width: 26rem;
68
72
  font-size: 0.875rem;
@@ -61,6 +61,10 @@
61
61
  color: var(--k-text);
62
62
  }
63
63
 
64
+ .k-error-title + .k-error-message {
65
+ margin-top: -0.375rem;
66
+ }
67
+
64
68
  .k-error-message {
65
69
  font-size: 0.875rem;
66
70
  color: var(--k-text-muted);
@@ -170,13 +170,14 @@
170
170
  .k-settings-modal-title {
171
171
  font-size: 0.9375rem;
172
172
  font-weight: 600;
173
- line-height: 1.4;
173
+ line-height: 1.3;
174
174
  letter-spacing: -0.015em;
175
175
  color: var(--k-text);
176
176
  }
177
177
 
178
178
  .k-settings-modal-description {
179
179
  font-size: 0.8125rem;
180
+ line-height: 1.4;
180
181
  color: var(--k-text-muted);
181
182
  }
182
183
 
@@ -258,6 +259,7 @@
258
259
  .k-settings-group-label {
259
260
  font-family: var(--k-font-sans);
260
261
  font-size: 0.875rem;
262
+ line-height: 1.3;
261
263
  font-weight: 500;
262
264
  letter-spacing: normal;
263
265
  color: var(--k-text);
@@ -265,6 +267,7 @@
265
267
 
266
268
  .k-settings-group-description {
267
269
  font-size: 0.8125rem;
270
+ line-height: 1.4;
268
271
  color: var(--k-text-muted);
269
272
  }
270
273
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xleddyl/443-components-vue",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Compact, scoped dashboard components for Vue and Nuxt",
5
5
  "license": "MIT",
6
6
  "author": "Edoardo Alberti (https://github.com/xleddyl)",