@saasmakers/ui 0.1.107 → 0.1.108

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 (33) hide show
  1. package/app/components/bases/BaseAlert.vue +13 -10
  2. package/app/components/bases/BaseAvatar.vue +27 -2
  3. package/app/components/bases/BaseCharacter.vue +8 -7
  4. package/app/components/bases/BaseChart.vue +21 -19
  5. package/app/components/bases/BaseEmoji.vue +11 -2
  6. package/app/components/bases/BaseHeading.stories.ts +1 -1
  7. package/app/components/bases/BaseHeading.vue +7 -1
  8. package/app/components/bases/BaseIcon.vue +41 -25
  9. package/app/components/bases/BaseMessage.vue +4 -6
  10. package/app/components/bases/BaseMetric.vue +9 -8
  11. package/app/components/bases/BaseOverlay.vue +17 -3
  12. package/app/components/bases/BaseParagraph.stories.ts +1 -1
  13. package/app/components/bases/BaseParagraph.vue +7 -1
  14. package/app/components/bases/BaseQuote.stories.ts +1 -1
  15. package/app/components/bases/BaseQuote.vue +19 -17
  16. package/app/components/bases/BaseSpinner.vue +10 -1
  17. package/app/components/bases/BaseTag.vue +21 -1
  18. package/app/components/bases/BaseTags.vue +3 -4
  19. package/app/components/bases/BaseToast.vue +10 -1
  20. package/app/components/fields/FieldDays.vue +2 -1
  21. package/app/components/fields/FieldEmojis.vue +3 -3
  22. package/app/components/fields/FieldLabel.vue +24 -8
  23. package/app/components/fields/FieldMessage.vue +60 -92
  24. package/app/components/fields/FieldSelect.vue +33 -8
  25. package/app/components/fields/FieldTime.vue +12 -1
  26. package/app/composables/useChartist.ts +1 -1
  27. package/app/composables/useDevice.ts +1 -0
  28. package/app/composables/useLayerUtils.ts +16 -16
  29. package/app/composables/useToasts.ts +1 -1
  30. package/app/composables/useTranslation.ts +1 -1
  31. package/app/types/bases.d.ts +2 -0
  32. package/nuxt.config.ts +6 -6
  33. package/package.json +17 -13
@@ -40,10 +40,9 @@ const { getIcon } = useLayerIcons()
40
40
 
41
41
  const sortedTags = computed({
42
42
  get() {
43
- return props.tags
44
- .slice()
43
+ return [...props.tags]
45
44
  .filter((_tag, tagIndex) => tagIndex < props.maxTags || showingAllTags.value)
46
- .sort((tagA, tagB) => {
45
+ .toSorted((tagA, tagB) => {
47
46
  return (tagA.order ?? 0) < (tagB.order ?? 0) ? -1 : 0
48
47
  })
49
48
  },
@@ -54,7 +53,7 @@ const sortedTags = computed({
54
53
  id: tag.id,
55
54
  order,
56
55
  }))
57
- .map(tag => ({
56
+ .map((tag) => ({
58
57
  id: tag.id,
59
58
  order: tag.order,
60
59
  }))
@@ -19,16 +19,25 @@ function onClose(event: MouseEvent) {
19
19
  emit('close', event, props)
20
20
  }
21
21
  }
22
+
23
+ function onKeydown(event: KeyboardEvent) {
24
+ if ((event.key === 'Enter' || event.key === ' ') && props.hasClose) {
25
+ onClose(event as unknown as MouseEvent)
26
+ }
27
+ }
22
28
  </script>
23
29
 
24
30
  <template>
25
31
  <div
26
32
  class="group flex select-none items-center rounded-full bg-gray-900 px-3 py-2 text-base text-white font-normal dark:bg-gray-100 dark:text-black"
27
33
  :class="{ 'cursor-pointer': hasClose }"
34
+ role="button"
35
+ tabindex="0"
28
36
  @click="onClose"
37
+ @keydown.prevent="onKeydown"
29
38
  >
30
39
  <BaseIcon
31
- class="flex-initial"
40
+ class="pointer-events-none flex-initial"
32
41
  :status="status"
33
42
  :text="text"
34
43
  />
@@ -49,7 +49,8 @@ function onUpdateDays(event: MouseEvent, day: number) {
49
49
 
50
50
  if (days.includes(day)) {
51
51
  days.splice(days.indexOf(day), 1)
52
- } else {
52
+ }
53
+ else {
53
54
  days.push(day)
54
55
  }
55
56
 
@@ -31,7 +31,7 @@ const emojisFiltered = computed(() => {
31
31
  const englishKeywords = (emoji.keywords.en ?? [])
32
32
  const allKeywords = [...localizedKeywords, ...englishKeywords]
33
33
 
34
- return allKeywords.some(keyword => normalizeText(keyword).includes(searchQueryCleaned))
34
+ return allKeywords.some((keyword) => normalizeText(keyword).includes(searchQueryCleaned))
35
35
  })
36
36
 
37
37
  return {
@@ -39,7 +39,7 @@ const emojisFiltered = computed(() => {
39
39
  emojis: filtered,
40
40
  }
41
41
  })
42
- .filter(category => category.emojis.length > 0)
42
+ .filter((category) => category.emojis.length > 0)
43
43
  })
44
44
 
45
45
  function onEmojiClick(event: MouseEvent, emoji?: string) {
@@ -69,7 +69,7 @@ function onEmojiClick(event: MouseEvent, emoji?: string) {
69
69
  :key="category.category"
70
70
  >
71
71
  <div
72
- v-if="category.emojis.length"
72
+ v-if="category.emojis.length > 0"
73
73
  class="mt-4"
74
74
  >
75
75
  <div class="mb-4">
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import type { FieldLabel } from '../../types/fields'
3
3
 
4
- withDefaults(defineProps<FieldLabel>(), {
4
+ const props = withDefaults(defineProps<FieldLabel>(), {
5
5
  disabled: false,
6
6
  forField: '',
7
7
  hasMarginBottom: false,
@@ -19,21 +19,34 @@ const emit = defineEmits<{
19
19
  click: [event: MouseEvent]
20
20
  }>()
21
21
 
22
+ const isClickable = computed(() => {
23
+ return !props.loading && !props.disabled
24
+ })
25
+
22
26
  function onClick(event: MouseEvent) {
23
- emit('click', event)
27
+ if (isClickable.value) {
28
+ emit('click', event)
29
+ }
30
+ }
31
+
32
+ function onKeydown(event: KeyboardEvent) {
33
+ if (event.key === 'Enter' || event.key === ' ') {
34
+ onClick(event as unknown as MouseEvent)
35
+ }
24
36
  }
25
37
  </script>
26
38
 
27
39
  <template>
28
- <label
40
+ <component
41
+ :is="forField ? 'label' : (isClickable ? 'button' : 'span')"
29
42
  class="flex select-none items-center font-semibold tracking-tight"
30
43
  :class="{
31
44
  'field-disabled': disabled,
32
- 'cursor-pointer': !loading && !disabled,
45
+ 'cursor-pointer': isClickable,
33
46
  'cursor-wait': loading,
34
47
  'text-gray-900 dark:text-gray-100': !lineThrough,
35
48
  'text-gray-500 dark:text-gray-500 line-through': lineThrough,
36
- 'hover:text-black dark:hover:text-white': !disabled && !lineThrough && !loading,
49
+ 'hover:text-black dark:hover:text-white': isClickable && !lineThrough,
37
50
  'w-0 truncate': truncate,
38
51
  'mb-1.5': size === 'xs' && hasMarginBottom,
39
52
  'mb-2': size === 'sm' && hasMarginBottom,
@@ -44,8 +57,11 @@ function onClick(event: MouseEvent) {
44
57
  'ml-2': size === 'base' && hasMarginLeft,
45
58
  'ml-2.5': size === 'lg' && hasMarginLeft,
46
59
  }"
47
- :for="forField"
48
- @click="onClick"
60
+ :for="forField || undefined"
61
+ :tabindex="isClickable && !forField ? 0 : undefined"
62
+ :type="isClickable && !forField ? 'button' : undefined"
63
+ @click="isClickable ? onClick : undefined"
64
+ @keydown.prevent="isClickable ? onKeydown : undefined"
49
65
  >
50
66
  <BaseIcon
51
67
  :icon="icon"
@@ -59,5 +75,5 @@ function onClick(event: MouseEvent) {
59
75
  :size="size"
60
76
  text="*"
61
77
  />
62
- </label>
78
+ </component>
63
79
  </template>
@@ -15,105 +15,73 @@ const { t } = useI18n()
15
15
  const validationMessage = computed(() => {
16
16
  // Available rules
17
17
  // https://vuelidate.js.org/#sub-builtin-validators
18
- let message = ''
19
-
20
18
  if (props.validation && props.validation.$dirty) {
21
19
  // Required, Required If, Required Unless
22
- if (props.validation.required === false || props.validation.requiredIf === false || props.validation.requiredUnless === false) {
23
- message = t('required')
24
- }
25
- // Min Length
26
- else if (props.validation.minLength === false) {
27
- const min = props.validation.$params.minLength.min
28
-
29
- message = t('minLength', { min })
30
- }
31
- // Max Length
32
- else if (props.validation.maxLength === false) {
33
- const max = props.validation.$params.maxLength.max
34
-
35
- message = t('maxLength', { max })
36
- }
37
- // Min Value
38
- else if (props.validation.minValue === false) {
39
- const min = props.validation.$params.minValue.min
40
-
41
- message = t('minValue', { min })
42
- }
43
- // Max Value
44
- else if (props.validation.maxValue === false) {
45
- const max = props.validation.$params.maxValue.max
46
-
47
- message = t('maxValue', { max })
48
- }
49
- // Between
50
- else if (props.validation.between === false) {
51
- const min = props.validation.$params.between.min
52
- const max = props.validation.$params.between.max
53
-
54
- message = t('between', {
55
- max,
56
- min,
57
- })
58
- }
59
- // Alpha
60
- else if (props.validation.alpha === false) {
61
- message = t('alpha')
62
- }
63
- // Alpha Num
64
- else if (props.validation.alphaNum === false) {
65
- message = t('alphaNum')
66
- }
67
- // Numeric
68
- else if (props.validation.numeric === false) {
69
- message = t('numeric')
70
- }
71
- // Integer
72
- else if (props.validation.integer === false) {
73
- message = t('integer')
74
- }
75
- // Integer
76
- else if (props.validation.decimal === false) {
77
- message = t('decimal')
78
- }
79
- // Email
80
- else if (props.validation.email === false) {
81
- message = t('email')
82
- }
83
- // IP Address
84
- else if (props.validation.ipAddress === false) {
85
- message = t('ipAddress')
86
- }
87
- // Mac Address
88
- else if (props.validation.macAddress === false) {
89
- message = t('maxAddress')
90
- }
91
- // Same As
92
- else if (props.validation.sameAs === false) {
93
- const field = props.validation.$params.sameAs.eq
94
-
95
- message = t('sameAs', { field })
96
- }
97
- // Url
98
- else if (props.validation.url === false) {
99
- message = t('url')
100
- }
101
- // Other rules
102
- else if (props.validation.$invalid === true) {
103
- message = t('invalid')
20
+ switch (false) {
21
+ case props.validation.alpha: {
22
+ return t('alpha')
23
+ }
24
+ case props.validation.alphaNum: {
25
+ return t('alphaNum')
26
+ }
27
+ case props.validation.between: {
28
+ return t('between', {
29
+ max: props.validation.$params.between.max,
30
+ min: props.validation.$params.between.min,
31
+ })
32
+ }
33
+ case props.validation.decimal: {
34
+ return t('decimal')
35
+ }
36
+ case props.validation.email: {
37
+ return t('email')
38
+ }
39
+ case props.validation.integer: {
40
+ return t('integer')
41
+ }
42
+ case props.validation.ipAddress: {
43
+ return t('ipAddress')
44
+ }
45
+ case props.validation.macAddress: {
46
+ return t('maxAddress')
47
+ }
48
+ case props.validation.maxLength: {
49
+ return t('maxLength', { max: props.validation.$params.maxLength.max })
50
+ }
51
+ case props.validation.maxValue: {
52
+ return t('maxValue', { max: props.validation.$params.maxValue.max })
53
+ }
54
+ case props.validation.minLength: {
55
+ return t('minLength', { min: props.validation.$params.minLength.min })
56
+ }
57
+ case props.validation.minValue: {
58
+ return t('minValue', { min: props.validation.$params.minValue.min })
59
+ }
60
+ case props.validation.numeric: {
61
+ return t('numeric')
62
+ }
63
+ case props.validation.required:
64
+ case props.validation.requiredIf:
65
+ case props.validation.requiredUnless: {
66
+ return t('required')
67
+ }
68
+ case props.validation.sameAs: {
69
+ return t('sameAs', { field: props.validation.$params.sameAs.eq })
70
+ }
71
+ case props.validation.url: {
72
+ return t('url')
73
+ }
74
+ default: {
75
+ if (props.validation.$invalid === true) {
76
+ return t('invalid')
77
+ }
78
+ }
104
79
  }
105
80
  }
106
-
107
- return message
108
81
  })
109
82
 
110
83
  const status = computed(() => {
111
- if (validationMessage.value && !props.hideError) {
112
- return 'error'
113
- }
114
- else {
115
- return 'default'
116
- }
84
+ return validationMessage.value && !props.hideError ? 'error' : 'default'
117
85
  })
118
86
 
119
87
  const text = computed<BaseTextText>(() => {
@@ -37,7 +37,7 @@ const modelValue = defineModel<FieldSelect['modelValue']>({ default: '' })
37
37
  const opened = ref(false)
38
38
 
39
39
  const computedColumns = computed(() => {
40
- if (props.columns.length) {
40
+ if (props.columns.length > 0) {
41
41
  return props.columns
42
42
  }
43
43
 
@@ -48,7 +48,7 @@ const computedOptions = computed(() => {
48
48
  const options: FieldSelectOption[] = []
49
49
 
50
50
  // Add index to each option
51
- if (props.columns.length) {
51
+ if (props.columns.length > 0) {
52
52
  for (const column of props.columns) {
53
53
  for (const columnOption of column.options) {
54
54
  options.push(columnOption)
@@ -70,6 +70,12 @@ const selectedOption = computed(() => {
70
70
  })
71
71
  })
72
72
 
73
+ function onBlur() {
74
+ if (props.openOnHover && !props.disabled) {
75
+ opened.value = false
76
+ }
77
+ }
78
+
73
79
  function onClose() {
74
80
  reset()
75
81
  }
@@ -90,6 +96,12 @@ function onContainerKeypress(event: KeyboardEvent) {
90
96
  }
91
97
  }
92
98
 
99
+ function onFocus() {
100
+ if (props.openOnHover && !props.disabled) {
101
+ opened.value = true
102
+ }
103
+ }
104
+
93
105
  function onLabelClick() {
94
106
  if (!props.disabled) {
95
107
  opened.value = !opened.value
@@ -111,17 +123,23 @@ function onMouseLeave() {
111
123
  function onOptionClick(event: MouseEvent, option: FieldSelectOption) {
112
124
  if (!props.disabled) {
113
125
  // Check that the option is not currently selected
114
- if ((selectedOption.value || {}).value !== option.value) {
115
- selectOption(event, option.value)
126
+ if ((selectedOption.value || {}).value === option.value) {
127
+ reset()
116
128
  }
117
129
  else {
118
- reset()
130
+ selectOption(event, option.value)
119
131
  }
120
132
 
121
133
  emit('optionClick', event, option.value)
122
134
  }
123
135
  }
124
136
 
137
+ function onOptionKeydown(event: KeyboardEvent, option: FieldSelectOption) {
138
+ if ((event.key === 'Enter' || event.key === ' ') && !props.disabled) {
139
+ onOptionClick(event as unknown as MouseEvent, option)
140
+ }
141
+ }
142
+
125
143
  function reset() {
126
144
  opened.value = false
127
145
  }
@@ -155,8 +173,6 @@ function selectOption(event: MouseEvent, value: string) {
155
173
  'field-disabled': disabled,
156
174
  'cursor-pointer': !disabled,
157
175
  }"
158
- @mouseenter="onMouseEnter"
159
- @mouseleave="onMouseLeave"
160
176
  >
161
177
  <div
162
178
  class="relative"
@@ -177,9 +193,15 @@ function selectOption(event: MouseEvent, value: string) {
177
193
  'text-sm h-12': size === 'base',
178
194
  'text-base h-14': size === 'lg',
179
195
  }"
180
- :tabindex="disabled ? -1 : 0"
196
+ role="button"
197
+ :aria-disabled="disabled"
198
+ tabindex="0"
181
199
  @click="onContainerClick"
182
200
  @keypress.prevent="onContainerKeypress"
201
+ @mouseenter="onMouseEnter"
202
+ @mouseleave="onMouseLeave"
203
+ @focusin="onFocus"
204
+ @focusout="onBlur"
183
205
  >
184
206
  <template v-if="selectedOption">
185
207
  <BaseIcon
@@ -258,7 +280,10 @@ function selectOption(event: MouseEvent, value: string) {
258
280
  'text-gray-900 dark:text-gray-100': selectedOption && option.value === selectedOption.value,
259
281
  'bg-white dark:bg-gray-900': selectedOption && option.value !== selectedOption.value,
260
282
  }"
283
+ role="button"
284
+ tabindex="0"
261
285
  @click="onOptionClick($event, option)"
286
+ @keydown.prevent="onOptionKeydown($event, option)"
262
287
  >
263
288
  <BaseIcon
264
289
  v-if="option.text"
@@ -29,7 +29,14 @@ const inputRef = ref<HTMLInputElement>()
29
29
  function onContainerClick() {
30
30
  if (!props.disabled && inputRef.value) {
31
31
  inputRef.value.focus()
32
- inputRef.value.showPicker?.()
32
+ inputRef.value.showPicker()
33
+ }
34
+ }
35
+
36
+ function onContainerKeydown(event: KeyboardEvent) {
37
+ if ((event.key === 'Enter' || event.key === ' ') && !props.disabled && inputRef.value) {
38
+ inputRef.value.focus()
39
+ inputRef.value.showPicker()
33
40
  }
34
41
  }
35
42
 
@@ -63,7 +70,11 @@ function onFieldBlur(event: FocusEvent) {
63
70
  'text-sm h-12': size === 'base',
64
71
  'text-base h-14': size === 'lg',
65
72
  }"
73
+ role="button"
74
+ :aria-disabled="disabled"
75
+ tabindex="0"
66
76
  @click="onContainerClick"
77
+ @keydown.prevent="onContainerKeydown"
67
78
  >
68
79
  <BaseIcon
69
80
  :icon="getIcon('clock')"
@@ -50,7 +50,7 @@ export default function useChartist() {
50
50
  // Clean up the dash attributes after it finishes
51
51
  const total = delay + duration + (stagger ? stagger * (lineCtx.seriesIndex ?? 0) : 0)
52
52
 
53
- window.setTimeout(() => {
53
+ globalThis.setTimeout(() => {
54
54
  lineCtx.element.attr({
55
55
  'stroke-dasharray': undefined,
56
56
  'stroke-dashoffset': undefined,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable n/no-unsupported-features/node-builtins */
1
2
  export default function useDevice() {
2
3
  const isDesktopBrowser = computed(() => {
3
4
  if (typeof navigator === 'undefined') {
@@ -1,24 +1,24 @@
1
1
  import numbroLib from 'numbro'
2
2
 
3
- export default function useLayerUtils() {
4
- const normalizeText = (text: string) => {
5
- return text
6
- .toLowerCase()
7
- .normalize('NFD')
8
- .replace(/[\u0300-\u036F]/g, '')
9
- }
10
-
11
- const numbro = (number: '∞' | number | undefined, format?: string) => {
12
- if (!number && number !== 0) {
13
- return ''
14
- }
15
- else if (number === '∞' || number % 1 !== 0) {
16
- return `${number}`
17
- }
3
+ const normalizeText = (text: string) => {
4
+ return text
5
+ .toLowerCase()
6
+ .normalize('NFD')
7
+ .replaceAll(/[\u0300-\u036F]/g, '')
8
+ }
18
9
 
19
- return numbroLib(number).format(format || '0,0')
10
+ const numbro = (number: '∞' | number | undefined, format?: string) => {
11
+ if (!number && number !== 0) {
12
+ return ''
20
13
  }
14
+ else if (number === '∞' || number % 1 !== 0) {
15
+ return `${number}`
16
+ }
17
+
18
+ return numbroLib(number).format(format || '0,0')
19
+ }
21
20
 
21
+ export default function useLayerUtils() {
22
22
  return {
23
23
  normalizeText,
24
24
  numbro,
@@ -20,7 +20,7 @@ export default function useToast() {
20
20
  const createToast = (message: string, status: BaseStatus = 'success', i18nParams?: Record<string, number | string>) => {
21
21
  if (import.meta.client) {
22
22
  const toast: BaseToast = {
23
- id: Math.floor((1 + Math.random()) * 0x10000).toString(16),
23
+ id: Math.floor((1 + Math.random()) * 0x1_00_00).toString(16),
24
24
  status: status || 'success',
25
25
  text: message.includes(' ') ? message : nuxtApp.$i18n.t(`toasts.${message}`, i18nParams || {}),
26
26
  }
@@ -35,7 +35,7 @@ export default function useTranslation(slots: SetupContext['slots'], locale: Ref
35
35
  const endPosition = content.indexOf(separator, startPosition)
36
36
 
37
37
  content = content
38
- .substring(startPosition, endPosition > 0 ? endPosition : content.length)
38
+ .slice(startPosition, endPosition > 0 ? endPosition : content.length)
39
39
  .trim()
40
40
 
41
41
  // Markdown parser
@@ -141,6 +141,7 @@ export interface BaseHeading {
141
141
  alignment?: BaseAlignment
142
142
  size?: BaseHeadingSize
143
143
  tag?: BaseHeadingTag
144
+ text?: BaseTextText
144
145
  }
145
146
 
146
147
  export type BaseHeadingSize
@@ -217,6 +218,7 @@ export type BaseOverlayPosition = 'absolute' | 'fixed'
217
218
  export interface BaseParagraph {
218
219
  alignment?: BaseAlignment
219
220
  size?: BaseParagraphSize
221
+ text?: BaseTextText
220
222
  }
221
223
 
222
224
  export type BaseParagraphSize = 'base' | 'lg' | 'sm'
package/nuxt.config.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { dirname, join } from 'node:path'
1
+ import path from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { defineNuxtConfig } from 'nuxt/config'
4
- import uno from './uno.config'
4
+ import uno from './uno.config.js'
5
5
 
6
- const currentDir = dirname(fileURLToPath(import.meta.url))
6
+ const currentDir = path.dirname(fileURLToPath(import.meta.url))
7
7
 
8
8
  export default defineNuxtConfig({
9
9
  // --> BUILD <--
@@ -44,9 +44,9 @@ export default defineNuxtConfig({
44
44
 
45
45
  css: [
46
46
  '@unocss/reset/tailwind.css',
47
- join(currentDir, './app/assets/styles/chartist.css'),
48
- join(currentDir, './app/assets/styles/colors.css'),
49
- join(currentDir, './app/assets/styles/v-popper.css'),
47
+ path.join(currentDir, './app/assets/styles/chartist.css'),
48
+ path.join(currentDir, './app/assets/styles/colors.css'),
49
+ path.join(currentDir, './app/assets/styles/v-popper.css'),
50
50
  ],
51
51
 
52
52
  modules: [
package/package.json CHANGED
@@ -1,30 +1,27 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "type": "module",
4
- "version": "0.1.107",
3
+ "version": "0.1.108",
5
4
  "private": false,
6
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
7
- "license": "MIT",
8
6
  "repository": {
9
7
  "type": "git",
10
8
  "url": "git+https://github.com/saasmakers/saasmakers-turborepo.git",
11
9
  "directory": "packages/ui"
12
10
  },
11
+ "license": "MIT",
12
+ "author": "SaaS Makers",
13
+ "sideEffects": false,
14
+ "type": "module",
15
+ "exports": {
16
+ ".": "./nuxt.config.ts"
17
+ },
13
18
  "main": "nuxt.config.ts",
14
19
  "types": "app/types/global.d.ts",
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
20
  "files": [
19
21
  "app",
20
- "nuxt.config.ts",
21
22
  "public",
22
23
  "uno.config.ts"
23
24
  ],
24
- "peerDependencies": {
25
- "@saasmakers/shared": "^0.1.13",
26
- "nuxt": "4.2.2"
27
- },
28
25
  "dependencies": {
29
26
  "@capacitor/preferences": "8.0.0",
30
27
  "@nuxt/icon": "2.1.1",
@@ -58,10 +55,17 @@
58
55
  "devDependencies": {
59
56
  "nuxt": "4.2.2",
60
57
  "typescript": "5.9.3",
61
- "@saasmakers/shared": "0.1.13"
58
+ "@saasmakers/shared": "0.1.15"
59
+ },
60
+ "peerDependencies": {
61
+ "@saasmakers/shared": "^0.1.15",
62
+ "nuxt": "4.2.2"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public"
62
66
  },
63
67
  "scripts": {
64
- "dev": "storybook dev -p 3103",
68
+ "dev": "storybook dev -p 3103 --no-open",
65
69
  "lint": "eslint .",
66
70
  "typecheck": "nuxi typecheck"
67
71
  }