@saasmakers/ui 1.4.49 → 1.4.51
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/app/components/bases/BaseAlert.vue +3 -3
- package/app/components/bases/BaseAvatar.vue +4 -4
- package/app/components/bases/BaseButton.vue +3 -3
- package/app/components/bases/BaseCard.vue +13 -32
- package/app/components/bases/BaseDivider.vue +5 -5
- package/app/components/bases/BaseIcon.vue +3 -3
- package/app/components/bases/BaseQuote.vue +3 -3
- package/app/components/bases/BaseTags.vue +5 -5
- package/app/components/bases/BaseText.vue +3 -3
- package/app/components/fields/FieldAvatar.vue +21 -21
- package/app/components/fields/FieldDays.vue +11 -11
- package/app/components/fields/FieldEmojis.vue +15 -15
- package/app/components/fields/FieldMessage.vue +19 -19
- package/app/components/fields/FieldSelect.vue +4 -4
- package/app/components/layout/LayoutBottomSheet.vue +391 -0
- package/app/types/bases.d.ts +0 -3
- package/app/types/global.d.ts +3 -1
- package/app/types/layout.d.ts +3 -0
- package/nuxt.config.ts +16 -0
- package/package.json +2 -1
|
@@ -115,6 +115,9 @@ async function onClose(event: MouseEvent) {
|
|
|
115
115
|
"fr": {
|
|
116
116
|
"closeThisMessage": "Fermer ce message"
|
|
117
117
|
},
|
|
118
|
+
"id": {
|
|
119
|
+
"closeThisMessage": "Tutup pesan ini"
|
|
120
|
+
},
|
|
118
121
|
"it": {
|
|
119
122
|
"closeThisMessage": "Chiudi questo messaggio"
|
|
120
123
|
},
|
|
@@ -136,9 +139,6 @@ async function onClose(event: MouseEvent) {
|
|
|
136
139
|
"pt-BR": {
|
|
137
140
|
"closeThisMessage": "Fechar esta mensagem"
|
|
138
141
|
},
|
|
139
|
-
"id": {
|
|
140
|
-
"closeThisMessage": "Tutup pesan ini"
|
|
141
|
-
},
|
|
142
142
|
"vi": {
|
|
143
143
|
"closeThisMessage": "Đóng thông báo này"
|
|
144
144
|
}
|
|
@@ -172,6 +172,10 @@ function onMouseLeave() {
|
|
|
172
172
|
"edit": "Modifier",
|
|
173
173
|
"fileSizeTooLarge": "Le fichier est trop grand (max: {maxSizeMb}MB)"
|
|
174
174
|
},
|
|
175
|
+
"id": {
|
|
176
|
+
"edit": "Edit",
|
|
177
|
+
"fileSizeTooLarge": "File terlalu besar (maks.: {maxSizeMb}MB)"
|
|
178
|
+
},
|
|
175
179
|
"it": {
|
|
176
180
|
"edit": "Modifica",
|
|
177
181
|
"fileSizeTooLarge": "Il file è troppo grande (max: {maxSizeMb}MB)"
|
|
@@ -200,10 +204,6 @@ function onMouseLeave() {
|
|
|
200
204
|
"edit": "Editar",
|
|
201
205
|
"fileSizeTooLarge": "O arquivo é muito grande (máx.: {maxSizeMb}MB)"
|
|
202
206
|
},
|
|
203
|
-
"id": {
|
|
204
|
-
"edit": "Edit",
|
|
205
|
-
"fileSizeTooLarge": "File terlalu besar (maks.: {maxSizeMb}MB)"
|
|
206
|
-
},
|
|
207
207
|
"vi": {
|
|
208
208
|
"edit": "Chỉnh sửa",
|
|
209
209
|
"fileSizeTooLarge": "Tệp quá lớn (tối đa: {maxSizeMb}MB)"
|
|
@@ -150,6 +150,9 @@ function onClick(event: MouseEvent) {
|
|
|
150
150
|
"fr": {
|
|
151
151
|
"confirm": "Confirmer ?"
|
|
152
152
|
},
|
|
153
|
+
"id": {
|
|
154
|
+
"confirm": "Konfirmasi?"
|
|
155
|
+
},
|
|
153
156
|
"it": {
|
|
154
157
|
"confirm": "Confermare?"
|
|
155
158
|
},
|
|
@@ -171,9 +174,6 @@ function onClick(event: MouseEvent) {
|
|
|
171
174
|
"pt-BR": {
|
|
172
175
|
"confirm": "Confirmar?"
|
|
173
176
|
},
|
|
174
|
-
"id": {
|
|
175
|
-
"confirm": "Konfirmasi?"
|
|
176
|
-
},
|
|
177
177
|
"vi": {
|
|
178
178
|
"confirm": "Xác nhận?"
|
|
179
179
|
}
|
|
@@ -17,7 +17,6 @@ const props = withDefaults(defineProps<BaseCard>(), {
|
|
|
17
17
|
id: undefined,
|
|
18
18
|
image: '',
|
|
19
19
|
isSelected: false,
|
|
20
|
-
size: 'base',
|
|
21
20
|
title: undefined,
|
|
22
21
|
titleIcon: undefined,
|
|
23
22
|
to: undefined,
|
|
@@ -38,7 +37,10 @@ defineSlots<{
|
|
|
38
37
|
}>()
|
|
39
38
|
|
|
40
39
|
const { getIcon } = useLayerIcons()
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
const hasAvatarBox = computed<boolean>(() => {
|
|
42
|
+
return !!(props.avatar || props.emoji || props.icon || props.image)
|
|
43
|
+
})
|
|
42
44
|
|
|
43
45
|
const isClickable = computed(() => {
|
|
44
46
|
return props.to || props.clickable
|
|
@@ -57,8 +59,7 @@ function onClick(event: MouseEvent) {
|
|
|
57
59
|
'cursor-pointer': isClickable,
|
|
58
60
|
'border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm p-1.5 pr-2.5': hasBackground,
|
|
59
61
|
'hover:border-gray-300 dark:hover:border-gray-700': hasBackground && isClickable,
|
|
60
|
-
'rounded-
|
|
61
|
-
'rounded-xl': ['base', 'lg'].includes(size) && hasBackground,
|
|
62
|
+
'rounded-xl': hasBackground,
|
|
62
63
|
}"
|
|
63
64
|
:to="to"
|
|
64
65
|
@click="onClick"
|
|
@@ -75,15 +76,11 @@ function onClick(event: MouseEvent) {
|
|
|
75
76
|
|
|
76
77
|
<span
|
|
77
78
|
v-if="hasAvatarBox"
|
|
78
|
-
class="relative z-10 flex items-center justify-center flex-initial"
|
|
79
|
+
class="relative z-10 h-9 w-9 flex items-center justify-center flex-initial"
|
|
79
80
|
:class="{
|
|
80
81
|
'border shadow-inner': !avatar && !image,
|
|
81
|
-
'rounded-
|
|
82
|
-
'rounded-lg': !avatar && !image && ['base', 'lg'].includes(size),
|
|
82
|
+
'rounded-lg': !avatar && !image,
|
|
83
83
|
'border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-900': !avatar && !image,
|
|
84
|
-
'h-8 w-8': size === 'sm' && !avatar && !image,
|
|
85
|
-
'h-9 w-9': size === 'base' && !avatar && !image,
|
|
86
|
-
'h-10 w-10': size === 'lg' && !avatar && !image,
|
|
87
84
|
'mr-2': direction === 'horizontal',
|
|
88
85
|
'mb-1': direction === 'vertical',
|
|
89
86
|
}"
|
|
@@ -92,12 +89,7 @@ function onClick(event: MouseEvent) {
|
|
|
92
89
|
|
|
93
90
|
<BaseAvatar
|
|
94
91
|
v-if="avatar"
|
|
95
|
-
class="rounded-lg flex-initial"
|
|
96
|
-
:class="{
|
|
97
|
-
'h-8 w-8': size === 'sm',
|
|
98
|
-
'h-10 w-10': size === 'base',
|
|
99
|
-
'h-14 w-14': size === 'lg',
|
|
100
|
-
}"
|
|
92
|
+
class="h-10 w-10 rounded-lg flex-initial"
|
|
101
93
|
:src="avatar"
|
|
102
94
|
/>
|
|
103
95
|
|
|
@@ -117,12 +109,7 @@ function onClick(event: MouseEvent) {
|
|
|
117
109
|
|
|
118
110
|
<img
|
|
119
111
|
v-else-if="image"
|
|
120
|
-
class="
|
|
121
|
-
:class="{
|
|
122
|
-
'h-7 w-7 rounded-md': size === 'sm',
|
|
123
|
-
'h-9 w-9 rounded-lg': size === 'base',
|
|
124
|
-
'h-10 w-10 rounded-lg': size === 'lg',
|
|
125
|
-
}"
|
|
112
|
+
class="h-9 w-9 rounded-lg object-cover shadow-sm drag-none flex-initial"
|
|
126
113
|
:alt="title"
|
|
127
114
|
loading="lazy"
|
|
128
115
|
:src="image"
|
|
@@ -135,25 +122,19 @@ function onClick(event: MouseEvent) {
|
|
|
135
122
|
</span>
|
|
136
123
|
|
|
137
124
|
<div
|
|
138
|
-
class="
|
|
125
|
+
class="min-w-0 flex flex-1 flex-col justify-center leading-snug"
|
|
139
126
|
:class="{
|
|
140
127
|
'mr-4': direction === 'horizontal',
|
|
141
128
|
'items-center': direction === 'vertical',
|
|
142
129
|
}"
|
|
143
130
|
>
|
|
144
|
-
<div
|
|
145
|
-
class="flex w-full flex-col"
|
|
146
|
-
:class="{
|
|
147
|
-
'gap-0': ['sm', 'base'].includes(size),
|
|
148
|
-
'gap-0.25': size === 'lg',
|
|
149
|
-
}"
|
|
150
|
-
>
|
|
131
|
+
<div class="w-full flex flex-col gap-0">
|
|
151
132
|
<BaseIcon
|
|
152
133
|
v-if="title"
|
|
153
134
|
class="w-full"
|
|
154
135
|
:class="{ 'self-center': !hasAvatarBox }"
|
|
155
136
|
:icon="titleIcon"
|
|
156
|
-
|
|
137
|
+
size="base"
|
|
157
138
|
:text="title"
|
|
158
139
|
truncate
|
|
159
140
|
/>
|
|
@@ -162,7 +143,7 @@ function onClick(event: MouseEvent) {
|
|
|
162
143
|
v-if="description"
|
|
163
144
|
block
|
|
164
145
|
class="text-gray-600 leading-4 dark:text-gray-400"
|
|
165
|
-
|
|
146
|
+
size="2xs"
|
|
166
147
|
:text="description"
|
|
167
148
|
/>
|
|
168
149
|
</div>
|
|
@@ -132,6 +132,11 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
|
|
|
132
132
|
"next": "Suivant",
|
|
133
133
|
"previous": "Précédent"
|
|
134
134
|
},
|
|
135
|
+
"id": {
|
|
136
|
+
"loading": "Memuat…",
|
|
137
|
+
"next": "Berikutnya",
|
|
138
|
+
"previous": "Sebelumnya"
|
|
139
|
+
},
|
|
135
140
|
"it": {
|
|
136
141
|
"loading": "Caricamento…",
|
|
137
142
|
"next": "Successivo",
|
|
@@ -167,11 +172,6 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
|
|
|
167
172
|
"next": "Próximo",
|
|
168
173
|
"previous": "Anterior"
|
|
169
174
|
},
|
|
170
|
-
"id": {
|
|
171
|
-
"loading": "Memuat…",
|
|
172
|
-
"next": "Berikutnya",
|
|
173
|
-
"previous": "Sebelumnya"
|
|
174
|
-
},
|
|
175
175
|
"vi": {
|
|
176
176
|
"loading": "Đang tải…",
|
|
177
177
|
"next": "Sau",
|
|
@@ -173,6 +173,9 @@ function onKeyDown(event: KeyboardEvent) {
|
|
|
173
173
|
"fr": {
|
|
174
174
|
"confirm": "Confirmer ?"
|
|
175
175
|
},
|
|
176
|
+
"id": {
|
|
177
|
+
"confirm": "Konfirmasi?"
|
|
178
|
+
},
|
|
176
179
|
"it": {
|
|
177
180
|
"confirm": "Confermare?"
|
|
178
181
|
},
|
|
@@ -194,9 +197,6 @@ function onKeyDown(event: KeyboardEvent) {
|
|
|
194
197
|
"pt-BR": {
|
|
195
198
|
"confirm": "Confirmar?"
|
|
196
199
|
},
|
|
197
|
-
"id": {
|
|
198
|
-
"confirm": "Konfirmasi?"
|
|
199
|
-
},
|
|
200
200
|
"vi": {
|
|
201
201
|
"confirm": "Xác nhận?"
|
|
202
202
|
}
|
|
@@ -170,6 +170,9 @@ function onClose(event: MouseEvent) {
|
|
|
170
170
|
"fr": {
|
|
171
171
|
"close": "Fermer"
|
|
172
172
|
},
|
|
173
|
+
"id": {
|
|
174
|
+
"close": "Tutup"
|
|
175
|
+
},
|
|
173
176
|
"it": {
|
|
174
177
|
"close": "Chiudi"
|
|
175
178
|
},
|
|
@@ -191,9 +194,6 @@ function onClose(event: MouseEvent) {
|
|
|
191
194
|
"pt-BR": {
|
|
192
195
|
"close": "Fechar"
|
|
193
196
|
},
|
|
194
|
-
"id": {
|
|
195
|
-
"close": "Tutup"
|
|
196
|
-
},
|
|
197
197
|
"vi": {
|
|
198
198
|
"close": "Đóng"
|
|
199
199
|
}
|
|
@@ -241,6 +241,11 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
|
|
|
241
241
|
"newTag": "Nouveau tag",
|
|
242
242
|
"showMore": "& {value} autre | & {value} autres"
|
|
243
243
|
},
|
|
244
|
+
"id": {
|
|
245
|
+
"cancel": "Batal",
|
|
246
|
+
"newTag": "Tag baru",
|
|
247
|
+
"showMore": "& {value} lainnya | & {value} lainnya"
|
|
248
|
+
},
|
|
244
249
|
"it": {
|
|
245
250
|
"cancel": "Annulla",
|
|
246
251
|
"newTag": "Nuovo tag",
|
|
@@ -276,11 +281,6 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
|
|
|
276
281
|
"newTag": "Nova tag",
|
|
277
282
|
"showMore": "& {value} outro | & {value} outros"
|
|
278
283
|
},
|
|
279
|
-
"id": {
|
|
280
|
-
"cancel": "Batal",
|
|
281
|
-
"newTag": "Tag baru",
|
|
282
|
-
"showMore": "& {value} lainnya | & {value} lainnya"
|
|
283
|
-
},
|
|
284
284
|
"vi": {
|
|
285
285
|
"cancel": "Hủy",
|
|
286
286
|
"newTag": "Thẻ mới",
|
|
@@ -155,6 +155,9 @@ function onShowMore() {
|
|
|
155
155
|
"fr": {
|
|
156
156
|
"showMore": "(afficher)"
|
|
157
157
|
},
|
|
158
|
+
"id": {
|
|
159
|
+
"showMore": "(tampilkan lebih banyak)"
|
|
160
|
+
},
|
|
158
161
|
"it": {
|
|
159
162
|
"showMore": "(mostra altro)"
|
|
160
163
|
},
|
|
@@ -176,9 +179,6 @@ function onShowMore() {
|
|
|
176
179
|
"pt-BR": {
|
|
177
180
|
"showMore": "(mostrar mais)"
|
|
178
181
|
},
|
|
179
|
-
"id": {
|
|
180
|
-
"showMore": "(tampilkan lebih banyak)"
|
|
181
|
-
},
|
|
182
182
|
"vi": {
|
|
183
183
|
"showMore": "(xem thêm)"
|
|
184
184
|
}
|
|
@@ -68,13 +68,13 @@ function onOpenFilePicker() {
|
|
|
68
68
|
:for-field="id"
|
|
69
69
|
has-margin-bottom
|
|
70
70
|
:icon="labelIcon"
|
|
71
|
-
:label="
|
|
71
|
+
:label="label || t('label')"
|
|
72
72
|
:required="required"
|
|
73
73
|
:size="size"
|
|
74
74
|
/>
|
|
75
75
|
|
|
76
76
|
<div
|
|
77
|
-
:aria-label="loading ? t('actionLoading') : (typeof
|
|
77
|
+
:aria-label="loading ? t('actionLoading') : (typeof action === 'string' ? action : (action?.base || t('action')))"
|
|
78
78
|
class="w-full flex items-center gap-2 px-3 normal-case outline-none"
|
|
79
79
|
:class="{
|
|
80
80
|
'cursor-pointer': !disabled && !loading,
|
|
@@ -131,7 +131,7 @@ function onOpenFilePicker() {
|
|
|
131
131
|
|
|
132
132
|
<BaseText
|
|
133
133
|
class="min-w-0 flex-1 text-gray-900 dark:text-gray-100"
|
|
134
|
-
:text="loading ? t('actionLoading') : (
|
|
134
|
+
:text="loading ? t('actionLoading') : (action || t('action'))"
|
|
135
135
|
/>
|
|
136
136
|
</div>
|
|
137
137
|
|
|
@@ -159,81 +159,81 @@ function onOpenFilePicker() {
|
|
|
159
159
|
<i18n lang="json">
|
|
160
160
|
{
|
|
161
161
|
"de": {
|
|
162
|
-
"fileSizeTooLarge": "Die Datei ist zu groß (max.: {maxSizeMb}MB)",
|
|
163
162
|
"action": "Foto ändern",
|
|
164
163
|
"actionLoading": "Foto wird aktualisiert...",
|
|
164
|
+
"fileSizeTooLarge": "Die Datei ist zu groß (max.: {maxSizeMb}MB)",
|
|
165
165
|
"label": "Profilbild"
|
|
166
166
|
},
|
|
167
167
|
"en": {
|
|
168
|
-
"fileSizeTooLarge": "The file is too large (max: {maxSizeMb}MB)",
|
|
169
168
|
"action": "Change photo",
|
|
170
169
|
"actionLoading": "Updating photo...",
|
|
170
|
+
"fileSizeTooLarge": "The file is too large (max: {maxSizeMb}MB)",
|
|
171
171
|
"label": "Profile picture"
|
|
172
172
|
},
|
|
173
173
|
"es": {
|
|
174
|
-
"fileSizeTooLarge": "El archivo es demasiado grande (máx.: {maxSizeMb}MB)",
|
|
175
174
|
"action": "Cambiar foto",
|
|
176
175
|
"actionLoading": "Actualizando foto...",
|
|
176
|
+
"fileSizeTooLarge": "El archivo es demasiado grande (máx.: {maxSizeMb}MB)",
|
|
177
177
|
"label": "Foto de perfil"
|
|
178
178
|
},
|
|
179
179
|
"fr": {
|
|
180
|
-
"fileSizeTooLarge": "Le fichier est trop grand (max: {maxSizeMb}MB)",
|
|
181
180
|
"action": "Changer la photo",
|
|
182
181
|
"actionLoading": "Mise a jour de la photo...",
|
|
182
|
+
"fileSizeTooLarge": "Le fichier est trop grand (max: {maxSizeMb}MB)",
|
|
183
183
|
"label": "Photo de profil"
|
|
184
184
|
},
|
|
185
|
+
"id": {
|
|
186
|
+
"action": "Ganti foto",
|
|
187
|
+
"actionLoading": "Memperbarui foto...",
|
|
188
|
+
"fileSizeTooLarge": "File terlalu besar (maks.: {maxSizeMb}MB)",
|
|
189
|
+
"label": "Foto profil"
|
|
190
|
+
},
|
|
185
191
|
"it": {
|
|
186
|
-
"fileSizeTooLarge": "Il file è troppo grande (max: {maxSizeMb}MB)",
|
|
187
192
|
"action": "Cambia foto",
|
|
188
193
|
"actionLoading": "Aggiornamento foto...",
|
|
194
|
+
"fileSizeTooLarge": "Il file è troppo grande (max: {maxSizeMb}MB)",
|
|
189
195
|
"label": "Foto del profilo"
|
|
190
196
|
},
|
|
191
197
|
"ja": {
|
|
192
|
-
"fileSizeTooLarge": "ファイルが大きすぎます (最大: {maxSizeMb}MB)",
|
|
193
198
|
"action": "写真を変更",
|
|
194
199
|
"actionLoading": "写真を更新中...",
|
|
200
|
+
"fileSizeTooLarge": "ファイルが大きすぎます (最大: {maxSizeMb}MB)",
|
|
195
201
|
"label": "プロフィール写真"
|
|
196
202
|
},
|
|
197
203
|
"ko": {
|
|
198
|
-
"fileSizeTooLarge": "파일이 너무 큽니다 (최대: {maxSizeMb}MB)",
|
|
199
204
|
"action": "사진 변경",
|
|
200
205
|
"actionLoading": "사진 업데이트 중...",
|
|
206
|
+
"fileSizeTooLarge": "파일이 너무 큽니다 (최대: {maxSizeMb}MB)",
|
|
201
207
|
"label": "프로필 사진"
|
|
202
208
|
},
|
|
203
209
|
"nl": {
|
|
204
|
-
"fileSizeTooLarge": "Het bestand is te groot (max.: {maxSizeMb}MB)",
|
|
205
210
|
"action": "Foto wijzigen",
|
|
206
211
|
"actionLoading": "Foto wordt bijgewerkt...",
|
|
212
|
+
"fileSizeTooLarge": "Het bestand is te groot (max.: {maxSizeMb}MB)",
|
|
207
213
|
"label": "Profielfoto"
|
|
208
214
|
},
|
|
209
215
|
"pl": {
|
|
210
|
-
"fileSizeTooLarge": "Plik jest za duży (maks.: {maxSizeMb}MB)",
|
|
211
216
|
"action": "Zmień zdjęcie",
|
|
212
217
|
"actionLoading": "Aktualizowanie zdjęcia...",
|
|
218
|
+
"fileSizeTooLarge": "Plik jest za duży (maks.: {maxSizeMb}MB)",
|
|
213
219
|
"label": "Zdjęcie profilowe"
|
|
214
220
|
},
|
|
215
221
|
"pt": {
|
|
216
|
-
"fileSizeTooLarge": "O ficheiro é demasiado grande (máx.: {maxSizeMb}MB)",
|
|
217
222
|
"action": "Alterar foto",
|
|
218
223
|
"actionLoading": "A atualizar foto...",
|
|
224
|
+
"fileSizeTooLarge": "O ficheiro é demasiado grande (máx.: {maxSizeMb}MB)",
|
|
219
225
|
"label": "Foto de perfil"
|
|
220
226
|
},
|
|
221
227
|
"pt-BR": {
|
|
222
|
-
"fileSizeTooLarge": "O arquivo é muito grande (máx.: {maxSizeMb}MB)",
|
|
223
228
|
"action": "Alterar foto",
|
|
224
229
|
"actionLoading": "Atualizando foto...",
|
|
230
|
+
"fileSizeTooLarge": "O arquivo é muito grande (máx.: {maxSizeMb}MB)",
|
|
225
231
|
"label": "Foto de perfil"
|
|
226
232
|
},
|
|
227
|
-
"id": {
|
|
228
|
-
"fileSizeTooLarge": "File terlalu besar (maks.: {maxSizeMb}MB)",
|
|
229
|
-
"action": "Ganti foto",
|
|
230
|
-
"actionLoading": "Memperbarui foto...",
|
|
231
|
-
"label": "Foto profil"
|
|
232
|
-
},
|
|
233
233
|
"vi": {
|
|
234
|
-
"fileSizeTooLarge": "Tệp quá lớn (tối đa: {maxSizeMb}MB)",
|
|
235
234
|
"action": "Đổi ảnh",
|
|
236
235
|
"actionLoading": "Dang cap nhat anh...",
|
|
236
|
+
"fileSizeTooLarge": "Tệp quá lớn (tối đa: {maxSizeMb}MB)",
|
|
237
237
|
"label": "Ảnh đại diện"
|
|
238
238
|
}
|
|
239
239
|
}
|
|
@@ -125,6 +125,17 @@ function onUpdateDays(event: MouseEvent, day: number) {
|
|
|
125
125
|
"wednesday": "Mercredi"
|
|
126
126
|
}
|
|
127
127
|
},
|
|
128
|
+
"id": {
|
|
129
|
+
"days": {
|
|
130
|
+
"friday": "Jumat",
|
|
131
|
+
"monday": "Senin",
|
|
132
|
+
"saturday": "Sabtu",
|
|
133
|
+
"sunday": "Minggu",
|
|
134
|
+
"thursday": "Kamis",
|
|
135
|
+
"tuesday": "Selasa",
|
|
136
|
+
"wednesday": "Rabu"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
128
139
|
"it": {
|
|
129
140
|
"days": {
|
|
130
141
|
"friday": "Venerdì",
|
|
@@ -202,17 +213,6 @@ function onUpdateDays(event: MouseEvent, day: number) {
|
|
|
202
213
|
"wednesday": "Quarta-feira"
|
|
203
214
|
}
|
|
204
215
|
},
|
|
205
|
-
"id": {
|
|
206
|
-
"days": {
|
|
207
|
-
"friday": "Jumat",
|
|
208
|
-
"monday": "Senin",
|
|
209
|
-
"saturday": "Sabtu",
|
|
210
|
-
"sunday": "Minggu",
|
|
211
|
-
"thursday": "Kamis",
|
|
212
|
-
"tuesday": "Selasa",
|
|
213
|
-
"wednesday": "Rabu"
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
216
|
"vi": {
|
|
217
217
|
"days": {
|
|
218
218
|
"friday": "Thứ Sáu",
|
|
@@ -163,6 +163,21 @@ function onEmojiClick(event: MouseEvent, emoji?: string) {
|
|
|
163
163
|
},
|
|
164
164
|
"searchEmoji": "Rechercher un emoji (en anglais)"
|
|
165
165
|
},
|
|
166
|
+
"id": {
|
|
167
|
+
"categories": {
|
|
168
|
+
"activity": "Aktivitas",
|
|
169
|
+
"diversity": "Warna kulit",
|
|
170
|
+
"flags": "Bendera",
|
|
171
|
+
"food": "Makanan & minuman",
|
|
172
|
+
"nature": "Hewan & alam",
|
|
173
|
+
"objects": "Benda",
|
|
174
|
+
"people": "Smiley & orang",
|
|
175
|
+
"regional": "Regional",
|
|
176
|
+
"symbol": "Simbol",
|
|
177
|
+
"travel": "Perjalanan & tempat"
|
|
178
|
+
},
|
|
179
|
+
"searchEmoji": "Cari emoji"
|
|
180
|
+
},
|
|
166
181
|
"it": {
|
|
167
182
|
"categories": {
|
|
168
183
|
"activity": "Attività",
|
|
@@ -268,21 +283,6 @@ function onEmojiClick(event: MouseEvent, emoji?: string) {
|
|
|
268
283
|
},
|
|
269
284
|
"searchEmoji": "Pesquisar um emoji"
|
|
270
285
|
},
|
|
271
|
-
"id": {
|
|
272
|
-
"categories": {
|
|
273
|
-
"activity": "Aktivitas",
|
|
274
|
-
"diversity": "Warna kulit",
|
|
275
|
-
"flags": "Bendera",
|
|
276
|
-
"food": "Makanan & minuman",
|
|
277
|
-
"nature": "Hewan & alam",
|
|
278
|
-
"objects": "Benda",
|
|
279
|
-
"people": "Smiley & orang",
|
|
280
|
-
"regional": "Regional",
|
|
281
|
-
"symbol": "Simbol",
|
|
282
|
-
"travel": "Perjalanan & tempat"
|
|
283
|
-
},
|
|
284
|
-
"searchEmoji": "Cari emoji"
|
|
285
|
-
},
|
|
286
286
|
"vi": {
|
|
287
287
|
"categories": {
|
|
288
288
|
"activity": "Hoạt động",
|
|
@@ -217,6 +217,25 @@ function ruleIsInvalid(rule: unknown) {
|
|
|
217
217
|
"sameAs": "La valeur ne correspond pas à: {field}",
|
|
218
218
|
"url": "La valeur n'est pas une URL valide"
|
|
219
219
|
},
|
|
220
|
+
"id": {
|
|
221
|
+
"alpha": "Nilai hanya menerima huruf",
|
|
222
|
+
"alphaNum": "Nilai hanya menerima karakter alfanumerik",
|
|
223
|
+
"between": "Nilai harus antara {min} dan {max}",
|
|
224
|
+
"decimal": "Nilai hanya menerima bilangan desimal positif dan negatif",
|
|
225
|
+
"email": "Nilai bukan email yang valid",
|
|
226
|
+
"integer": "Nilai hanya menerima bilangan bulat positif dan negatif",
|
|
227
|
+
"invalid": "Nilai tidak valid",
|
|
228
|
+
"ipAddress": "Nilai hanya menerima alamat IPv4 yang valid",
|
|
229
|
+
"macAddress": "Nilai hanya menerima alamat MAC yang valid",
|
|
230
|
+
"maxLength": "Nilai terlalu panjang (maks.: {max})",
|
|
231
|
+
"maxValue": "Nilai maksimum yang diizinkan: {max}",
|
|
232
|
+
"minLength": "Nilai terlalu pendek (min.: {min})",
|
|
233
|
+
"minValue": "Nilai minimum yang diizinkan: {min}",
|
|
234
|
+
"numeric": "Nilai hanya menerima angka",
|
|
235
|
+
"required": "Nilai wajib diisi",
|
|
236
|
+
"sameAs": "Nilai tidak cocok dengan: {field}",
|
|
237
|
+
"url": "Nilai bukan URL yang valid"
|
|
238
|
+
},
|
|
220
239
|
"it": {
|
|
221
240
|
"alpha": "Il valore accetta solo caratteri alfabetici",
|
|
222
241
|
"alphaNum": "Il valore accetta solo caratteri alfanumerici",
|
|
@@ -350,25 +369,6 @@ function ruleIsInvalid(rule: unknown) {
|
|
|
350
369
|
"sameAs": "O valor não corresponde a: {field}",
|
|
351
370
|
"url": "O valor não é uma URL válida"
|
|
352
371
|
},
|
|
353
|
-
"id": {
|
|
354
|
-
"alpha": "Nilai hanya menerima huruf",
|
|
355
|
-
"alphaNum": "Nilai hanya menerima karakter alfanumerik",
|
|
356
|
-
"between": "Nilai harus antara {min} dan {max}",
|
|
357
|
-
"decimal": "Nilai hanya menerima bilangan desimal positif dan negatif",
|
|
358
|
-
"email": "Nilai bukan email yang valid",
|
|
359
|
-
"integer": "Nilai hanya menerima bilangan bulat positif dan negatif",
|
|
360
|
-
"invalid": "Nilai tidak valid",
|
|
361
|
-
"ipAddress": "Nilai hanya menerima alamat IPv4 yang valid",
|
|
362
|
-
"macAddress": "Nilai hanya menerima alamat MAC yang valid",
|
|
363
|
-
"maxLength": "Nilai terlalu panjang (maks.: {max})",
|
|
364
|
-
"maxValue": "Nilai maksimum yang diizinkan: {max}",
|
|
365
|
-
"minLength": "Nilai terlalu pendek (min.: {min})",
|
|
366
|
-
"minValue": "Nilai minimum yang diizinkan: {min}",
|
|
367
|
-
"numeric": "Nilai hanya menerima angka",
|
|
368
|
-
"required": "Nilai wajib diisi",
|
|
369
|
-
"sameAs": "Nilai tidak cocok dengan: {field}",
|
|
370
|
-
"url": "Nilai bukan URL yang valid"
|
|
371
|
-
},
|
|
372
372
|
"vi": {
|
|
373
373
|
"alpha": "Giá trị chỉ chấp nhận chữ cái",
|
|
374
374
|
"alphaNum": "Giá trị chỉ chấp nhận ký tự chữ và số",
|
|
@@ -396,6 +396,10 @@ function selectOption(event: MouseEvent, value: string) {
|
|
|
396
396
|
"noResults": "Aucun résultat",
|
|
397
397
|
"search": "Rechercher"
|
|
398
398
|
},
|
|
399
|
+
"id": {
|
|
400
|
+
"noResults": "Tidak ada hasil",
|
|
401
|
+
"search": "Cari"
|
|
402
|
+
},
|
|
399
403
|
"it": {
|
|
400
404
|
"noResults": "Nessun risultato",
|
|
401
405
|
"search": "Cerca"
|
|
@@ -424,10 +428,6 @@ function selectOption(event: MouseEvent, value: string) {
|
|
|
424
428
|
"noResults": "Nenhum resultado",
|
|
425
429
|
"search": "Pesquisar"
|
|
426
430
|
},
|
|
427
|
-
"id": {
|
|
428
|
-
"noResults": "Tidak ada hasil",
|
|
429
|
-
"search": "Cari"
|
|
430
|
-
},
|
|
431
431
|
"vi": {
|
|
432
432
|
"noResults": "Không có kết quả",
|
|
433
433
|
"search": "Tìm kiếm"
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { LayoutBottomSheet } from '../../types/layout'
|
|
3
|
+
|
|
4
|
+
withDefaults(defineProps<LayoutBottomSheet>(), { title: undefined })
|
|
5
|
+
|
|
6
|
+
defineSlots<{
|
|
7
|
+
default?: () => VNode[]
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const closeThresholdRatio = 0.25
|
|
11
|
+
const dragMoveThreshold = 8
|
|
12
|
+
const focusableSelector = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
13
|
+
const visible = defineModel<boolean>({ default: false })
|
|
14
|
+
const contentRef = ref<HTMLElement>()
|
|
15
|
+
const focusTrigger = ref<HTMLElement>()
|
|
16
|
+
const panelRef = ref<HTMLElement>()
|
|
17
|
+
|
|
18
|
+
const drag = reactive({
|
|
19
|
+
closing: false,
|
|
20
|
+
isDragging: false,
|
|
21
|
+
offset: 0,
|
|
22
|
+
pointerId: undefined as number | undefined,
|
|
23
|
+
startedFromContent: false,
|
|
24
|
+
startY: 0,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
let savedBodyOverflow = ''
|
|
28
|
+
let savedBodyPaddingRight = ''
|
|
29
|
+
|
|
30
|
+
const fixedElementPadding = new Map<HTMLElement, string>()
|
|
31
|
+
|
|
32
|
+
const closeThreshold = computed(() => {
|
|
33
|
+
return (panelRef.value?.offsetHeight ?? 320) * closeThresholdRatio
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function getFocusableElements(container: HTMLElement) {
|
|
37
|
+
return Array.from(container.querySelectorAll<HTMLElement>(focusableSelector))
|
|
38
|
+
.filter((element) => {
|
|
39
|
+
return element.offsetParent !== null || getComputedStyle(element).position === 'fixed'
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function lockBodyScroll() {
|
|
44
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
|
|
45
|
+
|
|
46
|
+
savedBodyOverflow = document.body.style.overflow
|
|
47
|
+
savedBodyPaddingRight = document.body.style.paddingRight
|
|
48
|
+
document.body.style.overflow = 'hidden'
|
|
49
|
+
|
|
50
|
+
if (scrollbarWidth > 0) {
|
|
51
|
+
document.body.style.paddingRight = `${scrollbarWidth}px`
|
|
52
|
+
|
|
53
|
+
for (const element of document.querySelectorAll('body *')) {
|
|
54
|
+
if (!(element instanceof HTMLElement)) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (element.classList.contains('layout-bottom-sheet')) {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { position } = getComputedStyle(element)
|
|
63
|
+
|
|
64
|
+
if (position !== 'fixed' && position !== 'sticky') {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fixedElementPadding.set(element, element.style.paddingRight)
|
|
69
|
+
|
|
70
|
+
const currentPadding = Number.parseFloat(getComputedStyle(element).paddingRight) || 0
|
|
71
|
+
|
|
72
|
+
element.style.paddingRight = `${currentPadding + scrollbarWidth}px`
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function onClose() {
|
|
78
|
+
visible.value = false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function onContentPointerDown(event: PointerEvent) {
|
|
82
|
+
if (drag.closing) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if ((contentRef.value?.scrollTop ?? 0) > 0) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
drag.startedFromContent = true
|
|
91
|
+
drag.pointerId = event.pointerId
|
|
92
|
+
drag.startY = event.clientY
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function onFocusTrap(event: KeyboardEvent) {
|
|
96
|
+
if (event.key !== 'Tab' || !panelRef.value) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const focusableElements = getFocusableElements(panelRef.value)
|
|
101
|
+
|
|
102
|
+
if (focusableElements.length === 0) {
|
|
103
|
+
event.preventDefault()
|
|
104
|
+
panelRef.value.focus()
|
|
105
|
+
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const firstElement = focusableElements[0]
|
|
110
|
+
const lastElement = focusableElements[focusableElements.length - 1]
|
|
111
|
+
const activeElement = document.activeElement
|
|
112
|
+
|
|
113
|
+
if (event.shiftKey && activeElement === firstElement) {
|
|
114
|
+
event.preventDefault()
|
|
115
|
+
lastElement.focus()
|
|
116
|
+
}
|
|
117
|
+
else if (!event.shiftKey && activeElement === lastElement) {
|
|
118
|
+
event.preventDefault()
|
|
119
|
+
firstElement.focus()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function onHeaderPointerDown(event: PointerEvent) {
|
|
124
|
+
if (drag.closing) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
drag.startedFromContent = false
|
|
129
|
+
drag.isDragging = true
|
|
130
|
+
drag.pointerId = event.pointerId
|
|
131
|
+
drag.startY = event.clientY
|
|
132
|
+
drag.offset = 0
|
|
133
|
+
|
|
134
|
+
const handle = event.currentTarget as HTMLElement
|
|
135
|
+
|
|
136
|
+
handle.setPointerCapture(event.pointerId)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function onKeydown(event: KeyboardEvent) {
|
|
140
|
+
if (!visible.value) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (event.key === 'Escape') {
|
|
145
|
+
onClose()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function onPanelAfterLeave() {
|
|
150
|
+
if (import.meta.client) {
|
|
151
|
+
unlockBodyScroll()
|
|
152
|
+
window.removeEventListener('keydown', onFocusTrap)
|
|
153
|
+
focusTrigger.value?.focus()
|
|
154
|
+
|
|
155
|
+
focusTrigger.value = undefined
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function onPanelTransitionEnd(event: TransitionEvent) {
|
|
160
|
+
if (event.propertyName !== 'transform' || !drag.closing) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
onClose()
|
|
165
|
+
resetDragState()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function onPointerMove(event: PointerEvent) {
|
|
169
|
+
if (drag.startedFromContent && !drag.isDragging) {
|
|
170
|
+
if (event.pointerId !== drag.pointerId) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if ((contentRef.value?.scrollTop ?? 0) > 0) {
|
|
175
|
+
resetPendingDrag()
|
|
176
|
+
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const offset = event.clientY - drag.startY
|
|
181
|
+
|
|
182
|
+
if (offset < 0) {
|
|
183
|
+
resetPendingDrag()
|
|
184
|
+
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (offset < dragMoveThreshold) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
drag.isDragging = true
|
|
193
|
+
drag.offset = offset
|
|
194
|
+
|
|
195
|
+
const handle = event.currentTarget as HTMLElement
|
|
196
|
+
|
|
197
|
+
handle.setPointerCapture(event.pointerId)
|
|
198
|
+
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!drag.isDragging) {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const offset = event.clientY - drag.startY
|
|
207
|
+
|
|
208
|
+
if (drag.startedFromContent && offset < 0) {
|
|
209
|
+
drag.isDragging = false
|
|
210
|
+
|
|
211
|
+
resetPendingDrag()
|
|
212
|
+
|
|
213
|
+
drag.offset = 0
|
|
214
|
+
|
|
215
|
+
const handle = event.currentTarget as HTMLElement
|
|
216
|
+
|
|
217
|
+
if (handle.hasPointerCapture(event.pointerId)) {
|
|
218
|
+
handle.releasePointerCapture(event.pointerId)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
drag.offset = Math.max(0, offset)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function onPointerUp(event: PointerEvent) {
|
|
228
|
+
if (!drag.isDragging) {
|
|
229
|
+
resetPendingDrag()
|
|
230
|
+
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
drag.isDragging = false
|
|
235
|
+
|
|
236
|
+
resetPendingDrag()
|
|
237
|
+
|
|
238
|
+
const handle = event.currentTarget as HTMLElement
|
|
239
|
+
|
|
240
|
+
handle.releasePointerCapture(event.pointerId)
|
|
241
|
+
|
|
242
|
+
if (drag.offset >= closeThreshold.value) {
|
|
243
|
+
drag.closing = true
|
|
244
|
+
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
drag.offset = 0
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function resetDragState() {
|
|
252
|
+
drag.closing = false
|
|
253
|
+
drag.isDragging = false
|
|
254
|
+
drag.offset = 0
|
|
255
|
+
drag.pointerId = undefined
|
|
256
|
+
drag.startY = 0
|
|
257
|
+
drag.startedFromContent = false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function resetPendingDrag() {
|
|
261
|
+
drag.pointerId = undefined
|
|
262
|
+
drag.startedFromContent = false
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function unlockBodyScroll() {
|
|
266
|
+
document.body.style.overflow = savedBodyOverflow
|
|
267
|
+
document.body.style.paddingRight = savedBodyPaddingRight
|
|
268
|
+
|
|
269
|
+
for (const [element, paddingRight] of fixedElementPadding) {
|
|
270
|
+
element.style.paddingRight = paddingRight
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fixedElementPadding.clear()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
watch(visible, async (isVisible) => {
|
|
277
|
+
if (import.meta.client && isVisible) {
|
|
278
|
+
focusTrigger.value = document.activeElement instanceof HTMLElement ? document.activeElement : undefined
|
|
279
|
+
|
|
280
|
+
lockBodyScroll()
|
|
281
|
+
|
|
282
|
+
await nextTick()
|
|
283
|
+
|
|
284
|
+
if (panelRef.value) {
|
|
285
|
+
const focusableElements = getFocusableElements(panelRef.value)
|
|
286
|
+
|
|
287
|
+
if (focusableElements.length > 0) {
|
|
288
|
+
focusableElements[0].focus()
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
panelRef.value.focus()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
window.addEventListener('keydown', onFocusTrap)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!isVisible) {
|
|
299
|
+
resetDragState()
|
|
300
|
+
}
|
|
301
|
+
}, { immediate: true })
|
|
302
|
+
|
|
303
|
+
onMounted(() => window.addEventListener('keydown', onKeydown))
|
|
304
|
+
|
|
305
|
+
onUnmounted(() => {
|
|
306
|
+
window.removeEventListener('keydown', onKeydown)
|
|
307
|
+
window.removeEventListener('keydown', onFocusTrap)
|
|
308
|
+
|
|
309
|
+
if (import.meta.client) {
|
|
310
|
+
unlockBodyScroll()
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
</script>
|
|
314
|
+
|
|
315
|
+
<template>
|
|
316
|
+
<ClientOnly>
|
|
317
|
+
<Teleport to="body">
|
|
318
|
+
<Transition
|
|
319
|
+
enter-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
|
|
320
|
+
enter-from-class="opacity-0 motion-reduce:opacity-100"
|
|
321
|
+
enter-to-class="opacity-100"
|
|
322
|
+
leave-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
|
|
323
|
+
leave-from-class="opacity-100"
|
|
324
|
+
leave-to-class="opacity-0 motion-reduce:opacity-100"
|
|
325
|
+
>
|
|
326
|
+
<div
|
|
327
|
+
v-if="visible"
|
|
328
|
+
aria-hidden="true"
|
|
329
|
+
class="layout-bottom-sheet fixed inset-0 z-[60] bg-black/50"
|
|
330
|
+
:class="{
|
|
331
|
+
'transition-none motion-reduce:transition-none': drag.isDragging,
|
|
332
|
+
'transition-opacity duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging && (drag.closing || drag.offset > 0),
|
|
333
|
+
}"
|
|
334
|
+
:style="{
|
|
335
|
+
opacity: drag.closing ? 0 : drag.offset > 0 ? Math.max(0, 1 - drag.offset / (panelRef?.offsetHeight ?? 320)) : undefined,
|
|
336
|
+
}"
|
|
337
|
+
@click="onClose"
|
|
338
|
+
/>
|
|
339
|
+
</Transition>
|
|
340
|
+
|
|
341
|
+
<Transition
|
|
342
|
+
enter-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
|
|
343
|
+
enter-from-class="translate-y-full motion-reduce:translate-y-0"
|
|
344
|
+
enter-to-class="translate-y-0"
|
|
345
|
+
leave-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
|
|
346
|
+
leave-from-class="translate-y-0"
|
|
347
|
+
leave-to-class="translate-y-full motion-reduce:translate-y-0"
|
|
348
|
+
@after-leave="onPanelAfterLeave"
|
|
349
|
+
>
|
|
350
|
+
<div
|
|
351
|
+
v-if="visible"
|
|
352
|
+
ref="panelRef"
|
|
353
|
+
:aria-label="title"
|
|
354
|
+
aria-modal="true"
|
|
355
|
+
class="layout-bottom-sheet fixed bottom-0 left-0 right-0 z-[60] mx-auto max-h-[85dvh] max-w-screen-md flex flex-col rounded-t-2xl bg-white text-gray-900 shadow-lg safe-bottom dark:bg-gray-900 dark:text-gray-100"
|
|
356
|
+
:class="{
|
|
357
|
+
'transition-none motion-reduce:transition-none': drag.isDragging,
|
|
358
|
+
'transition-transform duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging && (drag.closing || drag.offset > 0),
|
|
359
|
+
}"
|
|
360
|
+
role="dialog"
|
|
361
|
+
:style="{
|
|
362
|
+
transform: drag.closing ? 'translateY(100%)' : drag.offset > 0 ? `translateY(${drag.offset}px)` : undefined,
|
|
363
|
+
}"
|
|
364
|
+
tabindex="-1"
|
|
365
|
+
@transitionend="onPanelTransitionEnd"
|
|
366
|
+
>
|
|
367
|
+
<div
|
|
368
|
+
class="flex shrink-0 cursor-grab touch-none justify-center px-5 pb-2 pt-3 active:cursor-grabbing"
|
|
369
|
+
@pointercancel="onPointerUp"
|
|
370
|
+
@pointerdown="onHeaderPointerDown"
|
|
371
|
+
@pointermove="onPointerMove"
|
|
372
|
+
@pointerup="onPointerUp"
|
|
373
|
+
>
|
|
374
|
+
<div class="h-1.5 w-10 rounded-full bg-gray-300 dark:bg-gray-700" />
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div
|
|
378
|
+
ref="contentRef"
|
|
379
|
+
class="min-h-0 flex-1 overflow-y-auto px-5 pb-4"
|
|
380
|
+
@pointercancel="onPointerUp"
|
|
381
|
+
@pointerdown="onContentPointerDown"
|
|
382
|
+
@pointermove="onPointerMove"
|
|
383
|
+
@pointerup="onPointerUp"
|
|
384
|
+
>
|
|
385
|
+
<slot />
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</Transition>
|
|
389
|
+
</Teleport>
|
|
390
|
+
</ClientOnly>
|
|
391
|
+
</template>
|
package/app/types/bases.d.ts
CHANGED
|
@@ -87,7 +87,6 @@ export interface BaseCard {
|
|
|
87
87
|
id?: number | string
|
|
88
88
|
image?: string
|
|
89
89
|
isSelected?: boolean
|
|
90
|
-
size?: BaseCardSize
|
|
91
90
|
title?: string
|
|
92
91
|
titleIcon?: string
|
|
93
92
|
to?: RouteLocationNamedI18n
|
|
@@ -95,8 +94,6 @@ export interface BaseCard {
|
|
|
95
94
|
|
|
96
95
|
export type BaseCardDirection = 'horizontal' | 'vertical'
|
|
97
96
|
|
|
98
|
-
export type BaseCardSize = 'base' | 'lg' | 'sm'
|
|
99
|
-
|
|
100
97
|
export interface BaseCharacter {
|
|
101
98
|
character?: BaseCharacterCharacter
|
|
102
99
|
size?: BaseCharacterSize
|
package/app/types/global.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ declare global {
|
|
|
14
14
|
type BaseButtonType = import('./bases').BaseButtonType
|
|
15
15
|
type BaseCard = import('./bases').BaseCard
|
|
16
16
|
type BaseCardDirection = import('./bases').BaseCardDirection
|
|
17
|
-
type BaseCardSize = import('./bases').BaseCardSize
|
|
18
17
|
type BaseCharacter = import('./bases').BaseCharacter
|
|
19
18
|
type BaseCharacterCharacter = import('./bases').BaseCharacterCharacter
|
|
20
19
|
type BaseCharacterSize = import('./bases').BaseCharacterSize
|
|
@@ -93,6 +92,9 @@ declare global {
|
|
|
93
92
|
type FieldTextarea = import('./fields').FieldTextarea
|
|
94
93
|
type FieldTime = import('./fields').FieldTime
|
|
95
94
|
|
|
95
|
+
// Layout
|
|
96
|
+
type LayoutBottomSheet = import('./layout').LayoutBottomSheet
|
|
97
|
+
|
|
96
98
|
// Project
|
|
97
99
|
type LayerIconIcon = import('../composables/useLayerIcons').LayerIconIcon
|
|
98
100
|
type LayerIconValue = import('../composables/useLayerIcons').LayerIconValue
|
package/nuxt.config.ts
CHANGED
|
@@ -5,9 +5,11 @@ import { defineNuxtConfig } from 'nuxt/config'
|
|
|
5
5
|
import uno from './uno.config.js'
|
|
6
6
|
|
|
7
7
|
const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const isPostHogEnabled = process.env.NODE_ENV === 'production' && Boolean(process.env.NUXT_PUBLIC_POSTHOG_KEY)
|
|
8
9
|
|
|
9
10
|
export default defineNuxtConfig({
|
|
10
11
|
modules: [
|
|
12
|
+
'@posthog/nuxt',
|
|
11
13
|
'@nuxt/icon',
|
|
12
14
|
'@nuxt/scripts',
|
|
13
15
|
'@nuxtjs/color-mode',
|
|
@@ -88,6 +90,20 @@ export default defineNuxtConfig({
|
|
|
88
90
|
experimental: { typedPages: true },
|
|
89
91
|
},
|
|
90
92
|
|
|
93
|
+
posthogConfig: {
|
|
94
|
+
clientConfig: {
|
|
95
|
+
capture_pageview: isPostHogEnabled,
|
|
96
|
+
defaults: '2026-01-30',
|
|
97
|
+
disable_session_recording: !isPostHogEnabled,
|
|
98
|
+
opt_out_capturing_by_default: !isPostHogEnabled,
|
|
99
|
+
person_profiles: 'identified_only',
|
|
100
|
+
session_recording: { maskAllInputs: true },
|
|
101
|
+
ui_host: 'https://eu.posthog.com',
|
|
102
|
+
},
|
|
103
|
+
host: 'https://eu.i.posthog.com',
|
|
104
|
+
publicKey: isPostHogEnabled ? process.env.NUXT_PUBLIC_POSTHOG_KEY : '',
|
|
105
|
+
},
|
|
106
|
+
|
|
91
107
|
plausible: {
|
|
92
108
|
apiHost: 'https://plausible.saasmakers.dev',
|
|
93
109
|
autoOutboundTracking: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasmakers/ui",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.51",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reusable Nuxt UI components for SaaS Makers projects",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@nuxtjs/robots": "6.1.0",
|
|
31
31
|
"@nuxtjs/sitemap": "8.2.0",
|
|
32
32
|
"@pinia/nuxt": "0.11.3",
|
|
33
|
+
"@posthog/nuxt": "1.7.76",
|
|
33
34
|
"@unhead/vue": "2.0.19",
|
|
34
35
|
"@unocss/nuxt": "66.7.0",
|
|
35
36
|
"@unocss/reset": "66.7.0",
|