@volverjs/ui-vue 0.0.1-beta.5 → 0.0.1-beta.8

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/README.md +61 -2
  2. package/dist/components/VvButton/vv-button.es.js +56 -58
  3. package/dist/components/VvButton/vv-button.umd.js +1 -1
  4. package/dist/components/VvCheckGroup/vv-check-group.es.js +221 -203
  5. package/dist/components/VvCheckGroup/vv-check-group.umd.js +2 -2
  6. package/dist/components/VvInputText/VvInputText.d.ts +14 -0
  7. package/dist/components/VvInputText/VvInputText.vue.d.ts +36 -1
  8. package/dist/components/VvInputText/VvInputTextActions.d.ts +3 -0
  9. package/dist/components/VvInputText/vv-input-text.es.js +509 -380
  10. package/dist/components/VvInputText/vv-input-text.umd.js +2 -2
  11. package/dist/components/VvNativeSelect/vv-native-select.es.js +180 -161
  12. package/dist/components/VvNativeSelect/vv-native-select.umd.js +2 -2
  13. package/dist/components/VvRadioGroup/vv-radio-group.es.js +211 -193
  14. package/dist/components/VvRadioGroup/vv-radio-group.umd.js +2 -2
  15. package/dist/components/VvSelect/vv-select.es.js +189 -171
  16. package/dist/components/VvSelect/vv-select.umd.js +2 -2
  17. package/dist/components/VvTextarea/VvTextarea.d.ts +43 -22
  18. package/dist/components/VvTextarea/VvTextarea.vue.d.ts +140 -85
  19. package/dist/components/VvTextarea/vv-textarea.es.js +364 -288
  20. package/dist/components/VvTextarea/vv-textarea.umd.js +2 -2
  21. package/dist/composables/debouncedInput/useDebouncedInput.d.ts +2 -0
  22. package/dist/composables/icons/useComponentIcons.d.ts +6 -0
  23. package/dist/composables/textLimit/useTextLimit.d.ts +14 -0
  24. package/dist/composables/useModifiers.d.ts +3 -2
  25. package/dist/icons.es.js +3 -3
  26. package/dist/icons.umd.js +1 -1
  27. package/dist/props/index.d.ts +42 -0
  28. package/dist/stories/utils.d.ts +5 -0
  29. package/dist/ui-vue.es.js +417 -401
  30. package/dist/ui-vue.umd.js +2 -2
  31. package/package.json +3 -1
  32. package/src/assets/icons/detailed.json +1 -1
  33. package/src/assets/icons/normal.json +1 -1
  34. package/src/assets/icons/simple.json +1 -1
  35. package/src/components/VvButton/VvButton.vue +1 -2
  36. package/src/components/VvInputText/VvInputText.ts +19 -2
  37. package/src/components/VvInputText/VvInputText.vue +123 -149
  38. package/src/components/VvInputText/VvInputTextActions.ts +151 -0
  39. package/src/components/VvTextarea/VvTextarea.ts +25 -16
  40. package/src/components/VvTextarea/VvTextarea.vue +89 -93
  41. package/src/components/common/HintSlot.ts +31 -13
  42. package/src/composables/debouncedInput/useDebouncedInput.ts +19 -0
  43. package/src/composables/icons/useComponentIcons.ts +35 -0
  44. package/src/composables/textLimit/useTextLimit.ts +44 -0
  45. package/src/composables/useModifiers.ts +47 -1
  46. package/src/props/index.ts +39 -0
  47. package/src/stories/InputText/InputTextMaxLength.stories.mdx +21 -0
  48. package/src/stories/Textarea/Textarea.stories.mdx +33 -51
  49. package/src/stories/Textarea/TextareaAutoclear.stories.mdx +23 -0
  50. package/src/stories/Textarea/TextareaAutocomplete.stories.mdx +10 -2
  51. package/src/stories/Textarea/TextareaAutofocus.stories.mdx +5 -1
  52. package/src/stories/Textarea/TextareaDebounce.stories.mdx +23 -0
  53. package/src/stories/Textarea/TextareaDisabled.stories.mdx +5 -1
  54. package/src/stories/Textarea/TextareaError.stories.mdx +6 -3
  55. package/src/stories/Textarea/TextareaErrorLabel.stories.mdx +37 -0
  56. package/src/stories/Textarea/TextareaFloating.stories.mdx +7 -2
  57. package/src/stories/Textarea/TextareaHintLabel.stories.mdx +5 -1
  58. package/src/stories/Textarea/TextareaIcon.stories.mdx +5 -1
  59. package/src/stories/Textarea/TextareaIconPosition.stories.mdx +9 -1
  60. package/src/stories/Textarea/TextareaId.stories.mdx +19 -0
  61. package/src/stories/Textarea/TextareaLabel.stories.mdx +5 -1
  62. package/src/stories/Textarea/TextareaLimit.stories.mdx +50 -0
  63. package/src/stories/Textarea/TextareaLoading.stories.mdx +6 -3
  64. package/src/stories/Textarea/TextareaLoadingLabel.stories.mdx +23 -0
  65. package/src/stories/Textarea/TextareaMaxLength.stories.mdx +6 -2
  66. package/src/stories/Textarea/TextareaMinLength.stories.mdx +5 -1
  67. package/src/stories/Textarea/TextareaModifiers.stories.mdx +24 -0
  68. package/src/stories/Textarea/TextareaName.stories.mdx +23 -0
  69. package/src/stories/Textarea/TextareaPlaceholder.stories.mdx +5 -1
  70. package/src/stories/Textarea/TextareaReadonly.stories.mdx +5 -1
  71. package/src/stories/Textarea/TextareaRequired.stories.mdx +22 -0
  72. package/src/stories/Textarea/TextareaResizable.stories.mdx +22 -0
  73. package/src/stories/Textarea/TextareaRowsCols.stories.mdx +9 -1
  74. package/src/stories/Textarea/TextareaValid.stories.mdx +7 -4
  75. package/src/stories/Textarea/TextareaValidLabel.stories.mdx +35 -0
  76. package/src/stories/stories.scss +11 -0
  77. package/src/stories/utils.ts +12 -0
  78. package/src/stories/volver-ui-vue.stories.mdx +7 -1
  79. package/dist/components/VvInputText/useInputNumber.d.ts +0 -16
  80. package/dist/components/VvInputText/useInputPassword.d.ts +0 -16
  81. package/src/components/VvInputText/useInputNumber.ts +0 -40
  82. package/src/components/VvInputText/useInputPassword.ts +0 -38
@@ -1,6 +1,8 @@
1
1
  <template>
2
- <div v-bind="vvTextareaProps" :class="vvInputTextareaClass">
3
- <label v-if="label" :for="innerTextareaProps.id">{{ label }}</label>
2
+ <div v-bind="textAreaProps" :class="textAreaClass">
3
+ <label v-if="label" :id="textAreaLabeledBy" :for="textAreaId">{{
4
+ label
5
+ }}</label>
4
6
  <div class="vv-textarea__wrapper">
5
7
  <!-- @slot icon-left to replace icon left -->
6
8
  <slot v-if="hasIconLeft" name="icon-left" v-bind="iconSlotProps">
@@ -8,18 +10,25 @@
8
10
  </slot>
9
11
  <textarea
10
12
  ref="input"
11
- v-bind="innerTextareaProps"
12
13
  v-model="inputTextData"
14
+ v-bind="htmlTextareaProps"
13
15
  @input="emit('input', $event)" />
16
+ <!-- autoclear text button -->
17
+ <button
18
+ v-if="autoclear && textLength > 0"
19
+ class="vv-button vv-button--ghost"
20
+ @click="clearTextarea">
21
+ <vv-icon name="clear-field" />
22
+ </button>
14
23
  <!-- @slot icon-right to replace icon right -->
15
24
  <slot v-if="hasIconRight" name="icon-right" v-bind="iconSlotProps">
16
- <!-- default password icon -->
17
25
  <vv-icon :name="icon" />
18
26
  </slot>
27
+ <span v-if="limit" class="vv-textarea__limit">
28
+ <slot name="limit"> {{ formattedTextLimitLength }} </slot>
29
+ </span>
19
30
  </div>
20
- <HintSlot
21
- :id="inputAriaAttrs['aria-describedby']"
22
- class="vv-textarea__hint" />
31
+ <HintSlot :id="textAreaDescribedBy" class="vv-textarea__hint" />
23
32
  </div>
24
33
  </template>
25
34
 
@@ -31,7 +40,6 @@ import {
31
40
  ref,
32
41
  toRefs,
33
42
  onMounted,
34
- watch,
35
43
  type HTMLAttributes,
36
44
  type TextareaHTMLAttributes
37
45
  } from 'vue'
@@ -43,10 +51,11 @@ import VvIcon from '../../components/VvIcon/VvIcon.vue'
43
51
  import HintSlotFactory from '../common/HintSlot'
44
52
 
45
53
  //Composables
46
- import { refDebounced } from '@vueuse/core'
47
- import { useComponentIcons } from '../../composables/icons/useComponentIcons'
54
+ import { useComponentIcon } from '../../composables/icons/useComponentIcons'
48
55
  import { useComponentFocus } from '../../composables/focus/useComponentFocus'
49
- import { useBemModifiers } from '@/composables/useModifiers'
56
+ import { useDebouncedInput } from '../../composables/debouncedInput/useDebouncedInput'
57
+ import { useTextLimit } from '../../composables/textLimit/useTextLimit'
58
+ import { toBem } from '@/composables/useModifiers'
50
59
 
51
60
  //Props, Emits, Slots e Attrs
52
61
  const props = defineProps(VvTextareaProps)
@@ -58,124 +67,111 @@ const attrs = useAttrs()
58
67
  const input = ref()
59
68
 
60
69
  //Data
61
- const inputTextData = ref(props.modelValue)
62
- const {
63
- readonly,
64
- icon,
65
- iconPosition,
66
- valid,
67
- error,
68
- loading,
69
- floating,
70
- label,
71
- modelValue
72
- } = toRefs(props)
73
-
74
- //Debounce
75
- const debouncedInputTextData = refDebounced(inputTextData, props.debounce || 0)
76
- watch(debouncedInputTextData, (v) => emit('update:modelValue', v))
70
+ const { icon, iconPosition, label, modelValue, autoclear, limit } =
71
+ toRefs(props)
72
+ const textAreaId = props.id || props.name
73
+ const textAreaLabeledBy = `${props.name}-label`
74
+ const textAreaDescribedBy = `${props.name}-hint`
75
+ //BUG - https://www.samanthaming.com/tidbits/88-css-placeholder-shown/
76
+ const textAreaPlaceholder = computed(() =>
77
+ props.floating && ObjectUtilities.isEmpty(props.placeholder)
78
+ ? ' '
79
+ : props.placeholder
80
+ )
81
+
82
+ //Debounce input
83
+ const inputTextData = useDebouncedInput(modelValue, props.debounce, emit)
77
84
 
78
85
  //Gestione ICONE
79
- const iconProps = { icon, iconPosition }
80
- const iconSlots = {
86
+ const { hasIconLeft, hasIconRight } = useComponentIcon(icon, iconPosition, {
81
87
  iconLeft: slots['icon-left'],
82
88
  iconRight: slots['icon-right']
83
- }
84
- const { hasIconLeft, hasIconRight } = useComponentIcons(iconProps, iconSlots)
89
+ })
85
90
 
86
91
  //Input FOCUS
87
92
  const { focused } = useComponentFocus(input, emit)
88
93
 
89
- //Styles & Bindings
90
- const { bemCssClasses: bemInputClass } = useBemModifiers('vv-textarea', {
91
- readonly,
92
- valid,
93
- invalid: error,
94
- loading,
95
- iconLeft: hasIconLeft,
96
- iconRight: hasIconRight,
97
- floating: computed(
98
- () => floating.value && ObjectUtilities.isNotEmpty(label?.value)
99
- ),
100
- dirty: computed(() => ObjectUtilities.isNotEmpty(modelValue))
94
+ //Conteggio battute
95
+ const { textLength, formattedTextLimitLength } = useTextLimit(inputTextData, {
96
+ mode: props.limit,
97
+ upperLimit: props.maxlength || 0
101
98
  })
102
- const vvInputTextareaClass = computed(() => {
103
- const { class: cssClass } = attrs
104
- return {
105
- class: cssClass,
106
- ...bemInputClass.value
107
- }
99
+
100
+ //Styles & Bindings
101
+ const textAreaClass = computed(() => {
102
+ return [
103
+ toBem('vv-textarea', {
104
+ modifiers: props.modifiers,
105
+ readonly: props.readonly,
106
+ valid: props.valid,
107
+ invalid: props.error,
108
+ loading: props.loading,
109
+ iconLeft: hasIconLeft,
110
+ iconRight: hasIconRight,
111
+ floating: props.floating && ObjectUtilities.isNotEmpty(props.label),
112
+ dirty: ObjectUtilities.isNotEmpty(modelValue?.value),
113
+ resizable: props.resizable
114
+ }),
115
+ attrs.class
116
+ ]
108
117
  })
109
- const vvTextareaProps = computed(() => {
110
- const { style } = attrs
118
+ const textAreaProps = computed(() => {
111
119
  const dataAttrs = ObjectUtilities.pickBy(attrs, (k: string) =>
112
120
  k.startsWith('data-')
113
121
  )
114
122
  return {
115
- style,
123
+ style: attrs.style,
116
124
  ...dataAttrs
117
125
  } as HTMLAttributes
118
126
  })
119
- const innerTextareaProps = computed(() => {
120
- const {
121
- id,
122
- name,
123
- autocomplete,
124
- minlength,
125
- maxlength,
126
- disabled,
127
- readonly,
128
- floating,
129
- placeholder,
130
- cols,
131
- rows
132
- } = props
133
-
134
- const _id = id || name
135
- //BUG - https://www.samanthaming.com/tidbits/88-css-placeholder-shown/
136
- const _placeholder =
137
- floating && ObjectUtilities.isEmpty(placeholder) ? ' ' : placeholder
138
-
139
- return {
140
- id: _id,
141
- placeholder: _placeholder,
142
- name,
143
- autocomplete,
144
- disabled,
145
- readonly,
146
- minlength,
147
- maxlength,
148
- cols,
149
- rows,
150
- ...inputAriaAttrs.value
151
- } as TextareaHTMLAttributes
152
- })
153
- const inputAriaAttrs = computed(() => {
154
- const { name } = attrs
127
+ const htmlTextareaProps = computed(() => {
155
128
  const ariaAttrs = ObjectUtilities.pickBy(attrs, (k: string) =>
156
129
  k.startsWith('aria-')
157
130
  )
131
+
158
132
  return {
159
- 'aria-label': name,
160
- 'aria-describedby': `${name}-hint`,
133
+ id: textAreaId,
134
+ placeholder: textAreaPlaceholder.value,
135
+ name: props.name,
136
+ autocomplete: props.autocomplete,
137
+ disabled: props.disabled,
138
+ readonly: props.readonly,
139
+ minlength: props.minlength,
140
+ maxlength: props.maxlength,
141
+ cols: props.cols,
142
+ rows: props.rows,
143
+ required: props.required,
144
+ tabindex: attrs.tabindex,
161
145
  'aria-invalid': props.error,
146
+ 'aria-valid': !props.valid,
147
+ 'aria-labeledby': textAreaLabeledBy,
148
+ 'aria-describedby': textAreaDescribedBy,
149
+ 'aria-errormessage': textAreaDescribedBy,
162
150
  ...ariaAttrs
163
- }
151
+ } as TextareaHTMLAttributes
164
152
  })
165
153
 
166
154
  //Slot props
167
155
  const iconSlotProps = computed(() => {
168
- const { modelValue, valid, error } = props
156
+ const { modelValue, valid, error, maxlength, hintLabel } = props
169
157
  return {
170
158
  valid,
171
159
  error,
172
- modelValue
160
+ modelValue,
161
+ hintLabel,
162
+ maxlength,
163
+ textLength
173
164
  }
174
165
  })
175
166
 
176
167
  //Hint
177
168
  const HintSlot = HintSlotFactory(props, slots)
178
169
 
170
+ //methods
171
+ function clearTextarea() {
172
+ inputTextData.value = ''
173
+ }
174
+
179
175
  onMounted(() => {
180
176
  if (props.autofocus) focused.value = true
181
177
  console.log('Focused', focused.value)
@@ -1,3 +1,4 @@
1
+ import { toReactive } from '@vueuse/core'
1
2
  import { h, type Component, type ExtractPropTypes, type Slots } from 'vue'
2
3
  import { computed, toRefs } from 'vue'
3
4
  import ObjectUtilities from '../../utils/ObjectUtilities'
@@ -7,7 +8,7 @@ import ObjectUtilities from '../../utils/ObjectUtilities'
7
8
  * @param {Array<string> | string} errors
8
9
  * @returns {string}
9
10
  */
10
- function joinErrors(errors: Array<string> | string) {
11
+ function joinLines(errors: Array<string> | string | unknown[] | undefined) {
11
12
  if (Array.isArray(errors))
12
13
  return errors
13
14
  .filter((e) => ObjectUtilities.isString(e))
@@ -50,7 +51,10 @@ export function HintSlotFactory(
50
51
  ): Component {
51
52
  return {
52
53
  name: 'HintSlot',
53
- setup() {
54
+ props: {
55
+ params: { type: Object, default: () => {} }
56
+ },
57
+ setup(hProps) {
54
58
  const props = toRefs(pProps)
55
59
 
56
60
  //Slots
@@ -110,19 +114,29 @@ export function HintSlotFactory(
110
114
  )
111
115
  })
112
116
 
113
- const errorMessage = computed(() => {
114
- if (Array.isArray(errorLabel?.value))
115
- return joinErrors(errorLabel?.value || '')
116
- else return errorLabel?.value
117
- })
117
+ // const errorMessage = computed(() => {
118
+ // if (Array.isArray(errorLabel?.value))
119
+ // return joinLines(errorLabel?.value || '')
120
+ // else return errorLabel?.value
121
+ // })
118
122
 
119
123
  const hintContent = computed(() => {
120
- const slotProps = { modelValue, error, valid }
124
+ const slotProps = toReactive({
125
+ hintLabel,
126
+ modelValue,
127
+ valid,
128
+ validLabel,
129
+ error,
130
+ errorLabel,
131
+ loading,
132
+ loadingLabel,
133
+ ...hProps.params
134
+ })
121
135
 
122
136
  if (error?.value) {
123
137
  return (
124
138
  errorSlot?.(slotProps) ||
125
- errorMessage?.value ||
139
+ joinLines(errorLabel?.value) ||
126
140
  hintLabel?.value
127
141
  )
128
142
  }
@@ -130,20 +144,20 @@ export function HintSlotFactory(
130
144
  if (valid?.value)
131
145
  return (
132
146
  validSlot?.(slotProps) ||
133
- validLabel?.value ||
147
+ joinLines(validLabel?.value) ||
134
148
  hintLabel?.value
135
149
  )
136
150
 
137
151
  if (loading?.value)
138
152
  return (
139
153
  loadingSlot?.(slotProps) ||
140
- loadingLabel?.value ||
154
+ joinLines(loadingLabel?.value) ||
141
155
  hintLabel?.value
142
156
  )
143
157
 
144
158
  return (
145
159
  hintSlot?.(slotProps) ||
146
- hintLabel?.value ||
160
+ joinLines(hintLabel?.value) ||
147
161
  hintLabel?.value
148
162
  )
149
163
  })
@@ -155,7 +169,11 @@ export function HintSlotFactory(
155
169
  },
156
170
  render() {
157
171
  if (this.hasHint) {
158
- return h('span', null, this.hintContent)
172
+ return h(
173
+ 'pre',
174
+ { style: { 'white-space': 'pre' } },
175
+ this.hintContent
176
+ )
159
177
  }
160
178
  }
161
179
  }
@@ -0,0 +1,19 @@
1
+ import type { Ref } from 'vue'
2
+
3
+ import { refDebounced } from '@vueuse/core'
4
+
5
+ import { watch, ref } from 'vue'
6
+
7
+ export function useDebouncedInput(
8
+ inputText: Ref<any> | undefined,
9
+ debounced: number | undefined,
10
+ emit: (event: any, ...args: any[]) => void
11
+ ): Ref<any> {
12
+ const _text = ref(inputText?.value)
13
+ const debouncedInputTextData = refDebounced(
14
+ _text as Ref<any>,
15
+ debounced || 0
16
+ )
17
+ watch(debouncedInputTextData, (v) => emit('update:modelValue', v))
18
+ return _text
19
+ }
@@ -50,3 +50,38 @@ export function useComponentIcons(
50
50
  hasIconBottom
51
51
  }
52
52
  }
53
+
54
+ export function useComponentIcon(
55
+ icon: Ref<string>,
56
+ iconPosition: Ref<string>,
57
+ slots: ComponentIconSlots
58
+ ) {
59
+ const hasIconLeft = computed(
60
+ () =>
61
+ !!((icon.value && iconPosition.value === 'left') || slots.iconLeft)
62
+ )
63
+ const hasIconRight = computed(
64
+ () =>
65
+ !!(
66
+ (icon.value && iconPosition.value === 'right') ||
67
+ slots.iconRight
68
+ )
69
+ )
70
+ const hasIconTop = computed(
71
+ () => !!((icon.value && iconPosition.value === 'top') || slots.iconTop)
72
+ )
73
+ const hasIconBottom = computed(
74
+ () =>
75
+ !!(
76
+ (icon.value && iconPosition.value === 'bottom') ||
77
+ slots.iconBottom
78
+ )
79
+ )
80
+
81
+ return {
82
+ hasIconLeft,
83
+ hasIconRight,
84
+ hasIconTop,
85
+ hasIconBottom
86
+ }
87
+ }
@@ -0,0 +1,44 @@
1
+ import { isString } from '@vueuse/core'
2
+ import { computed, unref, type Ref } from 'vue'
3
+
4
+ interface useTextOptions {
5
+ mode: string | boolean
6
+ upperLimit: number
7
+ }
8
+
9
+ /**
10
+ *
11
+ */
12
+ export function useTextLimit(inputText: Ref<string>, options: useTextOptions) {
13
+ const textLength = computed(() => {
14
+ const _text = unref(inputText)
15
+ return isString(_text) ? _text.length : 0
16
+ })
17
+
18
+ const textLimitLength = computed(() => {
19
+ const _text = unref(inputText) || ''
20
+
21
+ if (!isString(_text) || options.mode === false) return 0
22
+
23
+ if (options.mode === true) return _text.length
24
+ else return unref(options.upperLimit) - _text.length
25
+ })
26
+
27
+ const formattedTextLimitLength = computed(() => {
28
+ if (options.mode === false) return ''
29
+
30
+ if (
31
+ options.mode === true &&
32
+ options.upperLimit &&
33
+ options.upperLimit > 0
34
+ )
35
+ return `${textLimitLength.value}/${unref(options.upperLimit)}`
36
+ else return textLimitLength.value
37
+ })
38
+
39
+ return {
40
+ textLength,
41
+ textLimitLength,
42
+ formattedTextLimitLength
43
+ }
44
+ }
@@ -5,8 +5,17 @@ interface IBemModifiers {
5
5
  [key: string]:
6
6
  | Ref<boolean>
7
7
  | Ref<string | unknown[] | undefined>
8
+ | boolean
9
+ | string
10
+ | string[]
8
11
  | undefined
9
- modifiers?: Ref<string | unknown[] | undefined> | undefined
12
+ | unknown[]
13
+ modifiers?:
14
+ | Ref<string | unknown[] | undefined>
15
+ | undefined
16
+ | string[]
17
+ | string
18
+ | unknown[]
10
19
  }
11
20
 
12
21
  export function useBemModifiers(prefix: string, modifiers: IBemModifiers) {
@@ -52,3 +61,40 @@ export function useBemModifiers(prefix: string, modifiers: IBemModifiers) {
52
61
  bemCssClasses
53
62
  }
54
63
  }
64
+
65
+ export function toBem(prefix: string, modifiers: IBemModifiers) {
66
+ const baseCssClass: object = { [`${prefix}`]: true }
67
+
68
+ return (
69
+ Object.keys(modifiers).reduce((acc, k) => {
70
+ const _modifier = unref(modifiers[k] as Ref<any>) || false
71
+
72
+ if (!_modifier) return acc
73
+
74
+ if (k === 'modifiers') {
75
+ const _reduceModifiers = Array.isArray(_modifier)
76
+ ? _modifier
77
+ : [_modifier]
78
+ return {
79
+ ...acc,
80
+ ..._reduceModifiers.reduce(
81
+ (accVariant: object, currentVariant: string) => {
82
+ return {
83
+ ...accVariant,
84
+ [`${prefix}--${ObjectUtilities.kebabCase(
85
+ currentVariant
86
+ )}`]: true
87
+ }
88
+ },
89
+ {}
90
+ )
91
+ }
92
+ } else {
93
+ return {
94
+ ...acc,
95
+ [`${prefix}--${ObjectUtilities.kebabCase(k)}`]: _modifier
96
+ }
97
+ }
98
+ }, baseCssClass) || {}
99
+ )
100
+ }
@@ -43,3 +43,42 @@ export const OptionsProps = {
43
43
  */
44
44
  optionValue: { type: [String, Function], default: () => 'value' }
45
45
  }
46
+
47
+ export const LimitProps = {
48
+ /**
49
+ * Conteggio caratteri
50
+ * @description
51
+ * Se false, non mostrare il conteggio caratteri
52
+ * Se true, mostra quanti caratteri ho digitato sin ora e qualì'è la maxlength.
53
+ * Se true e maxlength > 0, mostra quanti caratteri ho digitato sin ora e qualì'è la maxlength.
54
+ * Se "countdown", mostra quanti caratteri mancano per raggiungere la maxlength.
55
+ */
56
+ limit: {
57
+ type: [Boolean, String],
58
+ default: false,
59
+ validator: (value: string) => [true, false, 'countdown'].includes(value)
60
+ }
61
+ }
62
+
63
+ export const InputProps = {
64
+ id: String,
65
+ name: { type: String, required: true },
66
+ autocomplete: { type: String, default: 'off' },
67
+ autofocus: Boolean,
68
+ minlength: Number,
69
+ maxlength: Number,
70
+ label: String,
71
+ placeholder: String,
72
+ required: Boolean,
73
+ disabled: Boolean,
74
+ readonly: Boolean
75
+ }
76
+
77
+ export const DebounceProps = {
78
+ /**
79
+ * Debounce time (millisecondi)
80
+ * @descrition
81
+ * Tempo che deve passare dall'ultima battuta prima che modelValue venga aggiornato.
82
+ */
83
+ debounce: Number
84
+ }
@@ -13,3 +13,24 @@ import { Template } from './InputText.stories.mdx'
13
13
  {Template.bind({})}
14
14
  </Story>
15
15
  </Canvas>
16
+
17
+ <Canvas>
18
+ <Story
19
+ name="MaxLength - limit"
20
+ args={{ name: 'input-text-limit', maxlength: 5, showLimit: true }}>
21
+ {Template.bind({})}
22
+ </Story>
23
+ </Canvas>
24
+
25
+ <Canvas>
26
+ <Story
27
+ name="MaxLength - countdown"
28
+ args={{
29
+ name: 'input-text-limit-countdown',
30
+ maxlength: 5,
31
+ showLimit: true,
32
+ showLimitMode: 'countdown'
33
+ }}>
34
+ {Template.bind({})}
35
+ </Story>
36
+ </Canvas>
@@ -1,7 +1,10 @@
1
1
  import { Canvas, Meta, Story, Source, ArgsTable } from '@storybook/addon-docs'
2
2
  import { action } from '@storybook/addon-actions'
3
3
 
4
+ import { toGroup } from '../utils'
5
+ import ObjectUtilities from '../../utils/ObjectUtilities'
4
6
  import VvTextarea from '../../components/VvTextarea/VvTextarea.vue'
7
+ import { VvTextareaProps } from '../../components/VvTextarea/VvTextarea'
5
8
 
6
9
  <Meta title="Components/Textarea" component={VvTextarea} />
7
10
 
@@ -20,65 +23,44 @@ export const Template = (args, { argTypes }) => ({
20
23
  `
21
24
  })
22
25
 
26
+ # VvTextarea
27
+
28
+ `VvTextarea` is a multi-line text input element.
29
+
23
30
  <Canvas>
24
31
  <Story
25
32
  name="vv-textarea"
26
33
  args={{
27
- text: 'text',
28
- id: 'vv-textarea',
29
- name: 'vv-textarea',
30
- placeholder: 'Prova input',
31
- label: 'Campo di prova',
32
- hintLabel: 'Hint label text',
33
- autocomplete: 'off',
34
- disabled: false,
35
- readonly: false,
36
- valid: false,
37
- error: false,
38
- loading: false,
39
- loadingLabel: 'Ciao sono il loading',
40
- icon: '',
41
- iconPosition: null,
42
- floating: false,
43
- minlength: null,
34
+ ...ObjectUtilities.propsToObject(VvTextareaProps),
44
35
  maxlength: null
45
36
  }}
46
37
  argTypes={{
47
- id: {
48
- description: 'Id input element.',
49
- table: { category: 'Attributes' }
50
- },
51
- name: {
52
- description: 'Nome input element',
53
- table: { category: 'Attributes' }
54
- },
55
- placeholder: {
56
- description: 'Placeholder input',
57
- table: { category: 'Attributes' }
58
- },
59
- iconPosition: {
60
- control: {
61
- type: 'select',
62
- options: [null, 'left', 'right']
63
- },
64
- table: { category: 'Attributes' }
65
- },
66
- type: {
67
- control: {
68
- type: 'select',
69
- options: ['text', 'password', 'date', 'datetime-local']
70
- },
71
- table: { category: 'Attributes' }
72
- },
73
- minlength: {
74
- description: 'Lunghezza minima testo',
75
- table: { category: 'Attributes' }
76
- },
77
- maxlength: {
78
- description: 'Lunghezza massima testo',
79
- table: { category: 'Attributes' }
80
- }
38
+ ...toGroup(
39
+ ['required', 'minlength', 'maxlength'],
40
+ 'Data validation'
41
+ ),
42
+ ...toGroup(
43
+ [
44
+ 'disabled',
45
+ 'readonly',
46
+ 'valid',
47
+ 'validLabel',
48
+ 'error',
49
+ 'errorLabel',
50
+ 'loading',
51
+ 'loadingLabel'
52
+ ],
53
+ 'State'
54
+ ),
55
+ ...toGroup(['floating'], 'Floating'),
56
+ ...toGroup(['modifiers'], 'Modifiers'),
57
+ ...toGroup(['icon', 'iconPosition'], 'Icon'),
58
+ ...toGroup(['hintLabel'], 'Hint text'),
59
+ ...toGroup(['limit'], 'Text limit count'),
60
+ ...toGroup(['autoclear'], 'Experimental feature')
81
61
  }}>
82
62
  {Template.bind({})}
83
63
  </Story>
84
64
  </Canvas>
65
+
66
+ <ArgsTable story="vv-textarea" />