@bagelink/vue 0.0.25 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/package.json +34 -9
  2. package/src/components/Btn.vue +221 -0
  3. package/src/components/Comments.vue +284 -0
  4. package/src/components/ContactArray.vue +142 -0
  5. package/src/components/ContactSubmissions.vue +45 -0
  6. package/src/components/DataPreview.vue +85 -0
  7. package/src/components/DropDown.vue +122 -0
  8. package/src/components/FileUploader.vue +353 -0
  9. package/src/components/FormKitTable.vue +299 -0
  10. package/src/components/FormSchema.vue +79 -0
  11. package/src/components/LangText.vue +32 -0
  12. package/src/components/ListItem.vue +20 -0
  13. package/src/components/ListView.vue +54 -0
  14. package/src/components/MaterialIcon.vue +19 -0
  15. package/src/components/Modal.vue +62 -0
  16. package/src/components/ModalForm.vue +109 -0
  17. package/src/components/NavBar.vue +353 -0
  18. package/src/components/PageTitle.vue +17 -0
  19. package/src/components/PersonPreview.vue +203 -0
  20. package/src/components/PersonPreviewFormkit.vue +205 -0
  21. package/src/components/RTXEditor.vue +151 -0
  22. package/src/components/RouterWrapper.vue +17 -0
  23. package/src/components/TabbedLayout.vue +83 -0
  24. package/src/components/TableSchema.vue +248 -0
  25. package/src/components/TopBar.vue +5 -0
  26. package/src/components/charts/BarChart.vue +316 -0
  27. package/src/components/dashboard/Lineart.vue +197 -0
  28. package/src/components/form/ItemRef.vue +45 -0
  29. package/src/components/form/MaterialIcon.vue +19 -0
  30. package/src/components/form/PlainInputField.vue +79 -0
  31. package/src/components/form/inputs/CheckInput.vue +143 -0
  32. package/src/components/form/inputs/Checkbox.vue +77 -0
  33. package/src/components/form/inputs/ColorPicker.vue +47 -0
  34. package/src/components/form/inputs/CurrencyInput.vue +137 -0
  35. package/src/components/form/inputs/DateInput.vue +55 -0
  36. package/src/components/form/inputs/DatetimeInput.vue +50 -0
  37. package/src/components/form/inputs/DurationInput.vue +55 -0
  38. package/src/components/form/inputs/DynamicLinkField.vue +142 -0
  39. package/src/components/form/inputs/EmailInput.vue +57 -0
  40. package/src/components/form/inputs/FloatInput.vue +52 -0
  41. package/src/components/form/inputs/IntInput.vue +53 -0
  42. package/src/components/form/inputs/JSONInput.vue +55 -0
  43. package/src/components/form/inputs/LinkField.vue +300 -0
  44. package/src/components/form/inputs/Password.vue +91 -0
  45. package/src/components/form/inputs/PasswordInput.vue +92 -0
  46. package/src/components/form/inputs/PlainText.vue +63 -0
  47. package/src/components/form/inputs/ReadOnlyInput.vue +28 -0
  48. package/src/components/form/inputs/RichTextEditor.vue +56 -0
  49. package/src/components/form/inputs/SelectField.vue +258 -0
  50. package/src/components/form/inputs/TableField.vue +319 -0
  51. package/src/components/form/inputs/TextArea.vue +79 -0
  52. package/src/components/form/inputs/TextInput.vue +63 -0
  53. package/src/components/form/inputs/index.ts +16 -0
  54. package/src/components/formkit/AddressArray.vue +240 -0
  55. package/src/components/formkit/BankDetailsArray.vue +265 -0
  56. package/src/components/formkit/ContactArrayFormKit.vue +192 -0
  57. package/src/components/formkit/FileUploader.vue +391 -0
  58. package/src/components/formkit/MiscFields.vue +74 -0
  59. package/src/components/formkit/Toggle.vue +164 -0
  60. package/src/components/formkit/index.ts +29 -0
  61. package/src/components/index.ts +20 -0
  62. package/src/components/whatsapp/form/MsgTemplate.vue +227 -0
  63. package/src/components/whatsapp/form/TextVariableExamples.vue +79 -0
  64. package/src/components/whatsapp/interfaces.ts +58 -0
  65. package/src/index.ts +1 -26
  66. package/src/plugins/bagel.ts +26 -0
  67. package/src/styles/modal.css +90 -0
  68. package/src/types/BagelField.ts +57 -0
  69. package/src/types/BtnOptions.ts +14 -0
  70. package/src/types/Person.ts +51 -0
  71. package/src/types/file.ts +12 -0
  72. package/src/types/index.ts +4 -0
  73. package/src/types/materialIcons.d.ts +3005 -0
  74. package/src/utils/index.ts +62 -0
  75. package/src/utils/modal.ts +101 -0
  76. package/src/utils/objects.ts +81 -0
  77. package/src/utils/strings.ts +36 -0
  78. package/dist/index.cjs +0 -23
  79. package/dist/index.d.cts +0 -12
  80. package/dist/index.d.mts +0 -12
  81. package/dist/index.d.ts +0 -12
  82. package/dist/index.mjs +0 -19
@@ -0,0 +1,299 @@
1
+ <template>
2
+ <div class="table-list-wrap h-100">
3
+ <!-- <Transition name="fade"> -->
4
+ <div class="table-list">
5
+ <table class="infinite-wrapper">
6
+ <thead class="row first-row">
7
+ <th
8
+ class="col"
9
+ v-for="field in columns"
10
+ :key="field.id"
11
+ @click="sort(field.id)"
12
+ >
13
+ <div class="flex">
14
+ {{ field.label || field.id }}
15
+ <div
16
+ class="list-arrows"
17
+ :class="{ sorted: sortField === field.id }"
18
+ >
19
+ <MaterialIcon
20
+ :class="{ desc: sortDirection === 'DESC' }"
21
+ icon="keyboard_arrow_up"
22
+ />
23
+ </div>
24
+ </div>
25
+ </th>
26
+ </thead>
27
+ <tbody
28
+ ref="infinite"
29
+ class="rows infinite"
30
+ :class="{ loading }"
31
+ >
32
+ <tr
33
+ @click="selectElement(row)"
34
+ class="row row-item position-relative"
35
+ v-for="row in data"
36
+ :key="row.id"
37
+ >
38
+ <td
39
+ class="col"
40
+ v-for="field in columns"
41
+ :key="`${field.id}-${row.id}`"
42
+ >
43
+ <slot
44
+ v-if="slots[field.id]"
45
+ :name="field.id"
46
+ :row="row"
47
+ :field="field"
48
+ />
49
+ <div v-else>
50
+ <div v-if="field.inputType === 'DateInput'">
51
+ {{ new Date(row[field.id]).toLocaleDateString() }}
52
+ </div>
53
+ <div v-else-if="field.inputType === 'CheckInput'">
54
+ <MaterialIcon
55
+ icon="check"
56
+ class="pill green"
57
+ v-if="row[field.id]"
58
+ />
59
+ <MaterialIcon
60
+ icon="close"
61
+ class="pill"
62
+ v-else
63
+ />
64
+ </div>
65
+ <div v-else-if="field.inputType === 'ItemRef' && row[field.id]">
66
+ <Btn
67
+ v-if="field.refCollection"
68
+ color="gray"
69
+ class="thin"
70
+ @click="
71
+ $router.push(
72
+ `/${field.refCollection}/${row[field.id].id}`,
73
+ )"
74
+ >
75
+ {{
76
+ field.refFields
77
+ ?.map((k) => row[field.id][k])
78
+ .join(' ') || row[field.id].id
79
+ }}
80
+ <MaterialIcon icon="arrow_forward" />
81
+ </Btn>
82
+ <Btn
83
+ icon="receipt"
84
+ is="a"
85
+ thin
86
+ v-else-if="field.link"
87
+ :href="row[field.id][field.link]"
88
+ target="_blank"
89
+ >
90
+ {{
91
+ field.refFields
92
+ ?.map((k) => row[field.id][k])
93
+ .join(' ') || row[field.id].id
94
+ }}
95
+ </Btn>
96
+ <div
97
+ class="pill"
98
+ v-else
99
+ :class="getPillClass(row, field)"
100
+ >
101
+ {{
102
+ field.refFields
103
+ ?.map((k) => row[field.id][k])
104
+ .join(' ') || row[field.id].id
105
+ }}
106
+ </div>
107
+ </div>
108
+ <div
109
+ class="max-col-width"
110
+ v-else
111
+ >
112
+ {{ row[field.id] }}
113
+ </div>
114
+ </div>
115
+ </td>
116
+ </tr>
117
+ </tbody>
118
+ </table>
119
+ </div>
120
+ <!-- </Transition> -->
121
+ </div>
122
+ </template>
123
+
124
+ <script setup lang="ts">
125
+ import { computed, ref, useSlots } from 'vue';
126
+ import { BagelField } from '@/types';
127
+ import { Btn, MaterialIcon } from '@/components';
128
+
129
+ const slots = useSlots();
130
+ const loading = ref(true);
131
+
132
+ const props = defineProps<{
133
+ data: any[];
134
+ schema?: BagelField[];
135
+ }>();
136
+
137
+ const emit = defineEmits(['selectElement']);
138
+
139
+ const selectElement = (data: Record<string, any>) => {
140
+ emit('selectElement', data);
141
+ };
142
+
143
+ // const sortList = () => {};
144
+ const sortDirection = ref('');
145
+ const sortField = ref('');
146
+
147
+ const sort = (fieldname: string) => {
148
+ if (sortField.value === fieldname) {
149
+ if (sortDirection.value === 'ASC') sortDirection.value = 'DESC';
150
+ else sortField.value = '';
151
+ } else {
152
+ sortField.value = fieldname;
153
+ sortDirection.value = 'ASC';
154
+ }
155
+ };
156
+
157
+ const columns = computed<BagelField[]>(
158
+ (): BagelField[] => props.schema ||
159
+ Object.keys(props.data[0]).map((k: string) => ({
160
+ id: k,
161
+ inputType: 'PlainText',
162
+ })),
163
+ );
164
+
165
+ function getPillClass(row: Record<string, any>, field: BagelField) {
166
+ return field.refFields?.map((k) => row[field.id][k]).join(' ') || row[field.id].id;
167
+ }
168
+ </script>
169
+
170
+ <style scoped>
171
+ .Paid {
172
+ background-color: var(--bgl-green);
173
+ color: white;
174
+ }
175
+
176
+ .Error {
177
+ background-color: var(--bgl-red-tint);
178
+ color: var(--bgl-red)
179
+ }
180
+
181
+ .list-arrows {
182
+ opacity: 0;
183
+ }
184
+
185
+ .list-arrows .icon-font {
186
+ transition: all ease-in-out 0.2s;
187
+ }
188
+
189
+ .list-arrows.sorted {
190
+ opacity: 1;
191
+ }
192
+
193
+ .list-arrows.sorted .desc {
194
+ transform: rotate(180deg);
195
+ }
196
+
197
+ table {
198
+ border-collapse: separate;
199
+ border-spacing: 0 15px;
200
+ border-collapse: collapse;
201
+ }
202
+
203
+ th {
204
+ font-weight: 400;
205
+ }
206
+
207
+ .row {
208
+ border-bottom: 1px solid var(--border-color);
209
+ cursor: pointer;
210
+ }
211
+
212
+ .row.first-row {
213
+ font-size: 0.8rem;
214
+ color: var(--bgl-black-tint);
215
+ position: sticky;
216
+ top: 0;
217
+ z-index: 2;
218
+ background: var(--bgl-white);
219
+ height: 50px;
220
+ vertical-align: bottom;
221
+ }
222
+
223
+ .row.first-row::after {
224
+ content: '';
225
+ border-bottom: 1px solid var(--border-color);
226
+ position: absolute;
227
+ left: 0;
228
+ right: 0;
229
+ bottom: -1px;
230
+ }
231
+
232
+ .first-row .col {
233
+ cursor: pointer;
234
+ background: var(--bgl-white);
235
+ }
236
+
237
+ .col {
238
+ white-space: nowrap;
239
+ padding: 14px;
240
+ transition: var(--bgl-transition);
241
+ line-height: 1;
242
+ padding-left: 1rem;
243
+ padding-right: 1rem;
244
+ align-items: center;
245
+ }
246
+
247
+ .col>div {
248
+ display: flex;
249
+ gap: 0.5rem;
250
+ }
251
+
252
+ .max-col-width {
253
+ max-width: 30vw;
254
+ overflow: hidden;
255
+ text-overflow: ellipsis;
256
+ }
257
+
258
+ .col.check .icon-font {
259
+ border-radius: 100%;
260
+ background: var(--bgl-blue-20);
261
+ color: var(--bgl-blue);
262
+ width: 20px;
263
+ height: 20px;
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: center;
267
+ margin-top: -2px;
268
+ }
269
+
270
+ .rows {
271
+ font-size: 0.8125em;
272
+ }
273
+
274
+ .table-list {
275
+ height: 100%;
276
+ position: relative;
277
+ padding-left: 0 !important;
278
+ padding-right: 0 !important;
279
+ overflow: auto;
280
+ }
281
+
282
+ .BagelTable .table-list {
283
+ overflow: unset;
284
+ }
285
+
286
+ .row-item {
287
+ height: 50px;
288
+ transition: all 200ms ease;
289
+ }
290
+
291
+ .row-item:hover {
292
+ background: var(--bgl-gray-light);
293
+ }
294
+
295
+ .infinite-wrapper {
296
+ overflow-y: auto;
297
+ width: 100%;
298
+ }
299
+ </style>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <div>
3
+ <FormKit
4
+ type="form"
5
+ :modelValue="modelValue"
6
+ @update:modelValue="handleEmit"
7
+ @submit="runSubmit"
8
+ :submitLabel="$t('save')"
9
+ >
10
+ <FormKitSchema
11
+ :schema="schema"
12
+ :data="data"
13
+ />
14
+ </FormKit>
15
+ <Btn
16
+ class="del-top"
17
+ v-if="modelValue?.id && onDelete"
18
+ @click="runDelete"
19
+ value="Delete"
20
+ flat
21
+ icon="delete"
22
+ color="red"
23
+ thin
24
+ />
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import { reactive } from 'vue';
30
+ import { type FormKitSchemaDefinition } from '@formkit/core';
31
+
32
+ import { Btn } from '@/components';
33
+ import { useModal } from '@/utils';
34
+
35
+ const { showModal } = useModal();
36
+ const emits = defineEmits(['update:modelValue', 'submit']);
37
+ const handleEmit = (val: any) => emits('update:modelValue', val);
38
+ const runSubmit = (val: any) => emits('submit', val);
39
+
40
+ const props = withDefaults(defineProps<{
41
+ modelValue?: any;
42
+ schema: FormKitSchemaDefinition;
43
+ // eslint-disable-next-line no-unused-vars
44
+ onDelete?: ((id: string) => void);
45
+ // eslint-disable-next-line no-unused-vars
46
+ $t: (key: string) => string;
47
+ }>(), {
48
+ $t: (key: string) => key,
49
+ });
50
+
51
+ const data = reactive({
52
+ ...props.modelValue,
53
+ t: (val: string) => props.$t(val),
54
+ });
55
+
56
+ const runDelete = () => {
57
+ showModal(
58
+ {
59
+ class: 'small-modal',
60
+ title: props.$t('Are you sure?'),
61
+ actions: [
62
+ {
63
+ value: 'Confirm',
64
+ color: 'red',
65
+ onClick: () => props.onDelete?.(data.id),
66
+ },
67
+ { value: 'Cancel', color: 'gray' },
68
+ ],
69
+ },
70
+ { default: props.$t('form.deleteMessage') },
71
+ );
72
+ };
73
+ </script>
74
+
75
+ <style>
76
+ .del-top {
77
+ transform: translateY(-2.6rem);
78
+ }
79
+ </style>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <span ref="el" />
3
+ {{ input }}
4
+ <!-- v-html="$t(input)" -->
5
+ </template>
6
+ <script setup lang="ts">
7
+ defineProps<{
8
+ input: any;
9
+ }>();
10
+ </script>
11
+ <style>
12
+ .translate {
13
+ cursor: pointer;
14
+ position: relative;
15
+ }
16
+
17
+ /*
18
+ .translate:hover {
19
+ color: var(--bgl-blue);
20
+ }
21
+ */
22
+
23
+ .translate-globe {
24
+ position: absolute;
25
+ top: -5px;
26
+ right: -5px;
27
+ width: 10px;
28
+ height: 10px;
29
+ background-size: contain;
30
+ /* background-image: url('@/assets/globe.svg'); */
31
+ }
32
+ </style>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <component
3
+ :is="to ? 'RouterLink' : 'div'"
4
+ :to="to"
5
+ class="list-item ellipsis"
6
+ >
7
+ <p class="txt16 no-margin">
8
+ <slot />
9
+ </p>
10
+ <p class="txt14 no-margin txtgray">
11
+ <slot name="subtitle" />
12
+ </p>
13
+ </component>
14
+ </template>
15
+
16
+ <script lang="ts" setup>
17
+ defineProps<{
18
+ to?: string;
19
+ }>();
20
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div class="card list-view grid thin">
3
+ <div class="list-header flex gap-3 align-items-top">
4
+ <div
5
+ v-if="enableSearch"
6
+ class="bagel-input search-wrap"
7
+ >
8
+ <input
9
+ :placeholder="searchPlaceholder"
10
+ v-model="searchTerm"
11
+ @input="search()"
12
+ >
13
+ <MaterialIcon
14
+ class="txtgray"
15
+ icon="search"
16
+ />
17
+ </div>
18
+ <Btn
19
+ color="blue"
20
+ v-if="enableAdd"
21
+ icon="add"
22
+ @click="emit('add')"
23
+ />
24
+ </div>
25
+ <div class="list-content grid auto-flow-rows align-items-start">
26
+ <slot />
27
+ </div>
28
+ </div>
29
+ </template>
30
+
31
+ <script lang="ts" setup>
32
+ import { MaterialIcon, Btn } from '@/components';
33
+ import { debounce } from '@/utils';
34
+
35
+ defineProps<{
36
+ enableAdd: boolean;
37
+ enableSearch: boolean;
38
+ open: boolean;
39
+ searchPlaceholder: string;
40
+ }
41
+ >();
42
+ const emit = defineEmits(['search', 'add', 'debounce']);
43
+ const searchTerm = $ref('');
44
+ const search = () => {
45
+ emit('search', searchTerm);
46
+ debounce(() => emit('debounce', searchTerm));
47
+ };
48
+ </script>
49
+
50
+ <style>
51
+ .search-wrap {
52
+ flex-grow: 1;
53
+ }
54
+ </style>
@@ -0,0 +1,19 @@
1
+ <template>
2
+ <div
3
+ class="icon-font"
4
+ :style="{ fontSize: `${size}rem` }"
5
+ >
6
+ {{ icon }}
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import type { MaterialIcons } from '@/types/materialIcons';
12
+
13
+ defineProps<{
14
+ icon: MaterialIcons;
15
+ size?: number;
16
+ }>();
17
+ </script>
18
+
19
+ <style scoped></style>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <div
3
+ class="bg-dark"
4
+ :class="{ 'is-side': side, 'is-active': isActive }"
5
+ @click="() => (dismissable ? closeModal() : '')"
6
+ @keydown.esc="closeModal"
7
+ >
8
+ <div
9
+ class="card modal"
10
+ @click.stop
11
+ >
12
+ <header class="tool-bar">
13
+ <slot name="toolbar" />
14
+ </header>
15
+ <h3 class="modal-title">
16
+ {{ title }}
17
+ </h3>
18
+ <slot />
19
+ <footer class="modal-footer mt-3">
20
+ <Btn
21
+ v-for="(action, i) in actions"
22
+ :key="i"
23
+ @click="closeModal"
24
+ color="gray"
25
+ v-bind="action"
26
+ />
27
+ <slot name="footer" />
28
+ </footer>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script lang="ts" setup>
34
+ import { onMounted } from 'vue';
35
+ import { Btn } from '@/components';
36
+ import { BtnOptions } from '@/types/BtnOptions';
37
+ import '../styles/modal.css';
38
+
39
+ defineProps<{
40
+ side?: boolean;
41
+ title?: string;
42
+ dismissable?: boolean;
43
+ actions?: BtnOptions[];
44
+ }>();
45
+
46
+ let isActive = $ref<boolean>(false);
47
+
48
+ onMounted(() => {
49
+ setTimeout(() => {
50
+ isActive = true;
51
+ }, 1);
52
+ });
53
+
54
+ const emit = defineEmits(['update:isModalVisible']);
55
+
56
+ const closeModal = () => {
57
+ isActive = false;
58
+ setTimeout(() => {
59
+ emit('update:isModalVisible', false);
60
+ }, 200);
61
+ };
62
+ </script>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div
3
+ class="bg-dark"
4
+ :class="{ 'is-side': side, 'is-active': isActive }"
5
+ @click="() => (dismissable ? closeModal() : '')"
6
+ @keydown.esc="closeModal"
7
+ >
8
+ <div
9
+ class="card modal"
10
+ @click.stop
11
+ >
12
+ <header class="tool-bar">
13
+ <slot name="toolbar" />
14
+ <Btn
15
+ :style="{ float: side ? 'left' : 'right' }"
16
+ flat
17
+ icon="close"
18
+ @click="closeModal"
19
+ />
20
+ <h3 class="modal-title">
21
+ {{ title }}
22
+ </h3>
23
+ </header>
24
+ <FormSchema
25
+ :onDelete="onDelete ? runDelete : null"
26
+ :modelValue="modelValue"
27
+ @update:modelValue="handleEmit"
28
+ @submit="runSubmit"
29
+ :schema="computedFormSchema"
30
+ />
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script lang="ts" setup>
36
+ import { type FormKitSchemaDefinition } from '@formkit/core';
37
+ import { onMounted } from 'vue';
38
+ import { Btn, FormSchema } from '@/components';
39
+ import '../styles/modal.css';
40
+ import { BtnOptions } from '@/types/BtnOptions';
41
+
42
+ // import { BagelField } from '@/types';
43
+
44
+ const props = defineProps<{
45
+ side?: boolean;
46
+ title?: string;
47
+ dismissable?: boolean;
48
+ actions?: BtnOptions[];
49
+ schema: FormKitSchemaDefinition | (() => FormKitSchemaDefinition);
50
+ modelValue?: Record<string, any>;
51
+ // eslint-disable-next-line no-unused-vars
52
+ onSubmit?: ((formData: any) => Promise<void>);
53
+ // eslint-disable-next-line no-unused-vars
54
+ onDelete?: ((id: string) => void);
55
+ }>();
56
+
57
+ const computedFormSchema = $computed(() => {
58
+ if (typeof props.schema === 'function') {
59
+ return props.schema();
60
+ }
61
+ return props.schema;
62
+ });
63
+
64
+ let isActive = $ref<boolean>(false);
65
+
66
+ onMounted(() => {
67
+ setTimeout(() => {
68
+ isActive = true;
69
+ }, 1);
70
+ });
71
+
72
+ const emit = defineEmits(['update:isModalVisible', 'update:modelValue']);
73
+ const handleEmit = (value: any) => {
74
+ emit('update:modelValue', value);
75
+ };
76
+
77
+ const closeModal = () => {
78
+ isActive = false;
79
+ setTimeout(() => {
80
+ emit('update:isModalVisible', false);
81
+ }, 200);
82
+ };
83
+
84
+ const runSubmit = async (formData: any) => {
85
+ try {
86
+ await props.onSubmit?.(formData);
87
+ closeModal();
88
+ } catch (err) {
89
+ console.error(err);
90
+ }
91
+ };
92
+
93
+ const runDelete = () => {
94
+ props.onDelete?.(props.modelValue?.id);
95
+ closeModal();
96
+ };
97
+ </script>
98
+
99
+ <style scoped>
100
+ .modal-title {
101
+ margin-top: 0.5rem;
102
+ }
103
+
104
+ @media screen and (max-width: 910px) {
105
+ .modal-title {
106
+ margin-top: 1rem;
107
+ }
108
+ }
109
+ </style>