vueless 0.0.514 → 0.0.516

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 (36) hide show
  1. package/package.json +1 -1
  2. package/types.ts +8 -0
  3. package/ui.button-link/useAttrs.ts +2 -2
  4. package/ui.dropdown-badge/UDropdownBadge.vue +70 -167
  5. package/ui.dropdown-badge/storybook/Docs.mdx +2 -2
  6. package/ui.dropdown-badge/storybook/{stories.js → stories.ts} +16 -5
  7. package/ui.dropdown-badge/types.ts +93 -0
  8. package/ui.dropdown-badge/{useAttrs.js → useAttrs.ts} +13 -2
  9. package/ui.dropdown-button/UDropdownButton.vue +85 -203
  10. package/ui.dropdown-button/storybook/Docs.mdx +2 -2
  11. package/ui.dropdown-button/storybook/{stories.js → stories.ts} +25 -11
  12. package/ui.dropdown-button/types.ts +108 -0
  13. package/ui.dropdown-button/{useAttrs.js → useAttrs.ts} +14 -3
  14. package/ui.dropdown-link/UDropdownLink.vue +84 -194
  15. package/ui.dropdown-link/storybook/Docs.mdx +2 -2
  16. package/ui.dropdown-link/storybook/{stories.js → stories.ts} +20 -9
  17. package/ui.dropdown-link/types.ts +103 -0
  18. package/ui.dropdown-link/{useAttrs.js → useAttrs.ts} +14 -3
  19. package/ui.dropdown-list/UDropdownList.vue +112 -177
  20. package/ui.dropdown-list/storybook/Docs.mdx +2 -2
  21. package/ui.dropdown-list/storybook/{stories.js → stories.ts} +23 -7
  22. package/ui.dropdown-list/types.ts +52 -0
  23. package/ui.dropdown-list/{useAttrs.js → useAttrs.ts} +6 -3
  24. package/ui.dropdown-list/usePointer.ts +111 -0
  25. package/ui.text-money/UMoney.vue +1 -1
  26. package/ui.text-money/config.ts +12 -13
  27. package/web-types.json +117 -55
  28. package/ui.dropdown-list/usePointer.js +0 -86
  29. /package/ui.dropdown-badge/{config.js → config.ts} +0 -0
  30. /package/ui.dropdown-badge/{constants.js → constants.ts} +0 -0
  31. /package/ui.dropdown-button/{config.js → config.ts} +0 -0
  32. /package/ui.dropdown-button/{constants.js → constants.ts} +0 -0
  33. /package/ui.dropdown-link/{config.js → config.ts} +0 -0
  34. /package/ui.dropdown-link/{constants.js → constants.ts} +0 -0
  35. /package/ui.dropdown-list/{config.js → config.ts} +0 -0
  36. /package/ui.dropdown-list/{constants.js → constants.ts} +0 -0
@@ -1,103 +1,4 @@
1
- <template>
2
- <div
3
- ref="wrapperRef"
4
- tabindex="1"
5
- :style="{ maxHeight: wrapperHeight }"
6
- v-bind="wrapperAttrs"
7
- @keydown.self.down.prevent="pointerForward"
8
- @keydown.self.up.prevent="pointerBackward"
9
- @keydown.enter.stop.self="addPointerElement"
10
- >
11
- <ul :id="`listbox-${elementId}`" v-bind="listAttrs" role="listbox">
12
- <li
13
- v-for="(option, index) of options"
14
- :id="`${elementId}-${index}`"
15
- :key="index"
16
- v-bind="listItemAttrs"
17
- ref="optionsRef"
18
- :role="!(option && option.groupLabel) ? 'option' : null"
19
- :data-group-label="Boolean(option.groupLabel)"
20
- >
21
- <!-- option title -->
22
- <span
23
- v-if="!(option && (option.groupLabel || option.isSubGroup)) && !option.isHidden"
24
- v-bind="optionAttrs"
25
- :class="optionHighlight(index, option)"
26
- @click="select(option), onClickOption(option)"
27
- @mouseenter.self="pointerSet(index)"
28
- >
29
- <!--
30
- @slot Use it to add something before the option.
31
- @binding {object} option
32
- @binding {number} index
33
- -->
34
- <slot name="before-option" :option="option" :index="index" />
35
-
36
- <!--
37
- @slot Use it to add something instead of the option.
38
- @binding {object} option
39
- @binding {number} index
40
- -->
41
- <slot name="option" :option="option" :index="index">
42
- <span
43
- :style="getMarginForSubCategory(option.level)"
44
- v-bind="optionContentAttrs"
45
- v-text="option[labelKey]"
46
- />
47
- </slot>
48
-
49
- <!--
50
- @slot Use it to add something after the option.
51
- @binding {object} option
52
- @binding {number} index
53
- -->
54
- <slot name="after-option" :option="option" :index="index" />
55
- </span>
56
-
57
- <!-- group title -->
58
- <template v-if="option && (option.groupLabel || option.isSubGroup) && !option.isHidden">
59
- <div v-if="option.groupLabel" v-bind="groupAttrs" v-text="option.groupLabel" />
60
-
61
- <div
62
- v-else-if="option.isSubGroup"
63
- :style="getMarginForSubCategory(option.level)"
64
- v-bind="subGroupAttrs"
65
- v-text="option[labelKey]"
66
- />
67
- </template>
68
- </li>
69
-
70
- <div v-if="!options.length" v-bind="optionAttrs">
71
- <!-- @slot Use it to add something instead of empty state. -->
72
- <slot name="empty">
73
- <span v-bind="optionContentAttrs" v-text="currentLocale.noDataToShow" />
74
- </slot>
75
- </div>
76
-
77
- <!-- Add button -->
78
- <template v-if="addOption">
79
- <div v-bind="addOptionLabelWrapperAttrs" @click="onClickAddOption">
80
- <div v-bind="addOptionLabelAttrs">
81
- {{ currentLocale.add }}
82
- <span v-bind="addOptionLabelHotkeyAttrs" v-text="addOptionKeyCombination" />
83
- </div>
84
- </div>
85
-
86
- <UButton round square v-bind="addOptionButtonAttrs" @click="onClickAddOption">
87
- <UIcon
88
- internal
89
- color="white"
90
- size="xs"
91
- :name="config.defaults.addOptionIcon"
92
- v-bind="addOptionIconAttrs"
93
- />
94
- </UButton>
95
- </template>
96
- </ul>
97
- </div>
98
- </template>
99
-
100
- <script setup>
1
+ <script lang="ts" setup>
101
2
  import { computed, ref, useId } from "vue";
102
3
  import { merge } from "lodash-es";
103
4
 
@@ -108,90 +9,25 @@ import { getDefault } from "../utils/ui.ts";
108
9
  import { isMac } from "../utils/platform.ts";
109
10
 
110
11
  import usePointer from "./usePointer.js";
111
- import useAttrs from "./useAttrs.js";
12
+ import useAttrs from "./useAttrs.ts";
112
13
  import { useLocale } from "../composables/useLocale.ts";
113
14
 
114
- import defaultConfig from "./config.js";
115
- import { UDropdownList } from "./constants.js";
15
+ import defaultConfig from "./config.ts";
16
+ import { UDropdownList } from "./constants.ts";
17
+
18
+ import type { UDropdownListProps } from "./types.ts";
116
19
 
117
20
  defineOptions({ inheritAttrs: false });
118
21
 
119
22
  // TODO: Use props and regular modal value
120
23
  const modelValue = defineModel({ type: [String, Number, Object], default: "" });
121
24
 
122
- const props = defineProps({
123
- /**
124
- * List options.
125
- */
126
- options: {
127
- type: Array,
128
- default: () => [],
129
- },
130
-
131
- /**
132
- * Label key in the item object of options.
133
- */
134
- labelKey: {
135
- type: String,
136
- default: getDefault(defaultConfig, UDropdownList).valueKey,
137
- },
138
-
139
- /**
140
- * Value key in the item object of options.
141
- */
142
- valueKey: {
143
- type: String,
144
- default: getDefault(defaultConfig, UDropdownList).valueKey,
145
- },
146
-
147
- /**
148
- * Show add option button.
149
- */
150
- addOption: {
151
- type: Boolean,
152
- default: false,
153
- },
154
-
155
- /**
156
- * Disable the list.
157
- */
158
- disabled: {
159
- type: Boolean,
160
- default: getDefault(defaultConfig, UDropdownList).disabled,
161
- },
162
-
163
- /**
164
- * List size.
165
- * @values sm, md, lg
166
- */
167
- size: {
168
- type: String,
169
- default: getDefault(defaultConfig, UDropdownList).size,
170
- },
171
-
172
- /**
173
- * Number of options to show without a scroll.
174
- */
175
- visibleOptions: {
176
- type: Number,
177
- default: getDefault(defaultConfig, UDropdownList).visibleOptions,
178
- },
179
-
180
- /**
181
- * Unique element id.
182
- */
183
- id: {
184
- type: String,
185
- default: "",
186
- },
187
-
188
- /**
189
- * Component config object.
190
- */
191
- config: {
192
- type: Object,
193
- default: () => ({}),
194
- },
25
+ const props = withDefaults(defineProps<UDropdownListProps>(), {
26
+ labelKey: getDefault<UDropdownListProps>(defaultConfig, UDropdownList).labelKey,
27
+ valueKey: getDefault<UDropdownListProps>(defaultConfig, UDropdownList).valueKey,
28
+ disabled: getDefault<UDropdownListProps>(defaultConfig, UDropdownList).disabled,
29
+ size: getDefault<UDropdownListProps>(defaultConfig, UDropdownList).size,
30
+ visibleOptions: getDefault<UDropdownListProps>(defaultConfig, UDropdownList).visibleOptions,
195
31
  });
196
32
 
197
33
  const emit = defineEmits([
@@ -232,7 +68,7 @@ const {
232
68
  const { tm } = useLocale();
233
69
 
234
70
  const i18nGlobal = tm(UDropdownList);
235
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
71
+ const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props?.config?.i18n));
236
72
 
237
73
  const addOptionKeyCombination = computed(() => {
238
74
  return isMac ? "(⌘ + Enter)" : "(Ctrl + Enter)";
@@ -349,3 +185,102 @@ defineExpose({
349
185
  wrapperRef,
350
186
  });
351
187
  </script>
188
+
189
+ <template>
190
+ <div
191
+ ref="wrapperRef"
192
+ tabindex="1"
193
+ :style="{ maxHeight: wrapperHeight }"
194
+ v-bind="wrapperAttrs"
195
+ @keydown.self.down.prevent="pointerForward"
196
+ @keydown.self.up.prevent="pointerBackward"
197
+ @keydown.enter.stop.self="addPointerElement"
198
+ >
199
+ <ul :id="`listbox-${elementId}`" v-bind="listAttrs" role="listbox">
200
+ <li
201
+ v-for="(option, index) of options"
202
+ :id="`${elementId}-${index}`"
203
+ :key="index"
204
+ v-bind="listItemAttrs"
205
+ ref="optionsRef"
206
+ :role="!(option && option.groupLabel) ? 'option' : null"
207
+ :data-group-label="Boolean(option.groupLabel)"
208
+ >
209
+ <!-- option title -->
210
+ <span
211
+ v-if="!(option && (option.groupLabel || option.isSubGroup)) && !option.isHidden"
212
+ v-bind="optionAttrs"
213
+ :class="optionHighlight(index, option)"
214
+ @click="select(option), onClickOption(option)"
215
+ @mouseenter.self="pointerSet(index)"
216
+ >
217
+ <!--
218
+ @slot Use it to add something before the option.
219
+ @binding {object} option
220
+ @binding {number} index
221
+ -->
222
+ <slot name="before-option" :option="option" :index="index" />
223
+
224
+ <!--
225
+ @slot Use it to add something instead of the option.
226
+ @binding {object} option
227
+ @binding {number} index
228
+ -->
229
+ <slot name="option" :option="option" :index="index">
230
+ <span
231
+ :style="getMarginForSubCategory(option.level)"
232
+ v-bind="optionContentAttrs"
233
+ v-text="option[labelKey]"
234
+ />
235
+ </slot>
236
+
237
+ <!--
238
+ @slot Use it to add something after the option.
239
+ @binding {object} option
240
+ @binding {number} index
241
+ -->
242
+ <slot name="after-option" :option="option" :index="index" />
243
+ </span>
244
+
245
+ <!-- group title -->
246
+ <template v-if="option && (option.groupLabel || option.isSubGroup) && !option.isHidden">
247
+ <div v-if="option.groupLabel" v-bind="groupAttrs" v-text="option.groupLabel" />
248
+
249
+ <div
250
+ v-else-if="option.isSubGroup"
251
+ :style="getMarginForSubCategory(option.level)"
252
+ v-bind="subGroupAttrs"
253
+ v-text="option[labelKey]"
254
+ />
255
+ </template>
256
+ </li>
257
+
258
+ <div v-if="!options.length" v-bind="optionAttrs">
259
+ <!-- @slot Use it to add something instead of empty state. -->
260
+ <slot name="empty">
261
+ <span v-bind="optionContentAttrs" v-text="currentLocale.noDataToShow" />
262
+ </slot>
263
+ </div>
264
+
265
+ <!-- Add button -->
266
+ <template v-if="addOption">
267
+ <div v-bind="addOptionLabelWrapperAttrs" @click="onClickAddOption">
268
+ <div v-bind="addOptionLabelAttrs">
269
+ {{ currentLocale.add }}
270
+ <span v-bind="addOptionLabelHotkeyAttrs" v-text="addOptionKeyCombination" />
271
+ </div>
272
+ </div>
273
+
274
+ <UButton round square v-bind="addOptionButtonAttrs" @click="onClickAddOption">
275
+ <UIcon
276
+ internal
277
+ color="white"
278
+ size="xs"
279
+ :name="config.defaults.addOptionIcon"
280
+ v-bind="addOptionIconAttrs"
281
+ />
282
+ </UButton>
283
+ </template>
284
+ </ul>
285
+ </div>
286
+ </template>
@@ -1,8 +1,8 @@
1
1
  import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/blocks";
2
2
  import { getSource } from "../../utils/storybook.ts";
3
3
 
4
- import * as stories from "./stories.js";
5
- import defaultConfig from "../config.js?raw"
4
+ import * as stories from "./stories.ts";
5
+ import defaultConfig from "../config.ts?raw"
6
6
 
7
7
  <Meta of={stories} />
8
8
  <Title of={stories} />
@@ -3,6 +3,15 @@ import { getArgTypes, getSlotNames, getSlotsFragment } from "../../utils/storybo
3
3
  import UDropdownList from "../../ui.dropdown-list/UDropdownList.vue";
4
4
  import URow from "../../ui.container-row/URow.vue";
5
5
 
6
+ import type { Meta, StoryFn } from "@storybook/vue3";
7
+ import type { UDropdownListProps } from "../types.ts";
8
+
9
+ interface UDropdownListArgs extends UDropdownListProps {
10
+ slotTemplate?: string;
11
+ enum: "size";
12
+ maxHeight: number;
13
+ }
14
+
6
15
  /**
7
16
  * The `UDropdownList` component. | [View on GitHub](https://github.com/vuelessjs/vueless/tree/main/src/ui.dropdown-list)
8
17
  */
@@ -27,9 +36,9 @@ export default {
27
36
  },
28
37
  },
29
38
  },
30
- };
39
+ } as Meta;
31
40
 
32
- const DefaultTemplate = (args) => ({
41
+ const DefaultTemplate: StoryFn<UDropdownListArgs> = (args: UDropdownListArgs) => ({
33
42
  components: { UDropdownList },
34
43
  setup() {
35
44
  const slots = getSlotNames(UDropdownList.__name);
@@ -38,17 +47,20 @@ const DefaultTemplate = (args) => ({
38
47
  },
39
48
  template: `
40
49
  <UDropdownList v-bind="args" class="mx-4 w-[24rem]">
41
- ${args.slotTemplate || getSlotsFragment()}
50
+ ${args.slotTemplate || getSlotsFragment("")}
42
51
  </UDropdownList>
43
52
  `,
44
53
  });
45
54
 
46
- const EnumVariantTemplate = (args, { argTypes }) => ({
55
+ const EnumVariantTemplate: StoryFn<UDropdownListArgs> = (
56
+ args: UDropdownListArgs,
57
+ { argTypes },
58
+ ) => ({
47
59
  components: { UDropdownList, URow },
48
60
  setup() {
49
61
  return {
50
62
  args,
51
- options: argTypes[args.enum].options,
63
+ options: argTypes?.[args.enum]?.options,
52
64
  };
53
65
  },
54
66
  template: `
@@ -82,7 +94,11 @@ OptionSettings.args = {
82
94
  options: [
83
95
  { label: "option 1", id: "1" },
84
96
  { label: "option 2", id: "2", isHidden: true },
85
- // eslint-disable-next-line no-console
86
- { label: "option 3", id: "3", onClick: (option) => console.log("onClick option 3 ", option) },
97
+
98
+ {
99
+ label: "option 3",
100
+ id: "3",
101
+ onClick: (option: object) => alert("onClick option 3 " + JSON.stringify(option)),
102
+ },
87
103
  ],
88
104
  };
@@ -0,0 +1,52 @@
1
+ import defaultConfig from "./config.ts";
2
+
3
+ import type { UnknownObject } from "../types.ts";
4
+
5
+ export type Config = Partial<typeof defaultConfig>;
6
+
7
+ export interface UDropdownListProps {
8
+ /**
9
+ * List of options.
10
+ */
11
+ options?: UnknownObject[];
12
+
13
+ /**
14
+ * Label key in the item object of options.
15
+ */
16
+ labelKey?: string;
17
+
18
+ /**
19
+ * Value key in the item object of options.
20
+ */
21
+ valueKey?: string;
22
+
23
+ /**
24
+ * Show add option button.
25
+ */
26
+ addOption?: boolean;
27
+
28
+ /**
29
+ * Disable the list.
30
+ */
31
+ disabled?: boolean;
32
+
33
+ /**
34
+ * List size.
35
+ */
36
+ size?: "sm" | "md" | "lg";
37
+
38
+ /**
39
+ * Number of options to show without a scroll.
40
+ */
41
+ visibleOptions?: number;
42
+
43
+ /**
44
+ * Unique element id.
45
+ */
46
+ id?: string;
47
+
48
+ /**
49
+ * Component config object.
50
+ */
51
+ config?: Partial<typeof defaultConfig>;
52
+ }
@@ -1,10 +1,13 @@
1
1
  import { computed } from "vue";
2
2
  import useUI from "../composables/useUI.ts";
3
3
 
4
- import defaultConfig from "./config.js";
4
+ import defaultConfig from "./config.ts";
5
5
 
6
- export default function useAttrs(props) {
7
- const { config, getKeysAttrs, hasSlotContent, getExtendingKeysClasses } = useUI(
6
+ import type { UseAttrs } from "../types.ts";
7
+ import type { UDropdownListProps, Config } from "./types.ts";
8
+
9
+ export default function useAttrs(props: UDropdownListProps): UseAttrs<Config> {
10
+ const { config, getKeysAttrs, hasSlotContent, getExtendingKeysClasses } = useUI<Config>(
8
11
  defaultConfig,
9
12
  () => props.config,
10
13
  );
@@ -0,0 +1,111 @@
1
+ import { ref, computed, toValue } from "vue";
2
+ import type { Ref } from "vue";
3
+
4
+ interface Option {
5
+ isSubGroup?: boolean;
6
+ groupLabel?: string;
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ export default function usePointer(
11
+ options: Option[],
12
+ optionElements: Ref<HTMLElement[]>,
13
+ listWrapperElement: Ref<HTMLElement | null>,
14
+ ) {
15
+ const pointer = ref<number | null>(null);
16
+ const pointerDirty = ref<boolean>(false);
17
+
18
+ const activeElementHeight = computed<number>(() => {
19
+ if (pointer.value === null) return 0;
20
+ const element = toValue(optionElements)[pointer.value];
21
+ const isGroupLabel = element.dataset.groupLabel;
22
+ const groupIndex = 2;
23
+
24
+ if (isGroupLabel) {
25
+ return toValue(optionElements)[pointer.value - groupIndex].getBoundingClientRect().height;
26
+ }
27
+
28
+ return element.getBoundingClientRect().height;
29
+ });
30
+
31
+ const pointerPosition = computed<number>(() => {
32
+ return (pointer.value !== null ? pointer.value : 0) * activeElementHeight.value || 0;
33
+ });
34
+
35
+ function scrollWrapperToElement() {
36
+ if (!listWrapperElement.value) return;
37
+ const visibleElements =
38
+ listWrapperElement.value.getBoundingClientRect().height / activeElementHeight.value;
39
+ const currentElement = visibleElements - 1;
40
+ const currentPointerPosition =
41
+ pointerPosition.value - currentElement * activeElementHeight.value;
42
+
43
+ if (listWrapperElement.value.scrollTop <= currentPointerPosition) {
44
+ listWrapperElement.value.scrollTop = currentPointerPosition;
45
+ }
46
+ }
47
+
48
+ function pointerForward() {
49
+ if (pointer.value === null) pointer.value = 0;
50
+
51
+ if (pointer.value < options.length - 1) {
52
+ pointer.value++;
53
+
54
+ const isGroup = options[pointer.value]?.isSubGroup || options[pointer.value]?.groupLabel;
55
+
56
+ if (listWrapperElement.value) scrollWrapperToElement();
57
+
58
+ if (options[pointer.value] && isGroup) {
59
+ pointerForward();
60
+ }
61
+ }
62
+
63
+ pointerDirty.value = true;
64
+ }
65
+
66
+ function pointerBackward() {
67
+ if (pointer.value === null) pointer.value = 0;
68
+
69
+ if (pointer.value > 0) {
70
+ pointer.value--;
71
+
72
+ const isGroup = options[pointer.value]?.isSubGroup || options[pointer.value]?.groupLabel;
73
+
74
+ if (listWrapperElement.value && listWrapperElement.value.scrollTop >= pointerPosition.value) {
75
+ listWrapperElement.value.scrollTop = pointerPosition.value;
76
+ }
77
+
78
+ if (options[pointer.value] && isGroup) {
79
+ pointerBackward();
80
+ }
81
+ } else {
82
+ if (options[0]?.groupLabel) {
83
+ pointerForward();
84
+ }
85
+ }
86
+
87
+ pointerDirty.value = true;
88
+ }
89
+
90
+ function pointerSet(index: number) {
91
+ pointer.value = index;
92
+ pointerDirty.value = true;
93
+ }
94
+
95
+ function pointerReset() {
96
+ pointer.value = 0;
97
+
98
+ if (listWrapperElement.value) {
99
+ listWrapperElement.value.scrollTop = 0;
100
+ }
101
+ }
102
+
103
+ return {
104
+ pointer,
105
+ pointerDirty,
106
+ pointerSet,
107
+ pointerReset,
108
+ pointerBackward,
109
+ pointerForward,
110
+ };
111
+ }
@@ -61,7 +61,7 @@ const mathSign = computed(() => {
61
61
 
62
62
  const preparedMoney = computed(() => {
63
63
  return separatedMoney(
64
- Math.abs(props.value),
64
+ Math.abs(props.value || 0),
65
65
  props.minFractionDigits,
66
66
  props.maxFractionDigits,
67
67
  props.decimalSeparator,
@@ -17,13 +17,13 @@ export default /*tw*/ {
17
17
  sum: {
18
18
  variants: {
19
19
  size: {
20
- sm: "text-xs",
21
- md: "text-sm",
22
- lg: "text-base",
23
- xl: "text-lg",
24
- "2xl": "text-xl",
25
- "3xl": "text-2xl",
26
- "4xl": "text-3xl",
20
+ xs: "text-xs",
21
+ sm: "text-sm",
22
+ md: "text-base",
23
+ lg: "text-lg",
24
+ xl: "text-xl",
25
+ "2xl": "text-2xl",
26
+ "3xl": "text-3xl",
27
27
  },
28
28
  planned: {
29
29
  true: "opacity-75 before:content-['('] after:content-[')']",
@@ -38,12 +38,11 @@ export default /*tw*/ {
38
38
  size: {
39
39
  xs: "text-2xs",
40
40
  sm: "text-xs",
41
- md: "text-xs",
42
- lg: "text-sm",
43
- xl: "text-base",
44
- "2xl": "text-lg",
45
- "3xl": "text-xl",
46
- "4xl": "text-2xl",
41
+ md: "text-sm",
42
+ lg: "text-base",
43
+ xl: "text-lg",
44
+ "2xl": "text-xl",
45
+ "3xl": "text-2xl",
47
46
  },
48
47
  },
49
48
  },