@citizenplane/pimp 10.9.3 → 11.0.1
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/dist/pimp.es.js +961 -944
- package/dist/pimp.umd.js +40 -40
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/styles/utilities/_index.scss +9 -0
- package/src/components/CpDialog.vue +79 -37
- package/src/components/icons/IconFlight.vue +2 -11
- package/src/components/icons/IconFlightFilled.vue +1 -1
- package/src/stories/CpDialog.stories.ts +72 -15
package/package.json
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
overflow: hidden;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
.u-layout-box-padding,
|
|
11
|
+
%u-layout-box-padding {
|
|
12
|
+
padding: var(--cp-spacing-2xl);
|
|
13
|
+
|
|
14
|
+
@media (max-width: 650px) {
|
|
15
|
+
padding: var(--cp-spacing-xl);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
.u-asterisk {
|
|
11
20
|
position: relative;
|
|
12
21
|
top: fn.px-to-rem(-3);
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="cpDialog">
|
|
3
|
-
<dialog
|
|
4
|
-
|
|
3
|
+
<dialog
|
|
4
|
+
ref="dialogElement"
|
|
5
|
+
:aria-describedby="ariaDescribedby"
|
|
6
|
+
:aria-labelledby="titleId"
|
|
7
|
+
aria-modal="true"
|
|
8
|
+
class="cpDialog__dialog"
|
|
9
|
+
@keydown.esc.stop.prevent="handleClose"
|
|
10
|
+
>
|
|
11
|
+
<div aria-hidden="true" class="cpDialog__overlay" />
|
|
5
12
|
<main ref="dialogContainer" class="cpDialog__container" :style="dynamicStyle" @keydown.tab="trapFocus">
|
|
6
|
-
<header
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
<header class="cpDialog__header">
|
|
14
|
+
<div class="cpDialog__left">
|
|
15
|
+
<div class="cpDialog__title">
|
|
16
|
+
<slot name="title">
|
|
17
|
+
<h2 :id="titleId">{{ title }}</h2>
|
|
18
|
+
</slot>
|
|
19
|
+
</div>
|
|
20
|
+
<div v-if="hasSubtitle" :id="subtitleId" class="cpDialog__subtitle">
|
|
21
|
+
<slot name="subtitle">
|
|
22
|
+
<p>{{ subtitle }}</p>
|
|
23
|
+
</slot>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<button aria-label="Close dialog" class="cpDialog__close" type="button" @click="handleClose">
|
|
27
|
+
<cp-icon aria-hidden="true" type="x" />
|
|
10
28
|
</button>
|
|
11
29
|
</header>
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
</section>
|
|
16
|
-
</slot>
|
|
30
|
+
<section class="cpDialog__content">
|
|
31
|
+
<slot />
|
|
32
|
+
</section>
|
|
17
33
|
<footer v-if="hasFooterSlot" class="cpDialog__footer">
|
|
18
34
|
<slot name="footer" />
|
|
19
35
|
</footer>
|
|
@@ -23,12 +39,14 @@
|
|
|
23
39
|
</template>
|
|
24
40
|
|
|
25
41
|
<script setup lang="ts">
|
|
26
|
-
import { computed, ref, useSlots, onMounted, nextTick, onBeforeUnmount } from 'vue'
|
|
42
|
+
import { computed, ref, useSlots, onMounted, nextTick, onBeforeUnmount, useId } from 'vue'
|
|
27
43
|
|
|
28
44
|
import { getKeyboardFocusableElements, handleTrapFocus } from '@/helpers/dom'
|
|
29
45
|
|
|
30
46
|
interface Props {
|
|
31
47
|
maxWidth?: number
|
|
48
|
+
subtitle?: string
|
|
49
|
+
title: string
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
interface Emits {
|
|
@@ -37,10 +55,17 @@ interface Emits {
|
|
|
37
55
|
|
|
38
56
|
const props = withDefaults(defineProps<Props>(), {
|
|
39
57
|
maxWidth: 600,
|
|
58
|
+
subtitle: '',
|
|
40
59
|
})
|
|
41
60
|
|
|
42
61
|
const emit = defineEmits<Emits>()
|
|
43
62
|
|
|
63
|
+
const dialogId = useId()
|
|
64
|
+
const titleId = computed(() => `${dialogId}-title`)
|
|
65
|
+
const subtitleId = computed(() => `${dialogId}-subtitle`)
|
|
66
|
+
|
|
67
|
+
const ariaDescribedby = computed(() => (hasSubtitle.value ? subtitleId.value : undefined))
|
|
68
|
+
|
|
44
69
|
const slots = useSlots()
|
|
45
70
|
|
|
46
71
|
const dialogElement = ref<HTMLDialogElement | null>(null)
|
|
@@ -48,8 +73,8 @@ const dialogContainer = ref<HTMLElement | null>(null)
|
|
|
48
73
|
|
|
49
74
|
const dynamicStyle = computed(() => ({ maxWidth: `${props.maxWidth}px` }))
|
|
50
75
|
|
|
51
|
-
const
|
|
52
|
-
|
|
76
|
+
const hasSubtitleSlot = computed(() => !!slots.subtitle)
|
|
77
|
+
const hasSubtitle = computed(() => !!props.subtitle || hasSubtitleSlot.value)
|
|
53
78
|
const hasFooterSlot = computed(() => !!slots.footer)
|
|
54
79
|
|
|
55
80
|
const handleClose = () => emit('close')
|
|
@@ -151,64 +176,81 @@ $dialog-breakpoint: 550px;
|
|
|
151
176
|
|
|
152
177
|
&__header,
|
|
153
178
|
&__footer {
|
|
154
|
-
|
|
155
|
-
align-items: center;
|
|
156
|
-
justify-content: space-between;
|
|
179
|
+
padding: var(--cp-spacing-xl) var(--cp-spacing-2xl);
|
|
157
180
|
}
|
|
158
181
|
|
|
159
182
|
&__header {
|
|
160
|
-
|
|
183
|
+
display: flex;
|
|
184
|
+
align-items: flex-start;
|
|
185
|
+
justify-content: space-between;
|
|
186
|
+
gap: var(--cp-spacing-md);
|
|
161
187
|
border-bottom: 1px solid var(--cp-border-soft);
|
|
162
188
|
}
|
|
163
189
|
|
|
190
|
+
&__left {
|
|
191
|
+
display: flex;
|
|
192
|
+
flex-direction: column;
|
|
193
|
+
min-width: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
&__title > * {
|
|
197
|
+
@extend %u-text-ellipsis;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&__title,
|
|
201
|
+
&__title > h2 {
|
|
202
|
+
font-size: var(--cp-text-size-xl);
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
line-height: var(--cp-line-height-xl);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
&__subtitle,
|
|
208
|
+
&__subtitle > p {
|
|
209
|
+
font-size: var(--cp-text-size-sm);
|
|
210
|
+
font-weight: 400;
|
|
211
|
+
line-height: var(--cp-line-height-md);
|
|
212
|
+
color: var(--cp-text-secondary);
|
|
213
|
+
}
|
|
214
|
+
|
|
164
215
|
&__close {
|
|
165
|
-
|
|
166
|
-
top: var(--cp-spacing-2xl);
|
|
167
|
-
right: var(--cp-spacing-2xl);
|
|
168
|
-
display: inline-flex;
|
|
216
|
+
display: flex;
|
|
169
217
|
align-items: center;
|
|
170
218
|
justify-content: center;
|
|
171
219
|
padding: var(--cp-spacing-sm);
|
|
172
220
|
border-radius: var(--cp-radius-md);
|
|
173
221
|
color: var(--cp-foreground-secondary);
|
|
174
|
-
transition:
|
|
175
|
-
transform: translate(var(--cp-dimensions-1), calc(var(--cp-dimensions-1) * -1));
|
|
222
|
+
transition: 200ms ease-in-out;
|
|
176
223
|
transition-property: color, background-color;
|
|
177
224
|
|
|
178
225
|
&:hover {
|
|
179
226
|
color: var(--cp-foreground-primary);
|
|
227
|
+
background-color: var(--cp-background-secondary);
|
|
180
228
|
}
|
|
181
229
|
|
|
182
230
|
&:focus-visible {
|
|
183
|
-
outline:
|
|
231
|
+
outline: fn.px-to-rem(2) solid var(--cp-focus);
|
|
184
232
|
}
|
|
185
233
|
}
|
|
186
234
|
|
|
187
235
|
&__content {
|
|
236
|
+
@extend %u-layout-box-padding;
|
|
237
|
+
|
|
188
238
|
overflow: auto;
|
|
189
239
|
min-height: 0;
|
|
190
240
|
flex: 1;
|
|
191
241
|
}
|
|
192
242
|
|
|
193
243
|
&__footer {
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: center;
|
|
246
|
+
justify-content: space-between;
|
|
247
|
+
|
|
194
248
|
&:not(:empty) {
|
|
195
249
|
border-top: 1px solid var(--cp-border-soft);
|
|
196
|
-
padding: var(--cp-spacing-xl) var(--cp-spacing-2xl);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
&--noBorder {
|
|
200
|
-
border-top: none;
|
|
201
250
|
}
|
|
202
251
|
}
|
|
203
252
|
}
|
|
204
253
|
|
|
205
|
-
@media screen and (max-width: $dialog-breakpoint) {
|
|
206
|
-
.cpDialog__close {
|
|
207
|
-
top: var(--cp-spacing-xl);
|
|
208
|
-
right: var(--cp-spacing-xl);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
254
|
@media (max-width: 650px) {
|
|
213
255
|
.cpDialog__footer {
|
|
214
256
|
padding: var(--cp-spacing-xl);
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<svg
|
|
3
|
-
width="24"
|
|
4
|
-
height="25"
|
|
5
|
-
viewBox="0 0 24 25"
|
|
6
|
-
fill="none"
|
|
7
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
-
stroke-width="2"
|
|
9
|
-
stroke="currentColor"
|
|
10
|
-
>
|
|
2
|
+
<svg fill="currentColor" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
|
11
3
|
<path
|
|
12
|
-
d="
|
|
13
|
-
stroke-linejoin="round"
|
|
4
|
+
d="M13.1035 4.13806C13.1033 3.52857 12.6096 3.03455 12 3.03455C11.3904 3.03455 10.8958 3.52857 10.8955 4.13806V8.7865L4.5 13.6049V14.7113L11 12.5072V18.1371L10.5615 18.434L9.39746 19.224V20.7494L12 19.8002L14.6016 20.7484V19.2152L13.4277 18.3939L13 18.0961V12.4691L19.5 14.7045V13.6049L13.1035 8.7865V4.13806ZM15.1035 7.78748L20.7031 12.0082C21.2046 12.386 21.4999 12.977 21.5 13.6049V15.4056C21.5 16.4355 20.4856 17.1596 19.5117 16.8246L15 15.2719V17.0531L15.7471 17.5756C16.2826 17.9497 16.6016 18.5619 16.6016 19.2152V21.4642C16.6013 22.5056 15.5663 23.2303 14.5879 22.8734L11.999 21.9291L9.41113 22.8734C8.43271 23.2302 7.39763 22.5056 7.39746 21.4642V19.224C7.39746 18.561 7.72572 17.9409 8.27441 17.5687L9 17.0756V15.2972L4.48145 16.8295C3.56963 17.1385 2.62619 16.5225 2.51172 15.598L2.5 15.4095V13.6049C2.5001 12.977 2.79542 12.386 3.29688 12.0082L8.89551 7.78845V4.13806C8.89577 2.424 10.2859 1.03455 12 1.03455C13.7141 1.03455 15.1033 2.424 15.1035 4.13806V7.78748Z"
|
|
14
5
|
/>
|
|
15
6
|
</svg>
|
|
16
7
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<svg fill="currentColor" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
|
3
3
|
<path
|
|
4
|
-
d="M20.1018 12.
|
|
4
|
+
d="M20.1018 12.807C20.3525 12.9959 20.5 13.2917 20.5 13.6057V15.4063C20.5 15.7496 20.162 15.9908 19.8374 15.8791L14 13.871V17.5749L15.1747 18.3955C15.4425 18.5826 15.602 18.8886 15.602 19.2153V21.4644C15.602 21.8116 15.2568 22.0531 14.9306 21.9341L12 20.865L9.0683 21.9342C8.74213 22.0531 8.39699 21.8116 8.39699 21.4645V19.2242C8.39699 18.8926 8.56134 18.5826 8.83577 18.3965L10 17.6071V13.9032L4.16055 15.8831C3.83632 15.993 3.5 15.7519 3.5 15.4096V13.6057C3.5 13.2917 3.64747 12.9959 3.89824 12.807L9.89599 8.28803V4.13903C9.89599 2.97703 10.838 2.03503 12 2.03503C13.162 2.03503 14.104 2.97703 14.104 4.13903V8.28803L20.1018 12.807Z"
|
|
5
5
|
/>
|
|
6
6
|
</svg>
|
|
7
7
|
</template>
|
|
@@ -14,13 +14,6 @@ const meta = {
|
|
|
14
14
|
},
|
|
15
15
|
onClose: { action: 'closed' },
|
|
16
16
|
},
|
|
17
|
-
parameters: {
|
|
18
|
-
styles: {
|
|
19
|
-
'.header': {
|
|
20
|
-
color: 'red',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
17
|
} satisfies Meta<typeof CpDialog>
|
|
25
18
|
|
|
26
19
|
export default meta
|
|
@@ -29,6 +22,8 @@ type Story = StoryObj<typeof meta>
|
|
|
29
22
|
export const Default: Story = {
|
|
30
23
|
args: {
|
|
31
24
|
maxWidth: 600,
|
|
25
|
+
title: 'Dialog title',
|
|
26
|
+
subtitle: 'Dialog subtitle',
|
|
32
27
|
},
|
|
33
28
|
render: (args) => ({
|
|
34
29
|
setup() {
|
|
@@ -39,14 +34,76 @@ export const Default: Story = {
|
|
|
39
34
|
<CpButton @click="isOpen = true">Open Dialog</CpButton>
|
|
40
35
|
<CpTransitionDialog>
|
|
41
36
|
<CpDialog v-bind="args" v-if="isOpen" @close="isOpen = false">
|
|
42
|
-
<template #
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
<template #title>Header slot</template>
|
|
38
|
+
<template #subtitle>Subtitle</template>
|
|
39
|
+
<p>This is the default slot content. You can put any content here.</p>
|
|
40
|
+
<template #footer>
|
|
41
|
+
<CpButton @click="isOpen = false">Cancel</CpButton>
|
|
42
|
+
This is the footer slot
|
|
43
|
+
</template>
|
|
44
|
+
</CpDialog>
|
|
45
|
+
</CpTransitionDialog>
|
|
46
|
+
`,
|
|
47
|
+
}),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const TitleSubtitleWithProps: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
maxWidth: 600,
|
|
53
|
+
title: 'Dialog title',
|
|
54
|
+
},
|
|
55
|
+
render: (args) => ({
|
|
56
|
+
setup() {
|
|
57
|
+
const isOpen = ref(false)
|
|
58
|
+
return { args, isOpen }
|
|
59
|
+
},
|
|
60
|
+
template: `
|
|
61
|
+
<CpButton @click="isOpen = true">Open Dialog with string title/subtitle</CpButton>
|
|
62
|
+
<CpTransitionDialog>
|
|
63
|
+
<CpDialog v-bind="args" v-if="isOpen" @close="isOpen = false">
|
|
64
|
+
<p>This is the default slot content. You can put any content here.</p>
|
|
65
|
+
<template #footer>
|
|
66
|
+
<CpButton @click="isOpen = false">Cancel</CpButton>
|
|
67
|
+
This is the footer slot
|
|
68
|
+
</template>
|
|
69
|
+
</CpDialog>
|
|
70
|
+
</CpTransitionDialog>
|
|
71
|
+
`,
|
|
72
|
+
}),
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const TitleSubtitleWithSlots: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
maxWidth: 560,
|
|
78
|
+
titleTag: 'div',
|
|
79
|
+
subtitleTag: 'div',
|
|
80
|
+
},
|
|
81
|
+
render: (args) => ({
|
|
82
|
+
setup() {
|
|
83
|
+
const isOpen = ref(false)
|
|
84
|
+
return { args, isOpen }
|
|
85
|
+
},
|
|
86
|
+
template: `
|
|
87
|
+
<CpButton @click="isOpen = true">Open Dialog (flex title/subtitle)</CpButton>
|
|
88
|
+
<CpTransitionDialog>
|
|
89
|
+
<CpDialog v-bind="args" v-if="isOpen" @close="isOpen = false" title-tag="div" subtitle-tag="div">
|
|
90
|
+
<template #title>
|
|
91
|
+
<div style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;">
|
|
92
|
+
<cp-icon type="info" style="flex-shrink: 0;" />
|
|
93
|
+
<span>Dialog with flex layout</span>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
<template #subtitle>
|
|
97
|
+
<div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
|
98
|
+
<span>Optional info</span>
|
|
99
|
+
<span style="color: var(--cp-text-tertiary);">•</span>
|
|
100
|
+
<span>Last updated today</span>
|
|
101
|
+
</div>
|
|
102
|
+
</template>
|
|
103
|
+
<p>Body content. Title and subtitle above are divs with flex layout.</p>
|
|
104
|
+
<template #footer>
|
|
105
|
+
<CpButton @click="isOpen = false">Close</CpButton>
|
|
106
|
+
</template>
|
|
50
107
|
</CpDialog>
|
|
51
108
|
</CpTransitionDialog>
|
|
52
109
|
`,
|