@bagelink/vue 1.8.71 → 1.8.76

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.
@@ -1,15 +1,10 @@
1
+ <!-- prettier-ignore-start -->
2
+ <!-- eslint-disable -->
1
3
  <script setup lang="ts">
2
- /**
3
- * Dialog - Native dialog wrapper with animations
4
- *
5
- * Uses native <dialog> element for:
6
- * - Automatic focus trap
7
- * - ESC key handling
8
- * - Backdrop via ::backdrop
9
- * - Browser-managed z-index stacking
10
- */
11
4
  import type { DialogWidth, DialogPosition } from './dialogTypes'
5
+ import { Btn } from '@bagelink/vue'
12
6
  import { ref, computed, onMounted, watch } from 'vue'
7
+
13
8
  import { DIALOG_WIDTHS } from './dialogTypes'
14
9
 
15
10
  const props = withDefaults(defineProps<{
@@ -18,10 +13,18 @@ const props = withDefaults(defineProps<{
18
13
  width?: DialogWidth
19
14
  position?: DialogPosition
20
15
  dismissable?: boolean
16
+ flat?: boolean
17
+ thin?: boolean
18
+ centerTitle?: boolean
19
+ closePlacement?: 'header-end' | 'header-start' | 'overlay-start' | 'overlay-end' | 'none'
21
20
  }>(), {
22
21
  width: 'm',
23
22
  position: 'center',
24
- dismissable: true
23
+ dismissable: true,
24
+ flat: true,
25
+ thin: false,
26
+ centerTitle: false,
27
+ closePlacement: 'header-end'
25
28
  })
26
29
 
27
30
  const emit = defineEmits<{
@@ -31,13 +34,14 @@ const emit = defineEmits<{
31
34
 
32
35
  const dialogRef = ref<HTMLDialogElement | null>(null)
33
36
  const isClosing = ref(false)
37
+ const overlayButtonsVisible = ref(true)
34
38
 
35
39
  const widthStyle = computed(() => DIALOG_WIDTHS[props.width])
36
40
 
37
41
  const positionClass = computed(() => {
38
- if (props.position === 'left') return 'dialog-left'
39
- if (props.position === 'right') return 'dialog-right'
40
- return 'dialog-center'
42
+ if (props.position === 'left') return 'dialog-left ms-1 me-auto mt-1 mb-1 height-100-2 m_m-0 m_h-100vh m_max-h-100vh m_radius-0'
43
+ if (props.position === 'right') return 'dialog-right me-1 ms-auto mt-1 mb-1 height-100-2 m_m-0 m_h-100vh m_max-h-100vh m_radius-0'
44
+ return 'dialog-center mx-auto'
41
45
  })
42
46
 
43
47
  // Handle open/close
@@ -45,6 +49,11 @@ watch(() => props.open, (isOpen) => {
45
49
  if (isOpen) {
46
50
  dialogRef.value?.showModal()
47
51
  isClosing.value = false
52
+ overlayButtonsVisible.value = false
53
+ // Show overlay buttons after dialog animation completes
54
+ setTimeout(() => {
55
+ overlayButtonsVisible.value = true
56
+ }, 250)
48
57
  } else {
49
58
  closeWithAnimation()
50
59
  }
@@ -86,6 +95,7 @@ function onCancel(e: Event) {
86
95
  }
87
96
 
88
97
  function close() {
98
+ overlayButtonsVisible.value = false
89
99
  emit('update:open', false)
90
100
  }
91
101
 
@@ -95,243 +105,173 @@ defineExpose({ close })
95
105
  <template>
96
106
  <dialog
97
107
  ref="dialogRef" :class="[positionClass, { 'is-closing': isClosing }]"
98
- :style="{ '--dialog-width': widthStyle }" @click="onBackdropClick" @cancel="onCancel"
108
+ :style="{ '--dialog-width': widthStyle }" class="border-none overflow-hidden shadow-30 p-0 testMe1" @click="onBackdropClick"
109
+ @cancel="onCancel"
99
110
  @animationend="onAnimationEnd"
100
111
  >
101
- <div class="dialog-content" @click.stop>
112
+ <div class="grid grid-dialog h-100p max-height-100-2" @click.stop>
102
113
  <!-- Header -->
103
- <header v-if="title || dismissable" class="dialog-header">
104
- <h2 v-if="title" class="dialog-title">
114
+ <header
115
+ v-if="title || dismissable" class="flex space-between flex-shrink-0 align-items-start gap-05"
116
+ :class="{ 'border-bottom': !flat, 'border-none': !title, 'm_ps-1': title, 'p-05 m_p-05': !title, 'ps-1-5 pe-1 py-1 ': !thin && title, 'ps-1 pe-05 py-05': thin && title }"
117
+ >
118
+ <Btn v-if="dismissable && closePlacement === 'header-start'" flat icon="close" aria-label="Close" class="dialog-close" thin @click="close" />
119
+
120
+ <h3 v-if="title" class="dialog-title m-0 txt20 semi w-100p word-break-all" :class="{ 'txt-center': centerTitle }">
105
121
  {{ title }}
106
- </h2>
107
- <button v-if="dismissable" type="button" class="dialog-close" aria-label="Close" @click="close">
108
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
109
- <path d="M18 6L6 18M6 6l12 12" />
110
- </svg>
111
- </button>
122
+ </h3>
123
+ <div v-if="!title" class="flex-grow-1" />
124
+ <Btn v-if="dismissable && closePlacement === 'header-end'" flat icon="close" aria-label="Close" class="dialog-close" thin @click="close" />
112
125
  </header>
113
126
 
114
127
  <!-- Body -->
115
- <div class="dialog-body">
128
+ <div class="overflow-y flex-grow-1 m_p-1" :class="{ 'px-1 py-05': thin, 'px-1-5 py-1': !thin }">
116
129
  <slot />
117
130
  </div>
118
131
 
119
132
  <!-- Footer -->
120
- <footer v-if="$slots.footer" class="dialog-footer">
133
+ <footer v-if="$slots.footer" class="flex flex-shrink-0 gap-05 m_px-05 m_py-05 justify-content-end" :class="{ 'border-top': !flat, 'px-05 py-05': thin, 'px-1-5 py-1': !thin }">
121
134
  <slot name="footer" />
122
135
  </footer>
123
136
  </div>
137
+ <!-- Overlay close buttons -->
138
+ <Btn v-if="dismissable && closePlacement === 'overlay-start' && overlayButtonsVisible" icon="close" aria-label="Close" class="fixed top-1 start-1 z-999 overlay-btn" @click="close" />
139
+ <Btn v-if="dismissable && closePlacement === 'overlay-end' && overlayButtonsVisible" icon="close" aria-label="Close" class="fixed top-1 end-1 z-999 overlay-btn" @click="close" />
124
140
  </dialog>
125
141
  </template>
126
142
 
127
143
  <style scoped>
144
+ /* Base dialog styles */
128
145
  dialog {
129
- border: none;
130
146
  border-radius: var(--bgl-card-radius, 12px);
131
- padding: 0;
132
147
  max-width: var(--dialog-width);
133
148
  width: calc(100% - 2rem);
134
149
  max-height: calc(100vh - 2rem);
135
150
  background: var(--bgl-popup-bg, #fff);
136
- color: var(--bgl-text, #1a1a1a);
137
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
138
- overflow: hidden;
151
+ color: var(--bgl-popup-text, black);
139
152
  }
140
153
 
141
- dialog::backdrop {
142
- background: var(--bgl-dark-bg, rgba(0, 0, 0, 0.5));
143
- opacity: 0;
144
- transition: opacity 0.2s ease-out;
154
+ .grid-dialog {
155
+ grid-template-rows: auto 1fr auto;
145
156
  }
146
157
 
147
- dialog[open]::backdrop {
148
- opacity: 1;
158
+ .height-100-2 {
159
+ height: calc(100vh - 2rem);
149
160
  }
150
161
 
151
- /* Center position (default) */
152
- dialog.dialog-center {
153
- margin: auto;
162
+ .max-height-100-2 {
163
+ max-height: calc(100vh - 2rem);
154
164
  }
155
165
 
156
- dialog.dialog-center[open] {
157
- animation: dialog-fade-in 0.2s ease-out;
166
+ .dialog-close {
167
+ margin-top: 0.125rem;
158
168
  }
159
169
 
160
- dialog.dialog-center.is-closing {
161
- animation: dialog-fade-out 0.15s ease-in forwards;
170
+ .overlay-btn {
171
+ opacity: 0;
172
+ animation: fade-in 0.3s ease-out 0.1s forwards;
162
173
  }
163
174
 
164
- @keyframes dialog-fade-in {
165
- from {
166
- opacity: 0;
167
- transform: scale(0.95) translateY(10px);
168
- }
169
-
170
- to {
171
- opacity: 1;
172
- transform: scale(1) translateY(0);
173
- }
175
+ /* Backdrop animations */
176
+ dialog::backdrop {
177
+ background: var(--bgl-dark-bg, rgba(0, 0, 0, 0.5));
178
+ opacity: 0;
174
179
  }
175
180
 
176
- @keyframes dialog-fade-out {
177
- from {
178
- opacity: 1;
179
- transform: scale(1) translateY(0);
180
- }
181
+ dialog[open]::backdrop {
182
+ animation: fade-in 0.2s ease-out forwards;
183
+ }
181
184
 
182
- to {
183
- opacity: 0;
184
- transform: scale(0.95) translateY(10px);
185
- }
185
+ dialog.is-closing::backdrop {
186
+ animation: fade-out 0.15s ease-in forwards;
186
187
  }
187
188
 
188
- /* Right position */
189
- dialog.dialog-right {
190
- margin: 0;
191
- margin-inline-start: auto;
192
- margin-inline-end: 1rem;
193
- margin-top: 1rem;
194
- margin-bottom: 1rem;
195
- height: calc(100vh - 2rem);
196
- max-height: calc(100vh - 2rem);
197
- border-radius: var(--bgl-card-radius, 12px);
189
+ /* Dialog position animations */
190
+ dialog.dialog-center[open] {
191
+ animation: scale-fade-in 0.2s ease-out;
198
192
  }
199
193
 
194
+ dialog.dialog-center.is-closing {
195
+ animation: scale-fade-out 0.15s ease-in forwards;
196
+ }
200
197
  dialog.dialog-right[open] {
201
- animation: dialog-slide-in-right 0.25s ease-out;
198
+ animation: slide-in-right 0.25s ease-out;
202
199
  }
203
200
 
204
201
  dialog.dialog-right.is-closing {
205
- animation: dialog-slide-out-right 0.2s ease-in forwards;
202
+ animation: slide-out-right 0.2s ease-in forwards;
206
203
  }
207
204
 
208
- @keyframes dialog-slide-in-right {
205
+ dialog.dialog-left[open] {
206
+ animation: slide-in-left 0.25s ease-out;
207
+ }
208
+
209
+ dialog.dialog-left.is-closing {
210
+ animation: slide-out-left 0.2s ease-in forwards;
211
+ }
212
+ /* Keyframes - shared and reusable */
213
+ @keyframes fade-in {
209
214
  from {
210
215
  opacity: 0;
211
- transform: translateX(100%);
212
216
  }
213
217
 
214
218
  to {
215
219
  opacity: 1;
216
- transform: translateX(0);
217
220
  }
218
221
  }
219
222
 
220
- @keyframes dialog-slide-out-right {
223
+ @keyframes fade-out {
221
224
  from {
222
225
  opacity: 1;
223
- transform: translateX(0);
224
226
  }
225
-
226
227
  to {
227
228
  opacity: 0;
228
- transform: translateX(100%);
229
229
  }
230
230
  }
231
231
 
232
- /* Left position */
233
- dialog.dialog-left {
234
- margin: 0;
235
- margin-inline-end: auto;
236
- margin-inline-start: 1rem;
237
- margin-top: 1rem;
238
- margin-bottom: 1rem;
239
- height: calc(100vh - 2rem);
240
- max-height: calc(100vh - 2rem);
241
- border-radius: var(--bgl-card-radius, 12px);
242
- }
243
-
244
- dialog.dialog-left[open] {
245
- animation: dialog-slide-in-left 0.25s ease-out;
246
- }
247
-
248
- dialog.dialog-left.is-closing {
249
- animation: dialog-slide-out-left 0.2s ease-in forwards;
250
- }
251
-
252
- @keyframes dialog-slide-in-left {
232
+ @keyframes scale-fade-in {
253
233
  from {
254
234
  opacity: 0;
255
- transform: translateX(-100%);
235
+ transform: scale(0.95) translateY(10px);
256
236
  }
257
-
258
237
  to {
259
238
  opacity: 1;
260
- transform: translateX(0);
239
+ transform: scale(1) translateY(0);
261
240
  }
262
241
  }
263
242
 
264
- @keyframes dialog-slide-out-left {
243
+ @keyframes scale-fade-out {
265
244
  from {
266
245
  opacity: 1;
267
- transform: translateX(0);
246
+ transform: scale(1) translateY(0);
268
247
  }
269
-
270
248
  to {
271
249
  opacity: 0;
272
- transform: translateX(-100%);
250
+ transform: scale(0.95) translateY(10px);
273
251
  }
274
252
  }
275
253
 
276
- /* Content structure */
277
- .dialog-content {
278
- display: flex;
279
- flex-direction: column;
280
- height: 100%;
281
- max-height: inherit;
282
- }
283
-
284
- .dialog-header {
285
- display: flex;
286
- align-items: center;
287
- justify-content: space-between;
288
- padding: 1rem 1.5rem;
289
- border-bottom: 1px solid var(--bgl-border-color, #e5e5e5);
290
- flex-shrink: 0;
254
+ @keyframes slide-in-right {
255
+ from { opacity: 0; transform: translateX(100%); }
256
+ to { opacity: 1; transform: translateX(0); }
291
257
  }
292
258
 
293
- .dialog-title {
294
- margin: 0;
295
- font-size: 1.125rem;
296
- font-weight: 600;
259
+ @keyframes slide-out-right {
260
+ from { opacity: 1; transform: translateX(0); }
261
+ to { opacity: 0; transform: translateX(100%); }
297
262
  }
298
263
 
299
- .dialog-close {
300
- display: flex;
301
- align-items: center;
302
- justify-content: center;
303
- width: 32px;
304
- height: 32px;
305
- border: none;
306
- background: transparent;
307
- border-radius: 6px;
308
- cursor: pointer;
309
- color: var(--bgl-text-secondary, #666);
310
- transition: background 0.15s ease, color 0.15s ease;
264
+ @keyframes slide-in-left {
265
+ from { opacity: 0; transform: translateX(-100%); }
266
+ to { opacity: 1; transform: translateX(0); }
311
267
  }
312
268
 
313
- .dialog-close:hover {
314
- background: var(--bgl-gray-tint, #f0f0f0);
315
- color: var(--bgl-text, #1a1a1a);
269
+ @keyframes slide-out-left {
270
+ from { opacity: 1; transform: translateX(0); }
271
+ to { opacity: 0; transform: translateX(-100%); }
316
272
  }
317
273
 
318
- .dialog-body {
319
- flex: 1;
320
- padding: 1.5rem;
321
- overflow-y: auto;
322
- }
323
-
324
- .dialog-footer {
325
- display: flex;
326
- align-items: center;
327
- justify-content: flex-end;
328
- gap: 0.75rem;
329
- padding: 1rem 1.5rem;
330
- border-top: 1px solid var(--bgl-border-color, #e5e5e5);
331
- flex-shrink: 0;
332
- }
333
-
334
- /* Full width */
274
+ /* Full width adjustments */
335
275
  dialog[style*="--dialog-width: 100%"] {
336
276
  max-width: calc(100vw - 2rem);
337
277
  }
@@ -343,16 +283,17 @@ dialog.dialog-right[style*="--dialog-width: 100%"] {
343
283
  }
344
284
 
345
285
  /* Mobile adjustments */
346
- @media screen and (max-width: 640px) {
286
+ @media screen and (max-width: 910px) {
287
+ dialog.dialog-left .grid-dialog,
288
+ dialog.dialog-right .grid-dialog {
289
+ max-height: 100vh;
290
+ }
347
291
 
348
292
  dialog.dialog-left,
349
293
  dialog.dialog-right {
350
- margin: 0;
351
294
  width: 100%;
352
- max-width: 100%;
353
- height: 100vh;
354
- max-height: 100vh;
355
- border-radius: 0;
356
295
  }
357
296
  }
358
297
  </style>
298
+ <!-- prettier-ignore-end -->
299
+ <!-- eslint-enable -->
@@ -51,7 +51,7 @@ function handleClose() {
51
51
  :open="open" :title="title" :width="width" :position="position" :dismissable="dismissable"
52
52
  @update:open="$emit('update:open', $event)" @close="handleClose"
53
53
  >
54
- <p class="confirm-message">
54
+ <p>
55
55
  {{ message }}
56
56
  </p>
57
57
 
@@ -67,10 +67,4 @@ function handleClose() {
67
67
  </template>
68
68
 
69
69
  <style scoped>
70
- .confirm-message {
71
- margin: 0;
72
- font-size: 1rem;
73
- line-height: 1.5;
74
- color: var(--bgl-text-secondary, #666);
75
- }
76
70
  </style>
@@ -101,7 +101,7 @@ function handleClose() {
101
101
  >
102
102
  <form @submit.prevent="handleSubmit">
103
103
  <!-- Error message -->
104
- <div v-if="error" class="dialog-form-error">
104
+ <div v-if="error" class="dialog-form-error bg-red-30 color-red radius-1 mb-1 txt14 px-1 py-075">
105
105
  {{ error }}
106
106
  </div>
107
107
 
@@ -113,12 +113,12 @@ function handleClose() {
113
113
  <!-- Delete button (left side) -->
114
114
  <Btn
115
115
  v-if="hasDeleteHandler" color="red" flat :loading="isDeleting" :disabled="isSubmitting"
116
- class="dialog-delete-btn" @click="handleDelete"
116
+ class="me-auto" @click="handleDelete"
117
117
  >
118
118
  {{ deleteText }}
119
119
  </Btn>
120
120
 
121
- <div class="dialog-footer-spacer" />
121
+ <div class="flex-grow-1" />
122
122
 
123
123
  <!-- Cancel button -->
124
124
  <Btn flat :disabled="isSubmitting || isDeleting" @click="handleCancel">
@@ -132,22 +132,3 @@ function handleClose() {
132
132
  </template>
133
133
  </Dialog>
134
134
  </template>
135
-
136
- <style scoped>
137
- .dialog-form-error {
138
- background: var(--bgl-red-tint, #fee);
139
- color: var(--bgl-red, #dc3545);
140
- padding: 0.75rem 1rem;
141
- border-radius: 6px;
142
- margin-bottom: 1rem;
143
- font-size: 0.875rem;
144
- }
145
-
146
- .dialog-footer-spacer {
147
- flex: 1;
148
- }
149
-
150
- .dialog-delete-btn {
151
- margin-inline-end: auto;
152
- }
153
- </style>