@mozaic-ds/vue 2.10.0 → 2.12.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 (43) hide show
  1. package/dist/mozaic-vue.css +1 -1
  2. package/dist/mozaic-vue.d.ts +417 -212
  3. package/dist/mozaic-vue.js +1737 -1231
  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 +2 -2
  8. package/src/components/builtinmenu/MBuiltInMenu.spec.ts +111 -0
  9. package/src/components/builtinmenu/MBuiltInMenu.stories.ts +58 -0
  10. package/src/components/builtinmenu/MBuiltInMenu.vue +110 -0
  11. package/src/components/builtinmenu/README.md +18 -0
  12. package/src/components/checklistmenu/MCheckListMenu.spec.ts +77 -0
  13. package/src/components/checklistmenu/MCheckListMenu.stories.ts +46 -0
  14. package/src/components/checklistmenu/MCheckListMenu.vue +61 -0
  15. package/src/components/checklistmenu/README.md +18 -0
  16. package/src/components/kpiitem/MKpiItem.spec.ts +71 -0
  17. package/src/components/kpiitem/MKpiItem.stories.ts +69 -0
  18. package/src/components/kpiitem/MKpiItem.vue +89 -0
  19. package/src/components/kpiitem/README.md +15 -0
  20. package/src/components/phonenumber/README.md +2 -0
  21. package/src/components/starrating/MStarRating.spec.ts +203 -0
  22. package/src/components/starrating/MStarRating.stories.ts +82 -0
  23. package/src/components/starrating/MStarRating.vue +187 -0
  24. package/src/components/starrating/README.md +31 -0
  25. package/src/components/statusbadge/README.md +1 -1
  26. package/src/components/statusdot/README.md +1 -1
  27. package/src/components/statusmessage/MStatusMessage.spec.ts +76 -0
  28. package/src/components/statusmessage/MStatusMessage.stories.ts +52 -0
  29. package/src/components/statusmessage/MStatusMessage.vue +70 -0
  30. package/src/components/statusmessage/README.md +11 -0
  31. package/src/components/statusnotification/README.md +1 -1
  32. package/src/components/steppercompact/MStepperCompact.spec.ts +98 -0
  33. package/src/components/steppercompact/MStepperCompact.stories.ts +43 -0
  34. package/src/components/steppercompact/MStepperCompact.vue +105 -0
  35. package/src/components/steppercompact/README.md +13 -0
  36. package/src/components/stepperinline/MStepperInline.spec.ts +78 -0
  37. package/src/components/stepperinline/MStepperInline.stories.ts +49 -0
  38. package/src/components/stepperinline/MStepperInline.vue +94 -0
  39. package/src/components/stepperinline/README.md +11 -0
  40. package/src/components/tag/MTag.vue +2 -1
  41. package/src/components/tag/README.md +1 -1
  42. package/src/components/toaster/README.md +1 -1
  43. package/src/main.ts +6 -0
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="mc-kpi" :class="rootClasses">
3
+ <span v-if="isMedium && label" class="mc-kpi__label">
4
+ {{ label }}
5
+ </span>
6
+ <div class="mc-kpi__content">
7
+ <div class="mc-kpi__main">
8
+ <span v-if="isLarge && label" class="mc-kpi__label">
9
+ {{ label }}
10
+ </span>
11
+ <span class="mc-kpi__value">{{ value }}</span>
12
+ </div>
13
+ <div v-if="trend || information" class="mc-kpi__aside">
14
+ <span v-if="isLarge && information" class="mc-kpi__detail">
15
+ {{ information }}
16
+ </span>
17
+
18
+ <component v-if="trend" :is="getIconComponent" class="mc-kpi__icon" />
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { computed, type Component } from 'vue';
26
+ import ArrowBottomRight24 from '@mozaic-ds/icons-vue/src/components/ArrowBottomRight24/ArrowBottomRight24.vue';
27
+ import ArrowTopRight24 from '@mozaic-ds/icons-vue/src/components/ArrowTopRight24/ArrowTopRight24.vue';
28
+ import Less24 from '@mozaic-ds/icons-vue/src/components/Less24/Less24.vue';
29
+ /**
30
+ * A KPI Item is used to display Key Performance Indicators (KPIs) within an interface, providing a quick and clear visualization of essential data. It often includes contextual elements such as labels, trends, or status indicators to help users interpret the information at a glance. KPI Items are commonly used in dashboards, reports, and analytics tools to highlight critical metrics and facilitate data-driven decision-making.
31
+ */
32
+ const props = withDefaults(
33
+ defineProps<{
34
+ /**
35
+ * The current value of the kpi item.
36
+ */
37
+ value: string;
38
+ /**
39
+ * Defines the evolution of the kpi.
40
+ */
41
+ trend?: 'increasing' | 'decreasing' | 'stable';
42
+ /**
43
+ * Label of the kpi item.
44
+ */
45
+ label?: string;
46
+ /**
47
+ * Allows to define the kpi item status.
48
+ */
49
+ status?: 'info' | 'warning' | 'error' | 'success' | 'neutral';
50
+ /**
51
+ * The evolution information defining the kpi.
52
+ */
53
+ information?: string;
54
+ /**
55
+ * Allows to define the kpi item size.
56
+ */
57
+ size?: 's' | 'm' | 'l';
58
+ }>(),
59
+ {
60
+ size: 's',
61
+ status: 'info',
62
+ },
63
+ );
64
+
65
+ const isMedium = computed(() => props.size === 'm');
66
+ const isLarge = computed(() => props.size === 'l');
67
+
68
+ const iconMap: Record<
69
+ NonNullable<Exclude<typeof props.trend, undefined>>,
70
+ Component
71
+ > = {
72
+ increasing: ArrowTopRight24,
73
+ decreasing: ArrowBottomRight24,
74
+ stable: Less24,
75
+ };
76
+
77
+ const getIconComponent = computed<Component | undefined>(() =>
78
+ props.trend ? iconMap[props.trend] : undefined,
79
+ );
80
+
81
+ const rootClasses = computed(() => [
82
+ `mc-kpi--${props.size}`,
83
+ `mc-kpi--${props.status}`,
84
+ ]);
85
+ </script>
86
+
87
+ <style lang="scss" scoped>
88
+ @use '@mozaic-ds/styles/components/kpi-item';
89
+ </style>
@@ -0,0 +1,15 @@
1
+ # MKpiItem
2
+
3
+ A KPI Item is used to display Key Performance Indicators (KPIs) within an interface, providing a quick and clear visualization of essential data. It often includes contextual elements such as labels, trends, or status indicators to help users interpret the information at a glance. KPI Items are commonly used in dashboards, reports, and analytics tools to highlight critical metrics and facilitate data-driven decision-making.
4
+
5
+
6
+ ## Props
7
+
8
+ | Name | Description | Type | Default |
9
+ | --- | --- | --- | --- |
10
+ | `value*` | The current value of the kpi item. | `string` | - |
11
+ | `trend` | Defines the evolution of the kpi. | `"increasing"` `"decreasing"` `"stable"` | - |
12
+ | `label` | Label of the kpi item. | `string` | - |
13
+ | `status` | Allows to define the kpi item status. | `"info"` `"warning"` `"error"` `"success"` `"neutral"` | `"info"` |
14
+ | `information` | The evolution information defining the kpi. | `string` | - |
15
+ | `size` | Allows to define the kpi item size. | `"s"` `"m"` `"l"` | `"s"` |
@@ -17,6 +17,8 @@ A phone number input is a specialized input field designed to capture and valida
17
17
  | `readonly` | If `true`, the input is read-only (cannot be edited). | `boolean` | - |
18
18
  | `prefix` | If `true`, display the country calling code prefix (+33, +1, etc.). | `boolean` | `true` |
19
19
  | `flag` | If `true`, display the country flag selector | `boolean` | `true` |
20
+ | `locale` | Locale for displaying country names (e.g., 'fr', 'en', 'es', 'pt'). | `string` | `"en"` |
21
+ | `countryCodes` | List of country codes to display in the selector. If not provided, all countries will be shown. | `CountryCode[]` | - |
20
22
 
21
23
  ## Events
22
24
 
@@ -0,0 +1,203 @@
1
+ import { shallowMount, mount } from '@vue/test-utils';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { nextTick } from 'vue';
4
+ import MStarRating from './MStarRating.vue';
5
+ import StarFilled24 from '@mozaic-ds/icons-vue/src/components/StarFilled24/StarFilled24.vue';
6
+ import StarHalf24 from '@mozaic-ds/icons-vue/src/components/StarHalf24/StarHalf24.vue';
7
+
8
+ function mockRect(el: Element, { left = 0, width = 100 } = {}) {
9
+ Object.defineProperty(el, 'getBoundingClientRect', {
10
+ value: () => ({
11
+ left,
12
+ width,
13
+ top: 0,
14
+ bottom: 0,
15
+ right: left + width,
16
+ height: 0,
17
+ }),
18
+ configurable: true,
19
+ });
20
+ }
21
+
22
+ describe('MStarRating', () => {
23
+ it('renders 5 stars by default', () => {
24
+ const wrapper = shallowMount(MStarRating, { props: { modelValue: 0 } });
25
+ const stars = wrapper.findAll('.mc-star-rating__icon');
26
+ expect(stars.length).toBe(5);
27
+ });
28
+
29
+ it('renders 1 star when compact mode is enabled', () => {
30
+ const wrapper = shallowMount(MStarRating, {
31
+ props: { modelValue: 0, compact: true },
32
+ });
33
+ const stars = wrapper.findAll('.mc-star-rating__icon');
34
+ expect(stars.length).toBe(1);
35
+ });
36
+
37
+ it('does not render half star on input mode', async () => {
38
+ const wrapper = shallowMount(MStarRating, {
39
+ props: { modelValue: 0, readonly: false },
40
+ });
41
+ const stars = wrapper.findAll('.mc-star-rating__icon');
42
+ const first = stars[0];
43
+ mockRect(first.element, { left: 0, width: 100 });
44
+ await first.trigger('mousemove', { clientX: 10 });
45
+ expect(wrapper.findComponent(StarHalf24).exists()).toBe(false);
46
+ });
47
+
48
+ it('render half star on readonly mode if possible', () => {
49
+ const wrapper = shallowMount(MStarRating, {
50
+ props: { modelValue: 1.5, readonly: true },
51
+ });
52
+ const filledStars = wrapper.findAllComponents(StarFilled24);
53
+ const halfStars = wrapper.findAllComponents(StarHalf24);
54
+ expect(filledStars.length).toBe(1);
55
+ expect(halfStars.length).toBe(1);
56
+ });
57
+
58
+ it('emits update:modelValue with the correct value when clicking a star', async () => {
59
+ const wrapper = shallowMount(MStarRating, {
60
+ props: { modelValue: 2, readonly: false },
61
+ });
62
+ const stars = wrapper.findAll('.mc-star-rating__icon');
63
+ const first = stars[0];
64
+ mockRect(first.element, { left: 0, width: 100 });
65
+ await first.trigger('click', { clientX: 10 });
66
+ const emitted = wrapper.emitted('update:modelValue') || [];
67
+ expect(emitted.length).toBe(1);
68
+ expect(emitted[0][0]).toBe(1);
69
+ });
70
+
71
+ it('emits the correct values when pressing ArrowRight', async () => {
72
+ const wrapper = shallowMount(MStarRating, {
73
+ props: { modelValue: 2, readonly: false },
74
+ });
75
+
76
+ const ratingInput = wrapper.find('.mc-star-rating__wrapper');
77
+
78
+ await ratingInput.trigger('keydown', { key: 'ArrowRight' });
79
+ const emitted = wrapper.emitted('update:modelValue') || [];
80
+ expect(emitted.length).toBe(1);
81
+ expect(emitted[0][0]).toBe(3);
82
+ });
83
+
84
+ it('emits the correct values when pressing ArrowLeft', async () => {
85
+ const wrapper = shallowMount(MStarRating, {
86
+ props: { modelValue: 2, readonly: false },
87
+ });
88
+
89
+ const ratingInput = wrapper.find('.mc-star-rating__wrapper');
90
+ await ratingInput.trigger('keydown', { key: 'ArrowLeft' });
91
+ const emitted = wrapper.emitted('update:modelValue') || [];
92
+ expect(emitted.length).toBe(1);
93
+ expect(emitted[0][0]).toBe(1);
94
+ });
95
+
96
+ it('does not do anything if the wrong key is pressed', async () => {
97
+ const wrapper = shallowMount(MStarRating, {
98
+ props: { modelValue: 2, readonly: false },
99
+ });
100
+ const ratingInput = wrapper.find('.mc-star-rating__wrapper');
101
+ await ratingInput.trigger('keydown', { key: 'ArrowUp' });
102
+ const emitted = wrapper.emitted('update:modelValue') || [];
103
+ expect(emitted.length).toBe(0);
104
+ });
105
+
106
+ it('resets hover to null on mouseleave (aria-label falls back to modelValue)', async () => {
107
+ const wrapper = shallowMount(MStarRating, {
108
+ props: { modelValue: 2, size: 'm', readonly: false },
109
+ });
110
+
111
+ const stars = wrapper.findAll('.mc-star-rating__icon');
112
+ const first = stars[0];
113
+ mockRect(first.element, { left: 0, width: 100 });
114
+
115
+ await first.trigger('mousemove', { clientX: 10 });
116
+ await nextTick();
117
+
118
+ // aria-label should reflect hovered value (0.5) not modelValue (2)
119
+ const root = wrapper.find('[role="slider"]');
120
+ expect(root.attributes('aria-label')).toContain('1');
121
+
122
+ // trigger mouseleave on root, hover should be cleared and aria-label revert to modelValue
123
+ await root.trigger('mouseleave');
124
+ await nextTick();
125
+ expect(root.attributes('aria-label')).toContain('2');
126
+ });
127
+
128
+ it('resets hover to null on blur (aria-label falls back to modelValue)', async () => {
129
+ const wrapper = shallowMount(MStarRating, {
130
+ props: { modelValue: 3, size: 'm', readonly: false },
131
+ });
132
+
133
+ const stars = wrapper.findAll('.mc-star-rating__icon');
134
+ const secondStar = stars[1];
135
+ mockRect(secondStar.element, { left: 0, width: 100 });
136
+
137
+ const root = wrapper.find('[role="slider"]');
138
+
139
+ await root.trigger('focus');
140
+ await secondStar.trigger('mousemove', { clientX: 10 });
141
+ await nextTick();
142
+ expect(root.attributes('aria-label')).toContain('2');
143
+
144
+ // blur should clear hover and revert aria-label to modelValue (3)
145
+ await root.trigger('blur');
146
+ await nextTick();
147
+ expect(root.attributes('aria-label')).toContain('3');
148
+ });
149
+
150
+ it('renders information text when text prop is provided', () => {
151
+ const wrapper = shallowMount(MStarRating, {
152
+ props: { modelValue: 3, text: 'Note publique' },
153
+ });
154
+
155
+ const info = wrapper.find('.mc-star-rating__info');
156
+ expect(info.exists()).toBe(true);
157
+ expect(info.text()).toBe('Note publique');
158
+ });
159
+
160
+ it('renders href when href prop is provided and text is not', () => {
161
+ const wrapper = shallowMount(MStarRating, {
162
+ props: { modelValue: 2, href: '/voir' },
163
+ });
164
+
165
+ const info = wrapper.find('.mc-star-rating__info');
166
+ expect(info.exists()).toBe(true);
167
+ expect(info.text()).toBe('/voir');
168
+ });
169
+
170
+ it('does not render info span when neither text nor href is provided', () => {
171
+ const wrapper = shallowMount(MStarRating, {
172
+ props: { modelValue: 1 },
173
+ });
174
+
175
+ const info = wrapper.find('.mc-star-rating__info');
176
+ expect(info.exists()).toBe(false);
177
+ });
178
+
179
+ it('renders router-link when href and router are provided', () => {
180
+ const wrapper = mount(MStarRating, {
181
+ props: { modelValue: 3, href: '/path', router: true },
182
+ global: { stubs: ['router-link'] },
183
+ });
184
+
185
+ expect(wrapper.element.tagName.toLowerCase()).toBe('router-link-stub');
186
+ });
187
+
188
+ it('renders an anchor when href is provided and router is falsy', () => {
189
+ const wrapper = shallowMount(MStarRating, {
190
+ props: { modelValue: 2, href: '#', router: false },
191
+ });
192
+
193
+ expect(wrapper.element.tagName.toLowerCase()).toBe('a');
194
+ });
195
+
196
+ it('renders a div when no href is provided', () => {
197
+ const wrapper = shallowMount(MStarRating, {
198
+ props: { modelValue: 1 },
199
+ });
200
+
201
+ expect(wrapper.element.tagName.toLowerCase()).toBe('div');
202
+ });
203
+ });
@@ -0,0 +1,82 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import MStarRating from './MStarRating.vue';
3
+
4
+ const meta: Meta<typeof MStarRating> = {
5
+ title: 'Indicators/Star rating',
6
+ component: MStarRating,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component:
11
+ 'A Star rating visually represents a score or evaluation and can be used to display a rating or allow users to rate an item, such as a product or service. It serves two main purposes: collecting user feedback by enabling individuals to express their experience and providing social proof by displaying ratings from other users to assist decision-making. Rating Stars are commonly found in e-commerce, review systems, and feedback interfaces, offering a quick and intuitive way to assess quality or satisfaction.',
12
+ },
13
+ },
14
+ },
15
+ args: {
16
+ modelValue: 3.5,
17
+ appearance: 'accent',
18
+ readonly: true,
19
+ },
20
+ render: (args) => ({
21
+ components: { MStarRating },
22
+ setup() {
23
+ return { args };
24
+ },
25
+ template: `
26
+ <MStarRating v-model="args.modelValue" v-bind="args"></MStarRating>
27
+ `,
28
+ }),
29
+ };
30
+ export default meta;
31
+
32
+ type Story = StoryObj<typeof MStarRating>;
33
+
34
+ export const Default: Story = {};
35
+
36
+ export const AsInput: Story = {
37
+ args: {
38
+ readonly: false,
39
+ modelValue: 0,
40
+ },
41
+ };
42
+
43
+ export const SizeM: Story = {
44
+ args: {
45
+ size: 'm',
46
+ },
47
+ };
48
+
49
+ export const SizeL: Story = {
50
+ args: {
51
+ size: 'l',
52
+ },
53
+ };
54
+
55
+ export const StandardAppearance: Story = {
56
+ args: {
57
+ appearance: 'standard',
58
+ },
59
+ };
60
+
61
+ export const WithText: Story = {
62
+ args: {
63
+ appearance: 'accent',
64
+ text: 'Additional text',
65
+ },
66
+ };
67
+
68
+ export const WithLink: Story = {
69
+ args: {
70
+ appearance: 'accent',
71
+ text: 'Additional text',
72
+ href: '#',
73
+ },
74
+ };
75
+
76
+ export const CompactModeWithText: Story = {
77
+ args: {
78
+ compact: true,
79
+ appearance: 'accent',
80
+ text: '(35)',
81
+ },
82
+ };
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <component
3
+ class="mc-star-rating"
4
+ :class="classObject"
5
+ :is="rootTag"
6
+ :href="props.href"
7
+ :to="props.href"
8
+ :target="props.target"
9
+ >
10
+ <div
11
+ class="mc-star-rating__wrapper"
12
+ :tabindex="isReadonly ? -1 : 0"
13
+ :role="isReadonly ? 'img' : 'slider'"
14
+ :aria-label="`Note ${displayValue} sur ${maxValue}`"
15
+ v-bind="
16
+ !isReadonly
17
+ ? {
18
+ 'aria-valuemin': 0,
19
+ 'aria-valuemax': maxValue,
20
+ 'aria-valuenow': rating,
21
+ 'aria-readonly': false,
22
+ }
23
+ : {}
24
+ "
25
+ v-on="
26
+ isReadonly
27
+ ? {}
28
+ : {
29
+ keydown: onKeydown,
30
+ mouseleave: clearHover,
31
+ blur: clearHover,
32
+ }
33
+ "
34
+ >
35
+ <component
36
+ v-for="(_, index) in maxValue"
37
+ :key="index"
38
+ :is="getStarComponent(index)"
39
+ class="mc-star-rating__icon"
40
+ v-on="
41
+ !isReadonly
42
+ ? {
43
+ mousemove: () => onHover(index),
44
+ click: () => onClick(index),
45
+ }
46
+ : {}
47
+ "
48
+ />
49
+ </div>
50
+
51
+ <span v-if="props.text || props.href" class="mc-star-rating__info">
52
+ {{ text || href }}
53
+ </span>
54
+ </component>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { computed, ref } from 'vue';
59
+ import Star24 from '@mozaic-ds/icons-vue/src/components/Star24/Star24.vue';
60
+ import StarFilled24 from '@mozaic-ds/icons-vue/src/components/StarFilled24/StarFilled24.vue';
61
+ import StarHalf24 from '@mozaic-ds/icons-vue/src/components/StarHalf24/StarHalf24.vue';
62
+
63
+ /**
64
+ * A Star rating visually represents a score or evaluation and can be used to display a rating or allow users to rate an item, such as a product or service. It serves two main purposes: collecting user feedback by enabling individuals to express their experience and providing social proof by displaying ratings from other users to assist decision-making. Rating Stars are commonly found in e-commerce, review systems, and feedback interfaces, offering a quick and intuitive way to assess quality or satisfaction.
65
+ */
66
+
67
+ const props = withDefaults(
68
+ defineProps<{
69
+ /**
70
+ * Determines whether the rating is interactive or read-only.
71
+ * When true, all user interactions (click, hover, keyboard) are disabled.
72
+ * Automatically set to true when `compact`, `href`, or `information` are provided.
73
+ *
74
+ */
75
+ readonly?: boolean;
76
+ /**
77
+ * Defines the visual size of the star icons and the accompanying text.
78
+ *
79
+ */
80
+ size?: 's' | 'm' | 'l';
81
+ /**
82
+ * Enables a compact display mode that shows only one star instead of five.
83
+ * Typically used for summaries or quick visual cues.
84
+ * When enabled, the component automatically becomes read-only.
85
+ *
86
+ */
87
+ compact?: boolean;
88
+ /**
89
+ * Specifies the color scheme of the stars.
90
+ *
91
+ */
92
+ appearance?: 'standard' | 'accent';
93
+ /**
94
+ * Optional text displayed next to the star rating.
95
+ * If provided, the component automatically becomes read-only.
96
+ */
97
+ text?: string;
98
+ /**
99
+ * URL for the link (for external links or the `to` prop for `router-link`).
100
+ */
101
+ href?: string;
102
+ /**
103
+ * Where to open the link.
104
+ */
105
+ target?: '_self' | '_blank' | '_parent' | '_top';
106
+ /**
107
+ * If `true`, the link will be rendered as a `router-link` for internal navigation (Vue Router).
108
+ */
109
+ router?: boolean;
110
+ }>(),
111
+ {
112
+ readonly: true,
113
+ size: 's',
114
+ compact: false,
115
+ appearance: 'accent',
116
+ },
117
+ );
118
+
119
+ const hover = ref<number | null>(null);
120
+
121
+ /**
122
+ * The current rating value of the component.
123
+ * Used with `v-model` for two-way data binding.
124
+ * When the rating changes (via click or keyboard), an `update:modelValue` event is emitted.
125
+ */
126
+ const rating = defineModel<number>({ required: true });
127
+
128
+ const rootTag = computed(() => {
129
+ if (props.href) return props.router ? 'router-link' : 'a';
130
+ return 'div';
131
+ });
132
+
133
+ const isReadonly = computed(() =>
134
+ [props.readonly, props.compact, props.href, props.text].some(Boolean),
135
+ );
136
+
137
+ const classObject = computed(() => ({
138
+ 'mc-star-rating--link': props.href,
139
+ 'mc-star-rating--slider': !isReadonly.value,
140
+ [`mc-star-rating--${props.size}`]: props.size,
141
+ [`mc-star-rating--${props.appearance}`]: props.appearance,
142
+ }));
143
+
144
+ const displayValue = computed(() => hover.value ?? rating.value);
145
+ const maxValue = computed(() => (props.compact ? 1 : 5));
146
+
147
+ function getStarComponent(index: number) {
148
+ if (props.compact || displayValue.value >= index + 1) {
149
+ return StarFilled24;
150
+ } else if (isReadonly.value && displayValue.value >= index + 0.5) {
151
+ return StarHalf24;
152
+ } else {
153
+ return Star24;
154
+ }
155
+ }
156
+
157
+ function onHover(index: number) {
158
+ hover.value = index + 1;
159
+ }
160
+
161
+ function clearHover() {
162
+ hover.value = null;
163
+ }
164
+
165
+ function onClick(index: number) {
166
+ rating.value = index + 1;
167
+ }
168
+
169
+ function onKeydown(e: KeyboardEvent) {
170
+ const key = e.key;
171
+ let newValue = Math.floor(rating.value);
172
+
173
+ if (key === 'ArrowRight') {
174
+ newValue = Math.min(maxValue.value, newValue + 1);
175
+ } else if (key === 'ArrowLeft') {
176
+ newValue = Math.max(1, newValue - 1);
177
+ } else return;
178
+
179
+ rating.value = newValue;
180
+
181
+ e.preventDefault();
182
+ }
183
+ </script>
184
+
185
+ <style lang="scss" scoped>
186
+ @use '@mozaic-ds/styles/components/star-rating';
187
+ </style>
@@ -0,0 +1,31 @@
1
+ # MStarRating
2
+
3
+ A Star rating visually represents a score or evaluation and can be used to display a rating or allow users to rate an item, such as a product or service. It serves two main purposes: collecting user feedback by enabling individuals to express their experience and providing social proof by displaying ratings from other users to assist decision-making. Rating Stars are commonly found in e-commerce, review systems, and feedback interfaces, offering a quick and intuitive way to assess quality or satisfaction.
4
+
5
+
6
+ ## Props
7
+
8
+ | Name | Description | Type | Default |
9
+ | --- | --- | --- | --- |
10
+ | `readonly` | Determines whether the rating is interactive or read-only.
11
+ When true, all user interactions (click, hover, keyboard) are disabled.
12
+ Automatically set to true when `compact`, `href`, or `information` are provided. | `boolean` | `true` |
13
+ | `size` | Defines the visual size of the star icons and the accompanying text. | `"s"` `"m"` `"l"` | `"s"` |
14
+ | `compact` | Enables a compact display mode that shows only one star instead of five.
15
+ Typically used for summaries or quick visual cues.
16
+ When enabled, the component automatically becomes read-only. | `boolean` | `false` |
17
+ | `appearance` | Specifies the color scheme of the stars. | `"standard"` `"accent"` | `"accent"` |
18
+ | `text` | Optional text displayed next to the star rating.
19
+ If provided, the component automatically becomes read-only. | `string` | - |
20
+ | `href` | URL for the link (for external links or the `to` prop for `router-link`). | `string` | - |
21
+ | `target` | Where to open the link. | `"_self"` `"_blank"` `"_parent"` `"_top"` | - |
22
+ | `router` | If `true`, the link will be rendered as a `router-link` for internal navigation (Vue Router). | `boolean` | - |
23
+ | `modelValue*` | The current rating value of the component.
24
+ Used with `v-model` for two-way data binding.
25
+ When the rating changes (via click or keyboard), an `update:modelValue` event is emitted. | `number` | - |
26
+
27
+ ## Events
28
+
29
+ | Name | Description | Type |
30
+ | --- | --- | --- |
31
+ | `update:modelValue` | - | `[value: number]` |
@@ -8,4 +8,4 @@ A Status Badge is used to indicate the current status of an element, providing a
8
8
  | Name | Description | Type | Default |
9
9
  | --- | --- | --- | --- |
10
10
  | `label*` | Content of the status badge | `string` | - |
11
- | `status` | Allows to define the status badge type | `"info"` `"success"` `"warning"` `"error"` `"neutral"` | `"info"` |
11
+ | `status` | Allows to define the status badge type | `"info"` `"warning"` `"error"` `"success"` `"neutral"` | `"info"` |
@@ -7,5 +7,5 @@ A Status dot is a small visual indicator used to represent the state or conditio
7
7
 
8
8
  | Name | Description | Type | Default |
9
9
  | --- | --- | --- | --- |
10
- | `status` | Allows to define the status dot type. | `"info"` `"success"` `"warning"` `"error"` `"neutral"` | `"info"` |
10
+ | `status` | Allows to define the status dot type. | `"info"` `"warning"` `"error"` `"success"` `"neutral"` | `"info"` |
11
11
  | `size` | Determines the size of the status dot. | `"s"` `"m"` `"l"` | - |