@xleddyl/443-components-vue 0.9.1 → 0.9.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/README.md CHANGED
@@ -509,7 +509,7 @@ A large dialog (60rem) with a section nav on the left and the content on the rig
509
509
  - Built-in sections, used when no `#section-<id>` slot fills them: `account` (profile card, your `#account-fields`, a sign out button) and `appearance` (`AppearanceSettings`: theme cards light, dark, system and preset cards; `presets` limits or hides them).
510
510
  - `@sign-out` adds a Sign out item at the bottom of the nav and in the account section.
511
511
  - Labels: `title` (accessible name of the dialog, default `Settings`), `closeLabel`, `navLabel`, `signOutLabel`, `accountLabel`, `accountText`, `appearanceLabel`, `appearanceText`, `sessionLabel`, `sessionText`, `themeLabel`, `themeText`, `presetLabel`, `presetText`.
512
- - Subpages: `#section-<id>` and `#actions-<id>` get `page`, `pages`, `openPage({ id, label, description? })` and `back()`. With a page open, the header shows a breadcrumb from the section to the page, and the title and description of the page; a click on a breadcrumb goes back to it. `v-model:pages` binds the stack (it also works unbound). Changing section clears it. Render the detail when `page` is set, the list otherwise.
512
+ - Subpages: `#section-<id>` and `#actions-<id>` get `page`, `pages`, `openPage({ id, label })` and `back()`. With a page open, the header title becomes the path (`Members > Grace Hopper`, same size, only the last part in the text color, earlier parts are buttons that go back to them), the section description stays, and a Back button (`backLabel`, default `common.back`) shows on the right before `#actions-<id>`. `v-model:pages` binds the stack (it also works unbound). Changing section clears it. Render the detail when `page` is set, the list otherwise.
513
513
  - `hide-session` hides the Session group (sign out) in the account section; the Sign out item in the nav stays. `beforeClose` works as on Modal.
514
514
  - Accessibility: native modal `<dialog>` (focus trap, Escape, focus returns to the trigger), the nav is a labelled `nav` of buttons with `aria-current` and `aria-controls`, the content is a `section` labelled by its heading.
515
515
 
@@ -98,19 +98,42 @@
98
98
  >
99
99
  <header class="k-settings-modal-header">
100
100
  <div class="k-settings-modal-heading">
101
- <Breadcrumbs
102
- v-if="currentPage"
103
- class="k-settings-modal-breadcrumbs"
104
- :items="breadcrumbItems"
105
- />
106
101
  <h2 :id="`${baseId}-title`" class="k-settings-modal-title">
107
- {{ currentPage?.label ?? active.label }}
102
+ <template v-if="currentPage">
103
+ <template v-for="(crumb, index) in trail" :key="index">
104
+ <button
105
+ type="button"
106
+ class="k-settings-modal-crumb"
107
+ @click="goTo(index)"
108
+ >
109
+ {{ crumb }}
110
+ </button>
111
+ <Icon name="chevron-right" class="k-settings-modal-crumb-separator" />
112
+ </template>
113
+ <span class="k-settings-modal-crumb is-current">
114
+ {{ currentPage.label }}
115
+ </span>
116
+ </template>
117
+ <template v-else>{{ active.label }}</template>
108
118
  </h2>
109
- <p v-if="headingDescription" class="k-settings-modal-description">
110
- {{ headingDescription }}
119
+ <p v-if="active.description" class="k-settings-modal-description">
120
+ {{ active.description }}
111
121
  </p>
112
122
  </div>
113
- <div v-if="$slots[`actions-${active.id}`]" class="k-settings-modal-actions">
123
+ <div
124
+ v-if="currentPage || $slots[`actions-${active.id}`]"
125
+ class="k-settings-modal-actions"
126
+ >
127
+ <Button
128
+ v-if="currentPage"
129
+ variant="ghost"
130
+ tone="neutral"
131
+ size="sm"
132
+ icon="arrow-left"
133
+ @click="back"
134
+ >
135
+ {{ backText }}
136
+ </Button>
114
137
  <slot :name="`actions-${active.id}`" v-bind="slotProps(active, close)" />
115
138
  </div>
116
139
  </header>
@@ -186,7 +209,6 @@ import Icon from "../core/Icon.vue";
186
209
  import Avatar from "../data/Avatar.vue";
187
210
  import Badge from "../data/Badge.vue";
188
211
  import Modal from "../overlays/Modal.vue";
189
- import Breadcrumbs from "../layout/Breadcrumbs.vue";
190
212
  import AppearanceSettings from "./AppearanceSettings.vue";
191
213
  import SettingsGroup from "./SettingsGroup.vue";
192
214
  const props = defineProps({
@@ -195,6 +217,7 @@ const props = defineProps({
195
217
  presets: { type: [Array, Boolean], required: false, default: () => THEME_PRESETS },
196
218
  title: { type: String, required: false },
197
219
  closeLabel: { type: String, required: false },
220
+ backLabel: { type: String, required: false },
198
221
  navLabel: { type: String, required: false },
199
222
  signOutLabel: { type: String, required: false },
200
223
  sessionLabel: { type: String, required: false },
@@ -219,6 +242,7 @@ const pages = defineModel("pages", { type: Array, ...{ default: () => [] } });
219
242
  const { t } = useLocale();
220
243
  const titleText = computed(() => props.title ?? t("settings.title"));
221
244
  const closeText = computed(() => props.closeLabel ?? t("settings.close"));
245
+ const backText = computed(() => props.backLabel ?? t("common.back"));
222
246
  const navText = computed(() => props.navLabel ?? t("settings.sections"));
223
247
  const signOutText = computed(() => props.signOutLabel ?? t("settings.signOut"));
224
248
  const sessionLabelText = computed(() => props.sessionLabel ?? t("settings.session"));
@@ -267,9 +291,6 @@ const activeId = computed(() => {
267
291
  });
268
292
  const active = computed(() => sectionList.value.find((item) => item.id === activeId.value));
269
293
  const currentPage = computed(() => pages.value.at(-1));
270
- const headingDescription = computed(
271
- () => currentPage.value ? currentPage.value.description : active.value?.description
272
- );
273
294
  function goTo(depth) {
274
295
  pages.value = pages.value.slice(0, depth);
275
296
  }
@@ -279,12 +300,9 @@ function openPage(page) {
279
300
  function back() {
280
301
  goTo(pages.value.length - 1);
281
302
  }
282
- const breadcrumbItems = computed(() => [
283
- { label: active.value?.label ?? "", onClick: () => goTo(0) },
284
- ...pages.value.map((page, index) => ({
285
- label: page.label,
286
- onClick: index < pages.value.length - 1 ? () => goTo(index + 1) : void 0
287
- }))
303
+ const trail = computed(() => [
304
+ active.value?.label ?? "",
305
+ ...pages.value.slice(0, -1).map((page) => page.label)
288
306
  ]);
289
307
  function slotProps(item, close) {
290
308
  return { section: item, close, page: currentPage.value, pages: pages.value, openPage, back };
@@ -16,6 +16,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
16
16
  presets?: readonly ThemePreset[] | false;
17
17
  title?: string | undefined;
18
18
  closeLabel?: string;
19
+ backLabel?: string;
19
20
  navLabel?: string;
20
21
  signOutLabel?: string;
21
22
  sessionLabel?: string;
@@ -257,6 +258,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
257
258
  presets?: readonly ThemePreset[] | false;
258
259
  title?: string | undefined;
259
260
  closeLabel?: string;
261
+ backLabel?: string;
260
262
  navLabel?: string;
261
263
  signOutLabel?: string;
262
264
  sessionLabel?: string;
@@ -33,7 +33,6 @@ export interface SettingsModalSection {
33
33
  export interface SettingsModalPage {
34
34
  id: string;
35
35
  label: string;
36
- description?: string;
37
36
  }
38
37
  export interface SettingsSectionSlotProps {
39
38
  section: SettingsModalSection;
@@ -161,10 +161,6 @@
161
161
  min-width: 0;
162
162
  }
163
163
 
164
- .k-settings-modal-breadcrumbs {
165
- margin-bottom: 0.25rem;
166
- }
167
-
168
164
  .k-settings-modal-title {
169
165
  font-size: 0.9375rem;
170
166
  font-weight: var(--k-font-weight-strong, 600);
@@ -173,6 +169,44 @@
173
169
  color: var(--k-text);
174
170
  }
175
171
 
172
+ .k-settings-modal-title:has(> .k-settings-modal-crumb) {
173
+ display: flex;
174
+ flex-wrap: wrap;
175
+ align-items: center;
176
+ gap: 0.25rem;
177
+ }
178
+
179
+ .k-settings-modal-crumb {
180
+ padding: 0;
181
+ font: inherit;
182
+ letter-spacing: inherit;
183
+ color: var(--k-text-muted);
184
+ background: none;
185
+ border: 0;
186
+ border-radius: var(--k-radius-xs);
187
+ cursor: pointer;
188
+ transition: color var(--k-duration) var(--k-ease);
189
+ }
190
+
191
+ .k-settings-modal-crumb:hover {
192
+ color: var(--k-text);
193
+ }
194
+
195
+ .k-settings-modal-crumb:focus-visible {
196
+ outline: 2px solid var(--k-ring);
197
+ outline-offset: 2px;
198
+ }
199
+
200
+ .k-settings-modal-crumb.is-current {
201
+ color: var(--k-text);
202
+ cursor: default;
203
+ }
204
+
205
+ .k-settings-modal-crumb-separator {
206
+ --k-icon-size: 0.875rem;
207
+ color: var(--k-text-dimmed);
208
+ }
209
+
176
210
  .k-settings-modal-description {
177
211
  font-size: 0.8125rem;
178
212
  line-height: 1.4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xleddyl/443-components-vue",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Compact, scoped dashboard components for Vue and Nuxt",
5
5
  "license": "MIT",
6
6
  "author": "Edoardo Alberti (https://github.com/xleddyl)",