tide-design-system 2.4.5 → 2.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.
- package/.storybook/main.ts +1 -0
- package/dist/css/reset.css +1 -1
- package/dist/css/utilities-responsive.css +0 -546
- package/dist/style.css +1 -1
- package/dist/tide-design-system.cjs +2 -2
- package/dist/tide-design-system.esm.d.ts +171 -73
- package/dist/tide-design-system.esm.js +1725 -1594
- package/dist/utilities/storybook.ts +6 -2
- package/dist/utilities/validation.ts +1 -1
- package/docs/upgrading.md +79 -0
- package/index.ts +8 -5
- package/package.json +1 -1
- package/src/assets/css/reset.css +1 -1
- package/src/assets/css/utilities-responsive.css +0 -546
- package/src/components/InternalBaseLink.vue +11 -0
- package/src/components/TideAccordionItem.vue +21 -13
- package/src/components/TideBreadCrumbs.vue +3 -2
- package/src/components/TideButton.vue +17 -4
- package/src/components/TideButtonIcon.vue +15 -2
- package/src/components/TideButtonPagination.vue +16 -16
- package/src/components/TideButtonSegmented.vue +15 -15
- package/src/components/TideCard.vue +12 -2
- package/src/components/TideCarousel.vue +10 -5
- package/src/components/TideChipAction.vue +7 -1
- package/src/components/TideChipFilter.vue +14 -7
- package/src/components/TideChipInput.vue +1 -0
- package/src/components/TideIcon.vue +8 -9
- package/src/components/TideImage.vue +9 -9
- package/src/components/TideInputCheckbox.vue +0 -1
- package/src/components/TideInputText.vue +2 -0
- package/src/components/TideInputTextDeprecated.vue +2 -0
- package/src/components/TideInputTextarea.vue +2 -2
- package/src/components/TideLink.vue +7 -1
- package/src/components/TideMenuItem.vue +83 -0
- package/src/components/TideModal.vue +120 -101
- package/src/components/TidePagination.vue +17 -19
- package/src/components/TideSeoLinks.vue +3 -2
- package/src/components/TideSheet.vue +28 -23
- package/src/components/TideSwitch.vue +24 -20
- package/src/composables/useTideConfig.ts +23 -0
- package/src/stories/TideAccordionItem.stories.ts +33 -34
- package/src/stories/TideButtonPagination.stories.ts +6 -6
- package/src/stories/TideButtonSegmented.stories.ts +33 -25
- package/src/stories/TideCarousel.stories.ts +0 -1
- package/src/stories/TideChipFilter.stories.ts +33 -23
- package/src/stories/TideInputCheckbox.stories.ts +66 -23
- package/src/stories/TideInputRadio.stories.ts +39 -30
- package/src/stories/TideInputSelect.stories.ts +51 -27
- package/src/stories/TideInputText.stories.ts +83 -23
- package/src/stories/TideInputTextarea.stories.ts +66 -17
- package/src/stories/TideLink.stories.ts +1 -14
- package/src/stories/TideMenuItem.stories.ts +117 -0
- package/src/stories/TideModal.stories.ts +33 -37
- package/src/stories/TidePagination.stories.ts +33 -22
- package/src/stories/TidePopover.stories.ts +1 -1
- package/src/stories/TideSheet.stories.ts +33 -28
- package/src/stories/TideSwitch.stories.ts +33 -34
- package/src/types/Badge.ts +4 -0
- package/src/types/Element.ts +2 -2
- package/src/types/Formatted.ts +1 -0
- package/src/types/Storybook.ts +4 -6
- package/src/types/Type.ts +6 -0
- package/src/types/Validation.ts +1 -0
- package/src/utilities/storybook.ts +6 -2
- package/src/utilities/validation.ts +1 -1
- package/tests/InternalBaseLink.spec.ts +61 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Renders a generic menu item intended to be used in navigation menus or dropdowns.
|
|
4
|
+
*
|
|
5
|
+
* @see the [Storybook interface](https://tide-design-system.netlify.app/?path=/docs/components-tidemenuitem--docs) for TideMenuItem
|
|
6
|
+
*/
|
|
7
|
+
export default {};
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import TideIcon from '@/components/TideIcon.vue';
|
|
12
|
+
import { ELEMENT } from '@/types/Element';
|
|
13
|
+
import { SIZE } from '@/types/Size';
|
|
14
|
+
import { CSS } from '@/types/Styles';
|
|
15
|
+
import { TARGET } from '@/types/Target';
|
|
16
|
+
|
|
17
|
+
import type { Element } from '@/types/Element';
|
|
18
|
+
import type { Icon } from '@/types/Icon';
|
|
19
|
+
|
|
20
|
+
type Props = {
|
|
21
|
+
/** The element to render the root element as. */
|
|
22
|
+
element?: Element;
|
|
23
|
+
|
|
24
|
+
/** The href attribute to apply when the element is a link. */
|
|
25
|
+
href?: string;
|
|
26
|
+
|
|
27
|
+
/** Whether to open link in a new tab. */
|
|
28
|
+
isNewTab?: boolean;
|
|
29
|
+
|
|
30
|
+
/** Icon to show after the label. */
|
|
31
|
+
iconTrailing?: Icon;
|
|
32
|
+
|
|
33
|
+
/** The label to be displayed. */
|
|
34
|
+
label: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
withDefaults(defineProps<Props>(), {
|
|
38
|
+
element: ELEMENT.LINK,
|
|
39
|
+
href: undefined,
|
|
40
|
+
iconTrailing: undefined,
|
|
41
|
+
isNewTab: false,
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<template>
|
|
46
|
+
<component
|
|
47
|
+
:class="[
|
|
48
|
+
'tide-menu-item',
|
|
49
|
+
CSS.AXIS2.CENTER,
|
|
50
|
+
CSS.BORDER.RADIUS.ZERO,
|
|
51
|
+
CSS.DISPLAY.FLEX,
|
|
52
|
+
CSS.ELLIPSIS,
|
|
53
|
+
CSS.FONT.ROLE.LABEL_2,
|
|
54
|
+
CSS.GAP.QUARTER,
|
|
55
|
+
CSS.PADDING.X.ONE,
|
|
56
|
+
CSS.PADDING.Y.HALF,
|
|
57
|
+
CSS.UNDERLINE.REST.OFF,
|
|
58
|
+
CSS.WIDTH.FULL,
|
|
59
|
+
]"
|
|
60
|
+
:href="href"
|
|
61
|
+
:target="isNewTab ? TARGET.BLANK : undefined"
|
|
62
|
+
:is="element"
|
|
63
|
+
>
|
|
64
|
+
<span>{{ label }}</span>
|
|
65
|
+
|
|
66
|
+
<TideIcon
|
|
67
|
+
:class="[CSS.MARGIN.LEFT.AUTO]"
|
|
68
|
+
:icon="iconTrailing"
|
|
69
|
+
:size="SIZE.LARGE"
|
|
70
|
+
v-if="iconTrailing"
|
|
71
|
+
/>
|
|
72
|
+
</component>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style scoped>
|
|
76
|
+
.tide-menu-item {
|
|
77
|
+
height: 40px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.tide-menu-item:hover {
|
|
81
|
+
background-color: var(--tide-surface-variant);
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { nextTick, onMounted, ref, watch } from 'vue';
|
|
2
|
+
import { nextTick, onBeforeMount, onMounted, ref, watch } from 'vue';
|
|
3
3
|
|
|
4
4
|
import TideButtonIcon from '@/components/TideButtonIcon.vue';
|
|
5
5
|
import { BREAKPOINT } from '@/types/Breakpoint';
|
|
6
6
|
import { ICON } from '@/types/Icon';
|
|
7
7
|
import { PRIORITY } from '@/types/Priority';
|
|
8
8
|
import { CSS } from '@/types/Styles';
|
|
9
|
-
import { setScrollLock } from '@/utilities/viewport';
|
|
9
|
+
import { TOP_LAYER_ID, initFauxTopLayer, setScrollLock } from '@/utilities/viewport';
|
|
10
10
|
|
|
11
11
|
import type { Ref } from 'vue';
|
|
12
12
|
|
|
13
13
|
type Props = {
|
|
14
|
+
/**
|
|
15
|
+
* Called before the modal closes.
|
|
16
|
+
*
|
|
17
|
+
* Return `false` to cancel the close event.
|
|
18
|
+
*/
|
|
19
|
+
beforeClose?: () => void | boolean | Promise<void | boolean>;
|
|
14
20
|
isBackButton?: boolean;
|
|
15
21
|
isDismissible?: boolean;
|
|
16
|
-
isOpen: boolean;
|
|
17
22
|
title?: string;
|
|
18
23
|
width?: string;
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
const props = withDefaults(defineProps<Props>(), {
|
|
27
|
+
beforeClose: undefined,
|
|
22
28
|
isBackButton: false,
|
|
23
29
|
isDismissible: true,
|
|
24
30
|
title: undefined,
|
|
@@ -26,7 +32,6 @@
|
|
|
26
32
|
});
|
|
27
33
|
|
|
28
34
|
type Emits = {
|
|
29
|
-
(e: 'close'): void;
|
|
30
35
|
(e: 'back'): void;
|
|
31
36
|
};
|
|
32
37
|
|
|
@@ -35,6 +40,8 @@
|
|
|
35
40
|
const modalContent: Ref<HTMLDivElement | undefined> = ref();
|
|
36
41
|
const modalDialog: Ref<HTMLDialogElement | undefined> = ref();
|
|
37
42
|
|
|
43
|
+
const isOpen = defineModel<boolean>({ required: true });
|
|
44
|
+
|
|
38
45
|
const triggerNativeDialogOpen = () => {
|
|
39
46
|
modalDialog.value?.showModal();
|
|
40
47
|
};
|
|
@@ -50,128 +57,140 @@
|
|
|
50
57
|
});
|
|
51
58
|
};
|
|
52
59
|
|
|
60
|
+
const close = async () => {
|
|
61
|
+
if (props.beforeClose) {
|
|
62
|
+
const result = await props.beforeClose();
|
|
63
|
+
if (result === false) return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
isOpen.value = false;
|
|
67
|
+
};
|
|
68
|
+
|
|
53
69
|
const handleBackdropClick = () => {
|
|
54
|
-
if (props.isDismissible)
|
|
70
|
+
if (props.isDismissible) close();
|
|
55
71
|
};
|
|
56
72
|
|
|
57
73
|
const handleEscapeKeydown = (e: Event) => {
|
|
58
74
|
e.preventDefault();
|
|
59
|
-
if (props.isDismissible)
|
|
75
|
+
if (props.isDismissible) close();
|
|
60
76
|
};
|
|
61
77
|
|
|
62
|
-
watch(
|
|
63
|
-
()
|
|
64
|
-
(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
triggerNativeDialogClose();
|
|
71
|
-
}
|
|
72
|
-
setScrollLock(newValue);
|
|
78
|
+
watch(isOpen, () => {
|
|
79
|
+
if (!modalDialog.value) return;
|
|
80
|
+
if (isOpen.value) {
|
|
81
|
+
triggerNativeDialogOpen();
|
|
82
|
+
scrollContentToTop();
|
|
83
|
+
} else {
|
|
84
|
+
triggerNativeDialogClose();
|
|
73
85
|
}
|
|
74
|
-
|
|
86
|
+
setScrollLock(isOpen.value);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
onBeforeMount(() => {
|
|
90
|
+
initFauxTopLayer();
|
|
91
|
+
});
|
|
75
92
|
|
|
76
93
|
onMounted(() => {
|
|
77
|
-
if (
|
|
94
|
+
if (isOpen.value) {
|
|
78
95
|
triggerNativeDialogOpen();
|
|
79
96
|
}
|
|
80
97
|
});
|
|
81
98
|
</script>
|
|
82
99
|
|
|
83
100
|
<template>
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<div
|
|
93
|
-
:class="[
|
|
94
|
-
'tide-modal-body',
|
|
95
|
-
CSS.BG.SURFACE.DEFAULT,
|
|
96
|
-
CSS.BORDER.RADIUS.ONE,
|
|
97
|
-
CSS.DISPLAY.FLEX,
|
|
98
|
-
CSS.FLEX.DIRECTION.COLUMN,
|
|
99
|
-
CSS.OVERFLOW.XY.HIDDEN,
|
|
100
|
-
CSS.POSITION.ABSOLUTE,
|
|
101
|
-
CSS.POSITIONING.BOTTOM,
|
|
102
|
-
CSS.SHADOW.TOP,
|
|
103
|
-
CSS.WIDTH.FULL,
|
|
104
|
-
CSS.WIDTH.MAX_FULL,
|
|
105
|
-
CSS.withBreakpoint([CSS.SHADOW.BOTTOM], BREAKPOINT.SM),
|
|
106
|
-
]"
|
|
101
|
+
<Teleport :to="`#${TOP_LAYER_ID}`">
|
|
102
|
+
<dialog
|
|
103
|
+
:class="['tide-modal', CSS.BG.INITIAL, CSS.HEIGHT.FULL, CSS.WIDTH.FULL, CSS.OVERFLOW.XY.HIDDEN]"
|
|
104
|
+
ref="modalDialog"
|
|
105
|
+
:style="{ '--modal-width': width }"
|
|
106
|
+
@click.self="handleBackdropClick"
|
|
107
|
+
@close.prevent
|
|
108
|
+
@keydown.escape="handleEscapeKeydown"
|
|
107
109
|
>
|
|
108
|
-
<
|
|
110
|
+
<div
|
|
109
111
|
:class="[
|
|
110
|
-
'tide-modal-
|
|
111
|
-
CSS.
|
|
112
|
-
CSS.BORDER.
|
|
113
|
-
CSS.BORDER.COLOR.LOW,
|
|
112
|
+
'tide-modal-body',
|
|
113
|
+
CSS.BG.SURFACE.DEFAULT,
|
|
114
|
+
CSS.BORDER.RADIUS.ONE,
|
|
114
115
|
CSS.DISPLAY.FLEX,
|
|
115
|
-
CSS.
|
|
116
|
-
CSS.
|
|
117
|
-
CSS.
|
|
118
|
-
CSS.
|
|
119
|
-
CSS.
|
|
116
|
+
CSS.FLEX.DIRECTION.COLUMN,
|
|
117
|
+
CSS.OVERFLOW.XY.HIDDEN,
|
|
118
|
+
CSS.POSITION.ABSOLUTE,
|
|
119
|
+
CSS.POSITIONING.BOTTOM,
|
|
120
|
+
CSS.SHADOW.TOP,
|
|
121
|
+
CSS.WIDTH.FULL,
|
|
122
|
+
CSS.WIDTH.MAX_FULL,
|
|
123
|
+
CSS.withBreakpoint([CSS.SHADOW.BOTTOM], BREAKPOINT.SM),
|
|
120
124
|
]"
|
|
121
125
|
>
|
|
122
|
-
<
|
|
123
|
-
:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
<header
|
|
127
|
+
:class="[
|
|
128
|
+
'tide-modal-header',
|
|
129
|
+
CSS.AXIS2.CENTER,
|
|
130
|
+
CSS.BORDER.BOTTOM.ONE,
|
|
131
|
+
CSS.BORDER.COLOR.LOW,
|
|
132
|
+
CSS.DISPLAY.FLEX,
|
|
133
|
+
CSS.GAP.HALF,
|
|
134
|
+
CSS.ISOLATION.ISOLATE,
|
|
135
|
+
CSS.PADDING.Y.ONE,
|
|
136
|
+
CSS.POSITION.RELATIVE,
|
|
137
|
+
CSS.Z_INDEX.ONE,
|
|
138
|
+
]"
|
|
139
|
+
>
|
|
140
|
+
<TideButtonIcon
|
|
141
|
+
:icon="ICON.CHEVRON_LEFT"
|
|
142
|
+
:priority="PRIORITY.QUATERNARY"
|
|
143
|
+
@click="emit('back')"
|
|
144
|
+
title="Back"
|
|
145
|
+
v-if="isBackButton"
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
<div
|
|
149
|
+
:class="[CSS.FONT.ROLE.HEADLINE_2]"
|
|
150
|
+
v-text="title"
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
<TideButtonIcon
|
|
154
|
+
:class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF, CSS.MARGIN.LEFT.AUTO]"
|
|
155
|
+
:icon="ICON.CLOSE"
|
|
156
|
+
:priority="PRIORITY.QUATERNARY"
|
|
157
|
+
@click="close"
|
|
158
|
+
v-if="isDismissible"
|
|
159
|
+
/>
|
|
160
|
+
</header>
|
|
129
161
|
|
|
130
162
|
<div
|
|
131
|
-
:class="[
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
:class="[
|
|
164
|
+
'tide-modal-content',
|
|
165
|
+
CSS.DISPLAY.GRID,
|
|
166
|
+
CSS.ISOLATION.ISOLATE,
|
|
167
|
+
CSS.OVERFLOW.X.HIDDEN,
|
|
168
|
+
CSS.OVERFLOW.Y.AUTO,
|
|
169
|
+
CSS.PADDING.Y.TWO,
|
|
170
|
+
CSS.WIDTH.FULL,
|
|
171
|
+
]"
|
|
172
|
+
ref="modalContent"
|
|
173
|
+
>
|
|
174
|
+
<slot />
|
|
175
|
+
</div>
|
|
143
176
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
177
|
+
<footer
|
|
178
|
+
:class="[
|
|
179
|
+
'tide-modal-footer',
|
|
180
|
+
CSS.AXIS1.END,
|
|
181
|
+
CSS.DISPLAY.FLEX,
|
|
182
|
+
CSS.GAP.TWO,
|
|
183
|
+
CSS.ISOLATION.ISOLATE,
|
|
184
|
+
CSS.PADDING.Y.ONE,
|
|
185
|
+
CSS.SHADOW.TOP,
|
|
186
|
+
]"
|
|
187
|
+
v-if="$slots.footer"
|
|
188
|
+
>
|
|
189
|
+
<slot name="footer" />
|
|
190
|
+
</footer>
|
|
157
191
|
</div>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
:class="[
|
|
161
|
-
'tide-modal-footer',
|
|
162
|
-
CSS.AXIS1.END,
|
|
163
|
-
CSS.DISPLAY.FLEX,
|
|
164
|
-
CSS.GAP.TWO,
|
|
165
|
-
CSS.ISOLATION.ISOLATE,
|
|
166
|
-
CSS.PADDING.Y.ONE,
|
|
167
|
-
CSS.SHADOW.TOP,
|
|
168
|
-
]"
|
|
169
|
-
v-if="$slots.footer"
|
|
170
|
-
>
|
|
171
|
-
<slot name="footer" />
|
|
172
|
-
</footer>
|
|
173
|
-
</div>
|
|
174
|
-
</dialog>
|
|
192
|
+
</dialog>
|
|
193
|
+
</Teleport>
|
|
175
194
|
</template>
|
|
176
195
|
|
|
177
196
|
<style scoped>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed
|
|
2
|
+
import { computed } from 'vue';
|
|
3
3
|
|
|
4
4
|
import TideButtonIcon from '@/components/TideButtonIcon.vue';
|
|
5
5
|
import TideButtonPagination from '@/components/TideButtonPagination.vue';
|
|
@@ -8,25 +8,25 @@
|
|
|
8
8
|
import { CSS } from '@/types/Styles';
|
|
9
9
|
|
|
10
10
|
type Props = {
|
|
11
|
-
|
|
12
|
-
pageTotal?: number;
|
|
11
|
+
pageTotal: number;
|
|
13
12
|
};
|
|
14
13
|
|
|
15
|
-
const props =
|
|
16
|
-
pageCurrent: 1,
|
|
17
|
-
pageTotal: 1,
|
|
18
|
-
});
|
|
14
|
+
const props = defineProps<Props>();
|
|
19
15
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const pageCurrent = ref(props.pageCurrent);
|
|
16
|
+
const currentIndex = defineModel<number>({ required: true });
|
|
23
17
|
|
|
24
18
|
const paginationButtons = computed(() => new Array(props.pageTotal).fill('').map((empty, index) => index + 1));
|
|
25
19
|
|
|
26
20
|
const handleClick = (index: number) => {
|
|
27
|
-
|
|
21
|
+
currentIndex.value = index;
|
|
22
|
+
};
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
const handlePreviousClick = () => {
|
|
25
|
+
currentIndex.value--;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleNextClick = () => {
|
|
29
|
+
currentIndex.value++;
|
|
30
30
|
};
|
|
31
31
|
</script>
|
|
32
32
|
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
:class="['tide-pagination', CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER, CSS.GAP.QUARTER, CSS.WIDTH.FULL]"
|
|
36
36
|
>
|
|
37
37
|
<TideButtonIcon
|
|
38
|
-
:disabled="
|
|
38
|
+
:disabled="currentIndex <= 1"
|
|
39
39
|
:icon="ICON.CHEVRON_LEFT"
|
|
40
40
|
:priority="PRIORITY.QUATERNARY"
|
|
41
|
-
@click="
|
|
41
|
+
@click="handlePreviousClick"
|
|
42
42
|
/>
|
|
43
43
|
|
|
44
44
|
<ul :class="[CSS.DISPLAY.FLEX, CSS.AXIS2.CENTER, CSS.GAP.QUARTER, CSS.LIST_BULLETS.OFF]">
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
v-for="paginationButton in paginationButtons"
|
|
48
48
|
>
|
|
49
49
|
<TideButtonPagination
|
|
50
|
-
:disabled="
|
|
50
|
+
:disabled="currentIndex === paginationButton"
|
|
51
51
|
:label="paginationButton"
|
|
52
52
|
:priority="PRIORITY.QUATERNARY"
|
|
53
53
|
@click="handleClick(paginationButton)"
|
|
@@ -56,12 +56,10 @@
|
|
|
56
56
|
</ul>
|
|
57
57
|
|
|
58
58
|
<TideButtonIcon
|
|
59
|
-
:disabled="
|
|
59
|
+
:disabled="currentIndex >= paginationButtons[paginationButtons.length - 1]"
|
|
60
60
|
:icon="ICON.CHEVRON_RIGHT"
|
|
61
61
|
:priority="PRIORITY.QUATERNARY"
|
|
62
|
-
@click="
|
|
62
|
+
@click="handleNextClick"
|
|
63
63
|
/>
|
|
64
64
|
</section>
|
|
65
65
|
</template>
|
|
66
|
-
|
|
67
|
-
<style scoped></style>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import InternalBaseLink from '@/components/InternalBaseLink.vue';
|
|
2
3
|
import { CSS } from '@/types/Styles';
|
|
3
4
|
import { TARGET } from '@/types/Target';
|
|
4
5
|
|
|
@@ -26,13 +27,13 @@
|
|
|
26
27
|
:key="link.label"
|
|
27
28
|
v-for="link in props.links"
|
|
28
29
|
>
|
|
29
|
-
<
|
|
30
|
+
<InternalBaseLink
|
|
30
31
|
:class="[CSS.UNDERLINE.REST.OFF]"
|
|
31
32
|
:href="link.url"
|
|
32
33
|
:target="link.isNewTab ? TARGET.BLANK : TARGET.SELF"
|
|
33
34
|
>
|
|
34
35
|
{{ link.label }}
|
|
35
|
-
</
|
|
36
|
+
</InternalBaseLink>
|
|
36
37
|
</li>
|
|
37
38
|
</ul>
|
|
38
39
|
</section>
|
|
@@ -2,30 +2,32 @@
|
|
|
2
2
|
import { onBeforeMount, onMounted, ref, watch } from 'vue';
|
|
3
3
|
|
|
4
4
|
import TideButtonIcon from '@/components/TideButtonIcon.vue';
|
|
5
|
+
import TideDivider from '@/components/TideDivider.vue';
|
|
5
6
|
import { ICON } from '@/types/Icon';
|
|
6
7
|
import { PRIORITY } from '@/types/Priority';
|
|
7
8
|
import { CSS } from '@/types/Styles';
|
|
8
9
|
import { TOP_LAYER_ID, initFauxTopLayer, setScrollLock } from '@/utilities/viewport';
|
|
9
10
|
|
|
10
|
-
import TideDivider from './TideDivider.vue';
|
|
11
|
-
|
|
12
11
|
import type { Ref } from 'vue';
|
|
13
12
|
|
|
14
13
|
type Props = {
|
|
15
|
-
isOpen: boolean;
|
|
16
14
|
isBackButton?: boolean;
|
|
17
15
|
};
|
|
18
|
-
|
|
16
|
+
|
|
17
|
+
withDefaults(defineProps<Props>(), {
|
|
18
|
+
isBackButton: false,
|
|
19
|
+
});
|
|
19
20
|
|
|
20
21
|
type Emits = {
|
|
21
22
|
(e: 'back'): void;
|
|
22
|
-
(e: 'close'): void;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const emit = defineEmits<Emits>();
|
|
26
26
|
|
|
27
27
|
const dialogElement: Ref<HTMLDialogElement | null> = ref(null);
|
|
28
28
|
|
|
29
|
+
const isOpen = defineModel<boolean>({ required: true });
|
|
30
|
+
|
|
29
31
|
const triggerNativeDialogOpen = () => {
|
|
30
32
|
dialogElement.value?.showModal();
|
|
31
33
|
};
|
|
@@ -34,34 +36,35 @@
|
|
|
34
36
|
dialogElement.value?.close();
|
|
35
37
|
};
|
|
36
38
|
|
|
39
|
+
const close = () => {
|
|
40
|
+
isOpen.value = false;
|
|
41
|
+
};
|
|
42
|
+
|
|
37
43
|
const handleBackdropClick = () => {
|
|
38
|
-
|
|
44
|
+
close();
|
|
39
45
|
};
|
|
40
46
|
|
|
41
47
|
const handleEscapeKeydown = (e: Event) => {
|
|
42
48
|
e.preventDefault();
|
|
43
|
-
|
|
49
|
+
close();
|
|
44
50
|
};
|
|
45
51
|
|
|
46
|
-
watch(
|
|
47
|
-
()
|
|
48
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} else {
|
|
53
|
-
triggerNativeDialogClose();
|
|
54
|
-
}
|
|
55
|
-
setScrollLock(newValue);
|
|
52
|
+
watch(isOpen, () => {
|
|
53
|
+
if (!dialogElement.value) return;
|
|
54
|
+
if (isOpen.value) {
|
|
55
|
+
triggerNativeDialogOpen();
|
|
56
|
+
} else {
|
|
57
|
+
triggerNativeDialogClose();
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
setScrollLock(isOpen.value);
|
|
60
|
+
});
|
|
58
61
|
|
|
59
62
|
onBeforeMount(() => {
|
|
60
63
|
initFauxTopLayer();
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
onMounted(() => {
|
|
64
|
-
if (
|
|
67
|
+
if (isOpen.value) {
|
|
65
68
|
triggerNativeDialogOpen();
|
|
66
69
|
}
|
|
67
70
|
});
|
|
@@ -109,7 +112,7 @@
|
|
|
109
112
|
:class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF, CSS.MARGIN.LEFT.AUTO]"
|
|
110
113
|
:icon="ICON.CLOSE"
|
|
111
114
|
:priority="PRIORITY.QUATERNARY"
|
|
112
|
-
@click="
|
|
115
|
+
@click="close"
|
|
113
116
|
/>
|
|
114
117
|
</header>
|
|
115
118
|
|
|
@@ -119,11 +122,13 @@
|
|
|
119
122
|
:class="[
|
|
120
123
|
'tide-sheet-content',
|
|
121
124
|
CSS.DISPLAY.GRID,
|
|
122
|
-
CSS.
|
|
125
|
+
CSS.HEIGHT.FULL,
|
|
126
|
+
CSS.ISOLATION.ISOLATE,
|
|
123
127
|
CSS.OVERFLOW.X.HIDDEN,
|
|
124
|
-
CSS.
|
|
128
|
+
CSS.OVERFLOW.Y.AUTO,
|
|
125
129
|
CSS.PADDING.BOTTOM.ONE,
|
|
126
|
-
CSS.
|
|
130
|
+
CSS.POSITION.RELATIVE,
|
|
131
|
+
CSS.WIDTH.FULL,
|
|
127
132
|
]"
|
|
128
133
|
>
|
|
129
134
|
<slot />
|
|
@@ -6,19 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
disabled?: boolean;
|
|
9
|
-
isActive?: boolean;
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
const props = withDefaults(defineProps<Props>(), {
|
|
13
12
|
disabled: false,
|
|
14
|
-
isActive: false,
|
|
15
13
|
});
|
|
16
14
|
|
|
17
|
-
const
|
|
15
|
+
const isActive = defineModel<boolean>({ required: true });
|
|
18
16
|
|
|
19
17
|
const handleClick = () => {
|
|
20
18
|
if (!props.disabled) {
|
|
21
|
-
|
|
19
|
+
isActive.value = !isActive.value;
|
|
22
20
|
}
|
|
23
21
|
};
|
|
24
22
|
</script>
|
|
@@ -27,18 +25,20 @@
|
|
|
27
25
|
<button
|
|
28
26
|
:class="[
|
|
29
27
|
'tide-switch',
|
|
30
|
-
props.isActive ? 'active' : '',
|
|
31
28
|
CSS.DISPLAY.FLEX,
|
|
32
29
|
CSS.AXIS2.CENTER,
|
|
33
30
|
CSS.BORDER.FULL.ONE,
|
|
34
31
|
CSS.BORDER.RADIUS.FULL,
|
|
35
|
-
props.isActive ? '' : CSS.BORDER.COLOR.DEFAULT,
|
|
36
32
|
CSS.OVERFLOW.XY.HIDDEN,
|
|
37
|
-
|
|
33
|
+
CSS.FLEX.SHRINK.OFF,
|
|
34
|
+
CSS.FLEX.GROW.OFF,
|
|
35
|
+
isActive ? ['active', CSS.BG.SECONDARY] : [CSS.BG.SURFACE.DEFAULT, CSS.BORDER.COLOR.DEFAULT],
|
|
36
|
+
disabled && [CSS.OPACITY.DISABLED],
|
|
38
37
|
]"
|
|
39
38
|
:disabled="props.disabled"
|
|
40
39
|
@click="handleClick"
|
|
41
40
|
aria-label="Switch"
|
|
41
|
+
type="button"
|
|
42
42
|
>
|
|
43
43
|
<div
|
|
44
44
|
:class="[
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
CSS.AXIS1.CENTER,
|
|
49
49
|
CSS.AXIS2.CENTER,
|
|
50
50
|
CSS.BORDER.RADIUS.FULL,
|
|
51
|
-
|
|
51
|
+
isActive ? ['active', CSS.BG.SURFACE.DEFAULT] : [CSS.BG.SECONDARY],
|
|
52
52
|
]"
|
|
53
53
|
>
|
|
54
54
|
<TideIcon
|
|
@@ -61,27 +61,31 @@
|
|
|
61
61
|
|
|
62
62
|
<style scoped>
|
|
63
63
|
.tide-switch {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
--switch-indicator-size: 24px;
|
|
65
|
+
--switch-indicator-margin: var(--tide-spacing-1\/4);
|
|
66
|
+
--switch-border-width: var(--tide-border-width-1);
|
|
67
|
+
--switch-width: calc(var(--switch-indicator-size) * 2 + var(--switch-indicator-margin) * 4);
|
|
68
|
+
--switch-height: calc(var(--switch-indicator-size) + var(--switch-indicator-margin) * 2);
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
width: var(--switch-width);
|
|
71
|
+
height: var(--switch-height);
|
|
72
|
+
transition: var(--tide-animate);
|
|
73
|
+
transition-property: background-color, border-color;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
.tide-switch.active {
|
|
75
77
|
border-color: var(--tide-secondary);
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
.tide-switch.active .tide-switch-indicator {
|
|
79
|
-
left: 36px;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
80
|
.tide-switch-indicator {
|
|
83
|
-
left:
|
|
81
|
+
left: var(--switch-indicator-margin);
|
|
84
82
|
transition: var(--tide-animate);
|
|
85
83
|
transition-property: left, border-color, background-color;
|
|
86
84
|
}
|
|
85
|
+
|
|
86
|
+
.tide-switch-indicator.active {
|
|
87
|
+
left: calc(
|
|
88
|
+
var(--switch-width) - var(--switch-indicator-size) - var(--switch-indicator-margin) - var(--switch-border-width)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
87
91
|
</style>
|