@mozaic-ds/vue 2.14.0 → 2.15.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.
Files changed (35) hide show
  1. package/dist/mozaic-vue.css +1 -1
  2. package/dist/mozaic-vue.d.ts +922 -429
  3. package/dist/mozaic-vue.js +3128 -2461
  4. package/dist/mozaic-vue.js.map +1 -1
  5. package/dist/mozaic-vue.umd.cjs +5 -5
  6. package/dist/mozaic-vue.umd.cjs.map +1 -1
  7. package/package.json +4 -3
  8. package/src/components/DarkMode.mdx +115 -0
  9. package/src/components/actionlistbox/MActionListbox.spec.ts +6 -10
  10. package/src/components/actionlistbox/MActionListbox.vue +2 -11
  11. package/src/components/avatar/MAvatar.stories.ts +1 -1
  12. package/src/components/breadcrumb/MBreadcrumb.vue +2 -2
  13. package/src/components/pageheader/MPageHeader.spec.ts +12 -12
  14. package/src/components/pageheader/MPageHeader.stories.ts +9 -1
  15. package/src/components/pageheader/MPageHeader.vue +3 -6
  16. package/src/components/segmentedcontrol/MSegmentedControl.spec.ts +57 -25
  17. package/src/components/segmentedcontrol/MSegmentedControl.stories.ts +6 -19
  18. package/src/components/segmentedcontrol/MSegmentedControl.vue +27 -13
  19. package/src/components/segmentedcontrol/README.md +6 -3
  20. package/src/components/select/MSelect.vue +4 -3
  21. package/src/components/sidebar/stories/DefaultCase.stories.vue +2 -2
  22. package/src/components/sidebar/stories/README.md +8 -0
  23. package/src/components/sidebar/stories/WithExpandOnly.stories.vue +1 -1
  24. package/src/components/sidebar/stories/WithProfileInfoOnly.stories.vue +2 -2
  25. package/src/components/sidebar/stories/WithSingleLevel.stories.vue +2 -2
  26. package/src/components/stepperinline/MStepperInline.spec.ts +63 -28
  27. package/src/components/stepperinline/MStepperInline.stories.ts +18 -10
  28. package/src/components/stepperinline/MStepperInline.vue +24 -10
  29. package/src/components/stepperinline/README.md +6 -2
  30. package/src/components/tabs/MTabs.stories.ts +18 -0
  31. package/src/components/tabs/MTabs.vue +30 -14
  32. package/src/components/tabs/Mtabs.spec.ts +56 -10
  33. package/src/components/tabs/README.md +6 -3
  34. package/src/components/tileclickable/README.md +1 -1
  35. package/src/main.ts +9 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mozaic-ds/vue",
3
- "version": "2.14.0",
3
+ "version": "2.15.0",
4
4
  "type": "module",
5
5
  "description": "Mozaic-Vue is the Vue.js implementation of ADEO Design system",
6
6
  "author": "ADEO - ADEO Design system",
@@ -56,17 +56,18 @@
56
56
  "@storybook/addon-docs": "^10.0.4",
57
57
  "@storybook/addon-themes": "^10.0.4",
58
58
  "@storybook/vue3-vite": "^10.0.4",
59
- "@types/jsdom": "^27.0.0",
59
+ "@types/jsdom": "^28.0.0",
60
60
  "@vitejs/plugin-vue": "^6.0.1",
61
61
  "@vitest/coverage-v8": "^4.0.7",
62
62
  "@vitest/eslint-plugin": "^1.1.38",
63
63
  "@vue/eslint-config-prettier": "^10.2.0",
64
64
  "@vue/eslint-config-typescript": "^14.5.0",
65
65
  "@vue/test-utils": "^2.4.6",
66
- "eslint": "^9.22.0",
66
+ "eslint": "^10.0.2",
67
67
  "eslint-plugin-storybook": "^10.0.5",
68
68
  "eslint-plugin-vue": "^10.0.0",
69
69
  "eslint-plugin-vuejs-accessibility": "^2.4.1",
70
+ "globals": "^17.3.0",
70
71
  "husky": "^9.1.7",
71
72
  "jsdom": "^28.0.0",
72
73
  "libphonenumber-js": "^1.12.23",
@@ -0,0 +1,115 @@
1
+ import { Meta, Source } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="Dark Mode" />
4
+
5
+ # Dark Mode
6
+
7
+ A concise guide explaining **how dark mode works** with your CSS variables and **how to use it** in Storybook.
8
+
9
+ ---
10
+
11
+ ## What dark mode is (high‑level)
12
+
13
+ Dark mode is implemented with **two sets of CSS variables** (tokens):
14
+
15
+ - **Light** values live under `:root`.
16
+ - **Dark** values override under `:root[data-theme="dark"]`.
17
+
18
+ Components only reference tokens with `var(--token-name)` — switching theme is just toggling the `data-theme` attribute (no component code changes).
19
+
20
+ ---
21
+
22
+ ## Token structure (SCSS → CSS)
23
+
24
+ Your presets export SCSS like this:
25
+
26
+ <Source
27
+ language="scss"
28
+ dark
29
+ code={`
30
+ $root-selector: ':root' !default;
31
+ $dark-selector: '[data-theme="dark"]' !default;
32
+
33
+ #{$root-selector} {
34
+ /_ Light tokens _/
35
+ --color-background-primary: #ffffff;
36
+ --color-text-primary: #000000;
37
+ /_ … all your light variables … _/
38
+ }
39
+
40
+ #{$root-selector}#{$dark-selector} {
41
+ /_ Dark tokens _/
42
+ --color-background-primary: #191919;
43
+ --color-text-primary: #d9d9d9;
44
+ /_ … all your dark variables … _/
45
+ }
46
+ `}
47
+ />
48
+
49
+ After compilation, this becomes standard CSS:
50
+
51
+ <Source
52
+ language="css"
53
+ dark
54
+ code={`
55
+ :root {
56
+ /* light tokens */
57
+ }
58
+ :root[data-theme='dark'] {
59
+ /* dark tokens */
60
+ }
61
+ `}
62
+ />
63
+
64
+ > If you can’t (or don’t want to) target `:root`, you can pass a different `$root-selector` when building your theme and apply `data-theme="dark"` on that container instead.
65
+
66
+ ---
67
+
68
+ ## Using tokens inside components
69
+
70
+ To enable the dark mode you have to ensure to:
71
+
72
+ - Add the `data-theme` attribute in your root element with the value `dark`,
73
+ - Use variables — never hard‑code colors or sizes
74
+
75
+ ```html
76
+ <div class="root" data-theme="dark">…</div>
77
+ ```
78
+
79
+ <Source
80
+ language="sass"
81
+ dark
82
+ code={`
83
+ @use "@mozaic-ds/tokens" as *;
84
+
85
+ .mc-component: {
86
+ background-color: $--color-background-primary;
87
+ }
88
+
89
+ `}
90
+ />
91
+
92
+ When the theme changes, these values update automatically via CSS.
93
+
94
+ ---
95
+
96
+ ## Accessibility & good practices
97
+
98
+ - Aim for **WCAG AA** contrast at minimum; verify text vs. background pairs.
99
+ - Prefer **semantic tokens** (`--button-color-…`, `--color-text-…`) over raw color hexes.
100
+ - Keep all component styles expressed in tokens so the **theme switch has zero component logic**.
101
+
102
+ ---
103
+
104
+ ## Troubleshooting
105
+
106
+ - **Dark toggle does nothing** → Ensure the tokens were imported **before** component styles and that `data-theme="dark"` is set on the same selector the tokens target (usually `:root`).
107
+ - **Weird colors** → Search for hard‑coded values and replace them with tokens.
108
+ - **Variables undefined** → Check your build order and that the SCSS was compiled to CSS and loaded by Storybook.
109
+
110
+ ---
111
+
112
+ ## Summary
113
+
114
+ - Light tokens on `:root`, dark overrides on `:root[data-theme="dark"]`.
115
+ - Components read tokens with `var(--$token-name)` — no runtime branching required.
@@ -87,10 +87,7 @@ describe('MActionListbox', () => {
87
87
  });
88
88
 
89
89
  it('applies disabled class when item.disabled is true', () => {
90
- const disabledItems = [
91
- ...items,
92
- { label: 'Disabled', disabled: true },
93
- ];
90
+ const disabledItems = [...items, { label: 'Disabled', disabled: true }];
94
91
 
95
92
  const wrapper = mountComponent({ items: disabledItems });
96
93
 
@@ -101,20 +98,19 @@ describe('MActionListbox', () => {
101
98
 
102
99
  it('applies mc-listbox--bottom by default', () => {
103
100
  const wrapper = mountComponent();
104
- expect(wrapper.find('.mc-listbox').classes())
105
- .toContain('mc-listbox--bottom');
101
+ expect(wrapper.find('.mc-listbox').classes()).toContain(
102
+ 'mc-listbox--bottom',
103
+ );
106
104
  });
107
105
 
108
106
  it('applies mc-listbox--top when position is "top"', () => {
109
107
  const wrapper = mountComponent({ position: 'top' });
110
- expect(wrapper.find('.mc-listbox').classes())
111
- .toContain('mc-listbox--top');
108
+ expect(wrapper.find('.mc-listbox').classes()).toContain('mc-listbox--top');
112
109
  });
113
110
 
114
111
  it('applies mc-listbox--left when position is "left"', () => {
115
112
  const wrapper = mountComponent({ position: 'left' });
116
- expect(wrapper.find('.mc-listbox').classes())
117
- .toContain('mc-listbox--left');
113
+ expect(wrapper.find('.mc-listbox').classes()).toContain('mc-listbox--left');
118
114
  });
119
115
 
120
116
  it('emits "close" when close button is clicked', async () => {
@@ -61,12 +61,7 @@
61
61
  </template>
62
62
 
63
63
  <script setup lang="ts">
64
- import {
65
- useId,
66
- useTemplateRef,
67
- type Component,
68
- type VNode,
69
- } from 'vue';
64
+ import { useId, useTemplateRef, type Component, type VNode } from 'vue';
70
65
  import MIconButton from '../iconbutton/MIconButton.vue';
71
66
  import MDivider from '../divider/MDivider.vue';
72
67
  import { Cross24 } from '@mozaic-ds/icons-vue';
@@ -84,11 +79,7 @@ const props = withDefaults(
84
79
  /**
85
80
  * Defines the position of the listbox relative to its trigger or container.
86
81
  */
87
- position?:
88
- | 'top'
89
- | 'bottom'
90
- | 'left'
91
- | 'right';
82
+ position?: 'top' | 'bottom' | 'left' | 'right';
92
83
  /**
93
84
  * An array of objects that allows you to provide all the data needed to generate the content for each item.
94
85
  */
@@ -17,7 +17,7 @@ const meta: Meta<typeof MAvatar> = {
17
17
  },
18
18
  args: {
19
19
  default: `
20
- <img src="/images/Avatar.png" alt="Dieter Rams" loading="lazy"/>
20
+ <img src="/mozaic-vue/images/Avatar.png" alt="Dieter Rams" loading="lazy"/>
21
21
  `,
22
22
  },
23
23
  render: (args) => ({
@@ -18,8 +18,8 @@
18
18
  :aria-current="isLastLink(index) ? 'page' : undefined"
19
19
  >
20
20
  {{ link.label }}
21
- <template v-if="index !== (links.length - 1)" #icon>
22
- <ChevronRight20/>
21
+ <template v-if="index !== links.length - 1" #icon>
22
+ <ChevronRight20 />
23
23
  </template>
24
24
  </MLink>
25
25
  </li>
@@ -98,9 +98,9 @@ describe('MPageHeader', () => {
98
98
  },
99
99
  });
100
100
 
101
- expect(
102
- wrapper.findComponent({ name: 'MStatusBadge' }).exists(),
103
- ).toBe(true);
101
+ expect(wrapper.findComponent({ name: 'MStatusBadge' }).exists()).toBe(
102
+ true,
103
+ );
104
104
  });
105
105
 
106
106
  it('does not render status badge if statusLabel is missing', () => {
@@ -111,9 +111,9 @@ describe('MPageHeader', () => {
111
111
  },
112
112
  });
113
113
 
114
- expect(
115
- wrapper.findComponent({ name: 'MStatusBadge' }).exists(),
116
- ).toBe(false);
114
+ expect(wrapper.findComponent({ name: 'MStatusBadge' }).exists()).toBe(
115
+ false,
116
+ );
117
117
  });
118
118
 
119
119
  it('renders extra info when provided', () => {
@@ -124,9 +124,9 @@ describe('MPageHeader', () => {
124
124
  },
125
125
  });
126
126
 
127
- expect(
128
- wrapper.find('.mc-page-header__extra-info').text(),
129
- ).toBe('Details');
127
+ expect(wrapper.find('.mc-page-header__extra-info').text()).toBe(
128
+ 'Details',
129
+ );
130
130
  });
131
131
 
132
132
  it('renders info wrapper only when status or extraInfo exists', () => {
@@ -134,9 +134,9 @@ describe('MPageHeader', () => {
134
134
  props: { title: 'My Page' },
135
135
  });
136
136
 
137
- expect(
138
- wrapper.find('.mc-page-header__info-wrapper').exists(),
139
- ).toBe(false);
137
+ expect(wrapper.find('.mc-page-header__info-wrapper').exists()).toBe(
138
+ false,
139
+ );
140
140
  });
141
141
  });
142
142
  });
@@ -63,7 +63,15 @@ const meta: Meta<typeof MPageHeader> = {
63
63
  `,
64
64
  },
65
65
  render: (args) => ({
66
- components: { MPageHeader, MTabs, MIconButton, Search24, HelpCircle24, Notification24, MSelect },
66
+ components: {
67
+ MPageHeader,
68
+ MTabs,
69
+ MIconButton,
70
+ Search24,
71
+ HelpCircle24,
72
+ Notification24,
73
+ MSelect,
74
+ },
67
75
  setup() {
68
76
  const handleBackButtonClick = action('back');
69
77
  const handleMenuClick = action('toggle-menu');
@@ -23,10 +23,7 @@
23
23
  {{ title }}
24
24
  </span>
25
25
 
26
- <div
27
- v-if="status || extraInfo"
28
- class="mc-page-header__info-wrapper"
29
- >
26
+ <div v-if="status || extraInfo" class="mc-page-header__info-wrapper">
30
27
  <MStatusBadge
31
28
  v-if="statusLabel && status"
32
29
  :label="statusLabel"
@@ -55,13 +52,13 @@
55
52
  </MIconButton>
56
53
 
57
54
  <div class="mc-page-header__actions-content">
58
- <slot name="actions"/>
55
+ <slot name="actions" />
59
56
  </div>
60
57
  </div>
61
58
  </div>
62
59
 
63
60
  <div class="mc-page-header__tabs">
64
- <slot name="tabs"/>
61
+ <slot name="tabs" />
65
62
  </div>
66
63
  </div>
67
64
  </template>
@@ -4,9 +4,9 @@ import MSegmentedControl from './MSegmentedControl.vue';
4
4
 
5
5
  describe('MSegmentedControl.vue', () => {
6
6
  const segments = [
7
- { label: 'First' },
8
- { label: 'Second' },
9
- { label: 'Third' },
7
+ { id: '1', label: 'First' },
8
+ { id: '2', label: 'Second' },
9
+ { id: '3', label: 'Third' },
10
10
  ];
11
11
 
12
12
  it('renders segments with correct labels', () => {
@@ -23,42 +23,74 @@ describe('MSegmentedControl.vue', () => {
23
23
 
24
24
  it('sets default active segment based on modelValue prop', () => {
25
25
  const wrapper = mount(MSegmentedControl, {
26
- props: { segments, modelValue: 1 },
26
+ props: { segments, modelValue: '1' },
27
27
  });
28
28
 
29
29
  const buttons = wrapper.findAll('button');
30
- expect(buttons[1].classes()).toContain(
30
+ expect(buttons[0].classes()).toContain(
31
31
  'mc-segmented-control__segment--selected',
32
32
  );
33
- expect(buttons[1].attributes('aria-checked')).toBe('true');
33
+ expect(buttons[0].attributes('aria-checked')).toBe('true');
34
34
  });
35
35
 
36
- it('emits update:modelValue and changes active segment on click', async () => {
37
- const wrapper = mount(MSegmentedControl, {
38
- props: { segments, modelValue: 0 },
36
+ describe('Segment selection - string type model', () => {
37
+ it('emits update:modelValue and changes active segment on click', async () => {
38
+ const wrapper = mount(MSegmentedControl, {
39
+ props: { segments, modelValue: '1' },
40
+ });
41
+
42
+ const buttons = wrapper.findAll('button');
43
+ await buttons[2].trigger('click');
44
+
45
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
46
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual(['3']);
47
+
48
+ expect(buttons[2].classes()).toContain(
49
+ 'mc-segmented-control__segment--selected',
50
+ );
51
+ expect(buttons[2].attributes('aria-checked')).toBe('true');
39
52
  });
40
53
 
41
- const buttons = wrapper.findAll('button');
42
- await buttons[2].trigger('click');
54
+ it('does not emit update event if clicking already active segment', async () => {
55
+ const wrapper = mount(MSegmentedControl, {
56
+ props: { segments, modelValue: '2' },
57
+ });
43
58
 
44
- expect(wrapper.emitted('update:modelValue')).toBeTruthy();
45
- expect(wrapper.emitted('update:modelValue')![0]).toEqual([2]);
59
+ const buttons = wrapper.findAll('button');
60
+ await buttons[1].trigger('click');
46
61
 
47
- expect(buttons[2].classes()).toContain(
48
- 'mc-segmented-control__segment--selected',
49
- );
50
- expect(buttons[2].attributes('aria-checked')).toBe('true');
62
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
63
+ });
51
64
  });
52
65
 
53
- it('does not emit update event if clicking already active segment', async () => {
54
- const wrapper = mount(MSegmentedControl, {
55
- props: { segments, modelValue: 1 },
66
+ describe('Segment selection - number type model', () => {
67
+ it('emits update:modelValue and changes active segment on click', async () => {
68
+ const wrapper = mount(MSegmentedControl, {
69
+ props: { segments, modelValue: 0 },
70
+ });
71
+
72
+ const buttons = wrapper.findAll('button');
73
+ await buttons[2].trigger('click');
74
+
75
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
76
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual([2]);
77
+
78
+ expect(buttons[2].classes()).toContain(
79
+ 'mc-segmented-control__segment--selected',
80
+ );
81
+ expect(buttons[2].attributes('aria-checked')).toBe('true');
56
82
  });
57
83
 
58
- const buttons = wrapper.findAll('button');
59
- await buttons[1].trigger('click');
84
+ it('does not emit update event if clicking already active segment', async () => {
85
+ const wrapper = mount(MSegmentedControl, {
86
+ props: { segments, modelValue: 1 },
87
+ });
60
88
 
61
- expect(wrapper.emitted('update:modelValue')).toBeFalsy();
89
+ const buttons = wrapper.findAll('button');
90
+ await buttons[1].trigger('click');
91
+
92
+ expect(wrapper.emitted('update:modelValue')).toBeFalsy();
93
+ });
62
94
  });
63
95
 
64
96
  it('applies full width class when full prop is true', () => {
@@ -87,7 +119,7 @@ describe('MSegmentedControl.vue', () => {
87
119
 
88
120
  it('updates active segment when modelValue prop changes', async () => {
89
121
  const wrapper = mount(MSegmentedControl, {
90
- props: { segments, modelValue: 0 },
122
+ props: { segments, modelValue: '1' },
91
123
  });
92
124
 
93
125
  const buttons = wrapper.findAll('button');
@@ -95,7 +127,7 @@ describe('MSegmentedControl.vue', () => {
95
127
  'mc-segmented-control__segment--selected',
96
128
  );
97
129
 
98
- await wrapper.setProps({ modelValue: 2 });
130
+ await wrapper.setProps({ modelValue: '3' });
99
131
 
100
132
  expect(buttons[2].classes()).toContain(
101
133
  'mc-segmented-control__segment--selected',
@@ -15,17 +15,22 @@ const meta: Meta<typeof MSegmentedControl> = {
15
15
  },
16
16
  },
17
17
  args: {
18
+ modelValue: 'label1',
18
19
  segments: [
19
20
  {
21
+ id: 'label1',
20
22
  label: 'Label',
21
23
  },
22
24
  {
25
+ id: 'label2',
23
26
  label: 'Label',
24
27
  },
25
28
  {
29
+ id: 'label3',
26
30
  label: 'Label',
27
31
  },
28
32
  {
33
+ id: 'label4',
29
34
  label: 'Label',
30
35
  },
31
36
  ],
@@ -40,6 +45,7 @@ const meta: Meta<typeof MSegmentedControl> = {
40
45
  template: `
41
46
  <MSegmentedControl
42
47
  v-bind="args"
48
+ v-model="args.modelValue"
43
49
  @update:modelValue="handleUpdate"
44
50
  ></MSegmentedControl>
45
51
  `,
@@ -50,25 +56,6 @@ type Story = StoryObj<typeof MSegmentedControl>;
50
56
 
51
57
  export const Default: Story = {};
52
58
 
53
- export const Icons: Story = {
54
- args: {
55
- segments: [
56
- {
57
- label: 'Label',
58
- },
59
- {
60
- label: 'Label',
61
- },
62
- {
63
- label: 'Label',
64
- },
65
- {
66
- label: 'Label',
67
- },
68
- ],
69
- },
70
- };
71
-
72
59
  export const Size: Story = {
73
60
  args: { size: 'm' },
74
61
  };
@@ -6,11 +6,14 @@
6
6
  type="button"
7
7
  class="mc-segmented-control__segment"
8
8
  :class="{
9
- 'mc-segmented-control__segment--selected': isSegmentSelected(index),
9
+ 'mc-segmented-control__segment--selected': isSegmentSelected(
10
+ index,
11
+ segment.id,
12
+ ),
10
13
  }"
11
- :aria-checked="isSegmentSelected(index)"
14
+ :aria-checked="isSegmentSelected(index, segment.id)"
12
15
  role="radio"
13
- @click="onClickSegment(index)"
16
+ @click="onClickSegment(index, segment.id)"
14
17
  >
15
18
  {{ segment.label }}
16
19
  </button>
@@ -25,9 +28,12 @@ import { computed, ref, watch } from 'vue';
25
28
  const props = withDefaults(
26
29
  defineProps<{
27
30
  /**
28
- * The selected segment index, bound via v-model.
31
+ * Defines the currently active tab.
32
+ *
33
+ * - If a `number` is provided, it represents the index of the segment.
34
+ * - If a `string` is provided, it must match the `id` of one of the segments.
29
35
  */
30
- modelValue?: number;
36
+ modelValue?: string | number;
31
37
  /**
32
38
  * if `true`, the segmented control take the full width.
33
39
  */
@@ -40,6 +46,10 @@ const props = withDefaults(
40
46
  * An array of objects that allows you to provide all the data needed to generate the content for each segment.
41
47
  */
42
48
  segments: Array<{
49
+ /**
50
+ * Unique identifier for the segment.
51
+ */
52
+ id?: string;
43
53
  /**
44
54
  * The label displayed for the segment.
45
55
  */
@@ -59,7 +69,7 @@ const classObject = computed(() => {
59
69
  };
60
70
  });
61
71
 
62
- const modelValue = ref(props.modelValue);
72
+ const modelValue = ref<string | number | undefined>(props.modelValue);
63
73
 
64
74
  watch(
65
75
  () => props.modelValue,
@@ -68,22 +78,26 @@ watch(
68
78
  },
69
79
  );
70
80
 
71
- const onClickSegment = (index: number) => {
72
- if (index !== modelValue.value) {
73
- modelValue.value = index;
74
- emit('update:modelValue', index);
81
+ const onClickSegment = (index: number, id?: string) => {
82
+ const value = typeof props.modelValue === 'number' ? index : id;
83
+
84
+ if (value !== modelValue.value) {
85
+ modelValue.value = value;
86
+ emit('update:modelValue', value);
75
87
  }
76
88
  };
77
89
 
78
- const isSegmentSelected = (index: number) => {
79
- return modelValue.value === index;
90
+ const isSegmentSelected = (index: number, id?: string) => {
91
+ const value = typeof props.modelValue === 'number' ? index : id;
92
+
93
+ return modelValue.value === value;
80
94
  };
81
95
 
82
96
  const emit = defineEmits<{
83
97
  /**
84
98
  * Emits when the selected segment changes, updating the modelValue prop.
85
99
  */
86
- (on: 'update:modelValue', value: number): void;
100
+ (on: 'update:modelValue', value?: string | number): void;
87
101
  }>();
88
102
  </script>
89
103
 
@@ -7,13 +7,16 @@ A Segmented Control allows users to switch between multiple options or views wit
7
7
 
8
8
  | Name | Description | Type | Default |
9
9
  | --- | --- | --- | --- |
10
- | `modelValue` | The selected segment index, bound via v-model. | `number` | `0` |
10
+ | `modelValue` | Defines the currently active tab.
11
+
12
+ - If a `number` is provided, it represents the index of the segment.
13
+ - If a `string` is provided, it must match the `id` of one of the segments. | `string` `number` | `0` |
11
14
  | `full` | if `true`, the segmented control take the full width. | `boolean` | - |
12
15
  | `size` | Determines the size of the segmented control. | `"s"` `"m"` | `"s"` |
13
- | `segments*` | An array of objects that allows you to provide all the data needed to generate the content for each segment. | `{ label: string; }[]` | - |
16
+ | `segments*` | An array of objects that allows you to provide all the data needed to generate the content for each segment. | `{ id?: string` `undefined; label: string; }[]` | - |
14
17
 
15
18
  ## Events
16
19
 
17
20
  | Name | Description | Type |
18
21
  | --- | --- | --- |
19
- | `update:modelValue` | Emits when the selected segment changes, updating the modelValue prop. | [value: number] |
22
+ | `update:modelValue` | Emits when the selected segment changes, updating the modelValue prop. | [value?: string | number] |
@@ -1,7 +1,8 @@
1
1
  <template>
2
- <div :class="{
3
- 'mc-select': true,
4
- [`mc-select--${size}`]: props.size && props.size != 'm',
2
+ <div
3
+ :class="{
4
+ 'mc-select': true,
5
+ [`mc-select--${size}`]: props.size && props.size != 'm',
5
6
  }"
6
7
  >
7
8
  <select
@@ -5,7 +5,7 @@
5
5
  @close="emit('close')"
6
6
  >
7
7
  <template #header>
8
- <MSidebarHeader title="Adeo Design System" logo="/logo.svg" />
8
+ <MSidebarHeader title="Adeo Design System" logo="/mozaic-vue/logo.svg" />
9
9
  </template>
10
10
 
11
11
  <template #shortcuts>
@@ -49,7 +49,7 @@
49
49
  title="Dieter Rams"
50
50
  subtitle="Industrial designer"
51
51
  href="#"
52
- avatar="/images/Avatar.png"
52
+ avatar="/mozaic-vue/images/Avatar.png"
53
53
  @log-out="emit('log-out')"
54
54
  >
55
55
  <MSidebarNavItem
@@ -2,6 +2,14 @@
2
2
 
3
3
 
4
4
 
5
+ ## Events
6
+
7
+ | Name | Description | Type |
8
+ | --- | --- | --- |
9
+ | `update:modelValue` | - | `any[]` |
10
+ | `close` | - | `any[]` |
11
+ | `log-out` | - | `any[]` |
12
+
5
13
  ## Dependencies
6
14
 
7
15
  ### Depends on