tide-design-system 2.0.0 → 2.0.2

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 (123) hide show
  1. package/dist/assets/style.css +1 -0
  2. package/dist/css/animation.css +1 -1
  3. package/dist/css/dynamic-buttons.css +22 -22
  4. package/dist/css/dynamic-utilities.css +21 -22
  5. package/dist/css/main.css +0 -5
  6. package/dist/css/page-layout.css +30 -46
  7. package/dist/css/realm/aero.css +33 -33
  8. package/dist/css/realm/atv.css +34 -34
  9. package/dist/css/realm/boatmart.css +33 -33
  10. package/dist/css/realm/cycle.css +33 -33
  11. package/dist/css/realm/equip.css +33 -33
  12. package/dist/css/realm/pwc.css +33 -33
  13. package/dist/css/realm/rv.css +33 -33
  14. package/dist/css/realm/snow.css +32 -32
  15. package/dist/css/realm/truck.css +33 -33
  16. package/dist/css/reset.css +9 -9
  17. package/dist/css/sb-rv.css +1 -0
  18. package/dist/css/storybook.css +2 -1
  19. package/dist/css/utilities.css +105 -102
  20. package/dist/css/variables.css +96 -92
  21. package/dist/tide-design-system.js +5 -0
  22. package/dist/tide-design-system2.js +5 -0
  23. package/dist/tide-design-system3.js +5 -0
  24. package/dist/tide-design-system4.js +5 -0
  25. package/dist/tide-design-system5.js +5 -0
  26. package/dist/tide-design-system6.js +5 -0
  27. package/dist/tide-design-system7.js +5 -0
  28. package/dist/tide-design-system8.js +5 -0
  29. package/dist/types/FacetRange.ts +84 -0
  30. package/dist/types/Field.ts +0 -1
  31. package/dist/types/Form.ts +57 -0
  32. package/dist/types/Realm.ts +1 -0
  33. package/dist/types/Select.ts +6 -0
  34. package/dist/types/Storybook.ts +206 -1
  35. package/dist/types/Styles.ts +34 -2
  36. package/dist/types/Validation.ts +7 -1
  37. package/dist/utilities/format.ts +75 -39
  38. package/dist/utilities/storybook.ts +66 -8
  39. package/dist/utilities/validation.ts +139 -34
  40. package/package.json +3 -2
  41. package/dist/IconAccountBalance-91cf067b.js +0 -10
  42. package/dist/IconAdd-95c51c0e.js +0 -10
  43. package/dist/IconAi-08172540.js +0 -22
  44. package/dist/IconAlignSpace-9ab2bdf2.js +0 -10
  45. package/dist/IconApplePay-1ee6317b.js +0 -10
  46. package/dist/IconArrowBack-a2226a94.js +0 -10
  47. package/dist/IconArrowForward-e1ca9957.js +0 -10
  48. package/dist/IconArrowRight-53382084.js +0 -10
  49. package/dist/IconAssignment-332c2b2b.js +0 -10
  50. package/dist/IconAwardStar-1ca35385.js +0 -10
  51. package/dist/IconBookmark-49b42628.js +0 -10
  52. package/dist/IconCalendarMonth-22c938d7.js +0 -10
  53. package/dist/IconCall-989a47fc.js +0 -10
  54. package/dist/IconCheck-a3467b47.js +0 -10
  55. package/dist/IconChevronLeft-c1d90bb7.js +0 -10
  56. package/dist/IconChevronRight-ad47891f.js +0 -10
  57. package/dist/IconClear-7c8fad4e.js +0 -10
  58. package/dist/IconClose-4b6c5aed.js +0 -10
  59. package/dist/IconCycle-99d40f2d.js +0 -10
  60. package/dist/IconDelete-446eff93.js +0 -10
  61. package/dist/IconDiamond-765a7d8d.js +0 -10
  62. package/dist/IconEdit-ce05f3b5.js +0 -10
  63. package/dist/IconError-7983707a.js +0 -10
  64. package/dist/IconExpandContent-8b6e2125.js +0 -10
  65. package/dist/IconExpandLess-9e23f1e9.js +0 -10
  66. package/dist/IconExpandMore-ded098a7.js +0 -10
  67. package/dist/IconFacebook-3cab65a8.js +0 -10
  68. package/dist/IconFavorite-5fe831f4.js +0 -10
  69. package/dist/IconFavoriteFilled-58fa0bf7.js +0 -10
  70. package/dist/IconFormatBold-889f6b8b.js +0 -10
  71. package/dist/IconFormatItalic-103eba00.js +0 -10
  72. package/dist/IconFormatListBulleted-4c824025.js +0 -10
  73. package/dist/IconForum-abc2fe82.js +0 -10
  74. package/dist/IconGoogle-281b6d80.js +0 -27
  75. package/dist/IconGooglePay-cc83c5c8.js +0 -10
  76. package/dist/IconGrid-ef763745.js +0 -10
  77. package/dist/IconHelp-2ad33f76.js +0 -10
  78. package/dist/IconInfo-5878df77.js +0 -10
  79. package/dist/IconInsertText-0c62badf.js +0 -10
  80. package/dist/IconInstagram-69e21cfb.js +0 -10
  81. package/dist/IconIosShare-be5f117c.js +0 -10
  82. package/dist/IconLayout-c1ffbcd3.js +0 -10
  83. package/dist/IconLinkedIn-807ef6f5.js +0 -10
  84. package/dist/IconLocalShipping-2c6d71e0.js +0 -10
  85. package/dist/IconLock-9178e816.js +0 -10
  86. package/dist/IconMail-0123a7c6.js +0 -10
  87. package/dist/IconMenu-33ed2d99.js +0 -10
  88. package/dist/IconMoreHoriz-c281099f.js +0 -10
  89. package/dist/IconNotifications-89f80e0f.js +0 -10
  90. package/dist/IconOpenInNew-87ad0454.js +0 -10
  91. package/dist/IconPalette-7ee5b40c.js +0 -10
  92. package/dist/IconPaypal-e311eadd.js +0 -10
  93. package/dist/IconPerson-932fbcbc.js +0 -10
  94. package/dist/IconPhotoCamera-fdbd5767.js +0 -10
  95. package/dist/IconPinterest-2d19c2eb.js +0 -10
  96. package/dist/IconPlayArrow-9837a5c0.js +0 -10
  97. package/dist/IconRemove-29ef8f82.js +0 -10
  98. package/dist/IconRoundedCorners-8ad194fc.js +0 -10
  99. package/dist/IconSearch-5ff23d26.js +0 -10
  100. package/dist/IconSell-0e0ecd20.js +0 -10
  101. package/dist/IconShare-47084765.js +0 -10
  102. package/dist/IconShoppingCart-9d6495b3.js +0 -10
  103. package/dist/IconSms-5ba18382.js +0 -10
  104. package/dist/IconStar-ef69284b.js +0 -10
  105. package/dist/IconSwapVert-05e14e3d.js +0 -10
  106. package/dist/IconThreeDRotation-2433b2e8.js +0 -25
  107. package/dist/IconTune-3c6452f0.js +0 -10
  108. package/dist/IconTwitter-a634cef4.js +0 -10
  109. package/dist/IconVideocam-5712435e.js +0 -10
  110. package/dist/IconViewInAr-d38a23d5.js +0 -10
  111. package/dist/IconVisibility-8cdf7151.js +0 -10
  112. package/dist/IconWarning-b9e61180.js +0 -10
  113. package/dist/IconYoutube-92447826.js +0 -10
  114. package/dist/css/dynamic-inputs.css +0 -43
  115. package/dist/index-c5bc4216.js +0 -1910
  116. package/dist/style.css +0 -1
  117. package/dist/tide2-design-system.js +0 -36
  118. package/dist/types/StorybookStyles.ts +0 -209
  119. package/dist/types/Vehicle.ts +0 -139
  120. package/dist/types/VehicleDetail.ts +0 -44
  121. package/dist/types/index.d.ts +0 -7
  122. package/dist/utilities/forms.ts +0 -47
  123. package/dist/utilities/media.ts +0 -77
@@ -22,11 +22,31 @@ export const CSS = {
22
22
  END: 'tide-axis2-end',
23
23
  START: 'tide-axis2-start',
24
24
  },
25
+ BG: {
26
+ PRIMARY: 'tide-bg-primary',
27
+ SECONDARY: 'tide-bg-secondary',
28
+ SURFACE: {
29
+ ACCENT: 'tide-bg-surface-accent',
30
+ ACCENT_VARIANT: 'tide-bg-surface-accent-variant',
31
+ BRAND: 'tide-bg-surface-brand',
32
+ DEFAULT: 'tide-bg-surface',
33
+ FLOATING: 'tide-bg-surface-floating',
34
+ GRADIENT: 'tide-bg-surface-gradient',
35
+ VARIANT: 'tide-bg-surface-variant',
36
+ },
37
+ },
25
38
  BORDER: {
26
39
  BOTTOM: {
27
40
  ONE: 'tide-border-bottom-1',
28
41
  TWO: 'tide-border-bottom-2',
29
42
  },
43
+ COLOR: {
44
+ DEFAULT: 'tide-border',
45
+ FLOATING: 'tide-border-floating',
46
+ HIGH: 'tide-border-high',
47
+ LOW: 'tide-border-low',
48
+ PRIMARY: 'tide-border-primary',
49
+ },
30
50
  FULL: {
31
51
  ONE: 'tide-border-1',
32
52
  TWO: 'tide-border-2',
@@ -60,17 +80,18 @@ export const CSS = {
60
80
  CURSOR: {
61
81
  NOT_ALLOWED: 'tide-cursor-not-allowed',
62
82
  POINTER: 'tide-cursor-pointer',
83
+ TEXT: 'tide-cursor-text',
63
84
  },
64
85
  DISPLAY: {
65
86
  BLOCK: 'tide-display-block',
66
87
  CONTENTS: 'tide-contents',
67
88
  FLEX: 'tide-display-flex',
68
89
  GRID: 'tide-display-grid',
69
- HIDDEN: 'tide-display-hidden',
70
90
  INITIAL: 'initial',
71
91
  INLINE: 'tide-display-inline',
72
92
  INLINE_BLOCK: 'tide-display-inline-block',
73
93
  INLINE_FLEX: 'tide-display-inline-flex',
94
+ NONE: 'tide-display-none',
74
95
  },
75
96
  FLEX: {
76
97
  DIRECTION: {
@@ -90,6 +111,17 @@ export const CSS = {
90
111
  WRAP: 'tide-flex-wrap',
91
112
  },
92
113
  FONT: {
114
+ COLOR: {
115
+ PRIMARY: 'tide-font-on-primary',
116
+ SECONDARY: 'tide-font-on-secondary',
117
+ SURFACE: {
118
+ BRAND: 'tide-font-on-surface-brand',
119
+ DEFAULT: 'tide-font-on-surface',
120
+ INVERSE: 'tide-font-on-surface-inverse',
121
+ VARIANT: 'tide-font-on-surface-variant',
122
+ VARIANT_INVERSE: 'tide-font-on-surface-variant-inverse',
123
+ },
124
+ },
93
125
  SIZE: {
94
126
  FOURTEEN: 'tide-font-14',
95
127
  SIXTEEN: 'tide-font-16',
@@ -303,7 +335,7 @@ export const cssSortOrder = [
303
335
  CSS.DISPLAY.CONTENTS,
304
336
  CSS.DISPLAY.FLEX,
305
337
  CSS.DISPLAY.GRID,
306
- CSS.DISPLAY.HIDDEN,
338
+ CSS.DISPLAY.NONE,
307
339
  CSS.DISPLAY.INITIAL,
308
340
  CSS.DISPLAY.INLINE,
309
341
  CSS.DISPLAY.INLINE_BLOCK,
@@ -11,8 +11,14 @@ export const VALIDATOR = {
11
11
 
12
12
  export type ValidationError = string | boolean;
13
13
  export type ValidationResult = {
14
- valid: boolean;
15
14
  message: string;
15
+ valid: boolean;
16
+ };
17
+
18
+ export type ValidationLength = {
19
+ maxlength?: number;
20
+ minlength?: number;
21
+ value: string;
16
22
  };
17
23
 
18
24
  export type Validator = (value: string) => ValidationResult;
@@ -1,4 +1,6 @@
1
- import type { RealmConfig } from '@/types/RealmConfig';
1
+ const capitalizeFirst = (string: string) => {
2
+ return string.charAt(0).toUpperCase() + string.slice(1);
3
+ };
2
4
 
3
5
  const formatCamelCase = (input: string): string => {
4
6
  return input && typeof input === 'string'
@@ -22,9 +24,26 @@ const formatKebabCase = (input: string): string => {
22
24
  : input;
23
25
  };
24
26
 
27
+ const formatLabel = (value: string) => {
28
+ const labelMap: { [key: string]: string } = {
29
+ false: 'No',
30
+ true: 'Yes',
31
+ };
32
+ return Object.hasOwn(labelMap, value) ? labelMap[value] : value;
33
+ };
34
+
25
35
  const formatNumber = (input: number | string): string => {
26
- const integer = typeof input === 'number' ? input : parseInt(input.match(/\d/g)?.join('') || '', 10);
27
- const output = integer ? new Intl.NumberFormat().format(integer) : '';
36
+ let output = input && typeof input === 'number' ? new Intl.NumberFormat().format(input) : String(input || '');
37
+ let digits = output;
38
+
39
+ if (input && typeof input === 'string') {
40
+ digits = digits.replace(/\D/g, '');
41
+ if (Number(digits)) {
42
+ output = new Intl.NumberFormat().format(Number(digits));
43
+ } else {
44
+ output = '0';
45
+ }
46
+ }
28
47
 
29
48
  return output;
30
49
  };
@@ -42,38 +61,39 @@ const formatPascalCase = (input: string): string => {
42
61
  };
43
62
 
44
63
  const formatPhone = (input: number | string): string => {
45
- const integer = typeof input === 'number' ? input : parseInt(input.match(/\d/g)?.join('') || '', 10);
46
- let output = integer ? String(integer) : '';
47
-
48
- if (integer && typeof integer === 'number') {
49
- const digits = String(integer);
50
-
51
- output = digits;
52
-
53
- if (digits) {
54
- switch (digits.length) {
55
- case 7:
56
- output = `${digits.slice(0, 3)}-${digits.slice(3)}`;
57
- break;
58
- case 10:
59
- output = `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`;
60
- break;
61
- case 11:
62
- output = `${digits.slice(0, 1)}-${digits.slice(1, 4)}-${digits.slice(4, 7)}-${digits.slice(7)}`;
63
- break;
64
- default:
65
- break;
66
- }
67
- }
64
+ let output = input && typeof input === 'number' ? String(input) : String(input || '');
65
+ let digits = output;
66
+ digits = digits.replace(/\D/g, '');
67
+
68
+ switch (digits.length) {
69
+ case 7:
70
+ output = `${digits.slice(0, 3)}-${digits.slice(3)}`;
71
+ break;
72
+ case 10:
73
+ output = `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`;
74
+ break;
75
+ case 11:
76
+ output = `${digits.slice(0, 1)}-${digits.slice(1, 4)}-${digits.slice(4, 7)}-${digits.slice(7)}`;
77
+ break;
78
+ default:
79
+ output = digits;
80
+ break;
68
81
  }
69
82
 
70
83
  return output;
71
84
  };
72
85
 
73
86
  const formatPrice = (input: number | string): string => {
74
- const output = formatNumber(input);
87
+ const output = input ? formatNumber(input) : String(input || '0');
88
+ return `$${output}`;
89
+ };
75
90
 
76
- return `$${output || '--'}`;
91
+ const formatQuotes = (value: string) => {
92
+ if (value.startsWith('"') && value.endsWith('"')) {
93
+ return value.slice(1, -1);
94
+ } else {
95
+ return value;
96
+ }
77
97
  };
78
98
 
79
99
  const formatSentenceCase = (input: string): string => {
@@ -112,6 +132,17 @@ const formatTitleCase = (input: string): string => {
112
132
  : input;
113
133
  };
114
134
 
135
+ const formatUrlFromRoot = (url: string) => {
136
+ const urlFormatted = url.split('.com/');
137
+
138
+ return urlFormatted.length > 1 ? `/${urlFormatted[1]}` : url;
139
+ };
140
+
141
+ const formatWeight = (input: number | string): string => {
142
+ const output = input ? formatNumber(input) : String(input || '0');
143
+ return `${output} lbs`;
144
+ };
145
+
115
146
  const getArticle = (noun: string, isPlural = false, isDefinite = false) => {
116
147
  const vowels = ['a', 'e', 'i', 'o', 'u'];
117
148
  const isVowelLeading = vowels.includes(noun.charAt(0));
@@ -119,30 +150,35 @@ const getArticle = (noun: string, isPlural = false, isDefinite = false) => {
119
150
  return isDefinite ? 'the' : isPlural ? 'some' : isVowelLeading ? 'an' : 'a';
120
151
  };
121
152
 
122
- const getCdn = (realm: RealmConfig) => {
123
- return `https://cdn.${realm.cdn.domain}.com`;
124
- };
125
-
126
- const getCdnMediaRoot = (realm: RealmConfig) => {
127
- return `https://cdn.${realm.cdn.domain}.com/${realm.cdn.version}/media`;
153
+ const priceToNumber = (value: string) => {
154
+ if (Number.isNaN(Number(value))) {
155
+ value = value.replace('$', '');
156
+ value = value.replace(/,/g, '');
157
+ }
158
+ return parseInt(value, 10);
128
159
  };
129
160
 
130
- const removeMarkup = (markup: string) => {
131
- return markup.replace(/<[^>]*>/g, '');
161
+ const unformatPrice = (input: string): string => {
162
+ const output = input.replace(/\$/g, '').replace(/,/g, '');
163
+ return `${output}`;
132
164
  };
133
165
 
134
166
  export {
167
+ capitalizeFirst,
135
168
  formatCamelCase,
136
169
  formatKebabCase,
170
+ formatLabel,
137
171
  formatNumber,
138
172
  formatPascalCase,
139
173
  formatPhone,
140
174
  formatPrice,
175
+ formatQuotes,
141
176
  formatSentenceCase,
142
177
  formatSnakeCase,
143
178
  formatTitleCase,
179
+ formatUrlFromRoot,
180
+ formatWeight,
144
181
  getArticle,
145
- getCdn,
146
- getCdnMediaRoot,
147
- removeMarkup,
182
+ priceToNumber,
183
+ unformatPrice,
148
184
  };
@@ -1,6 +1,7 @@
1
1
  import type { ArgTypes } from '@storybook/vue3';
2
2
 
3
3
  import { BOOLEAN_UNREQUIRED } from '@/types/Storybook';
4
+ import { CSS } from '@/types/Styles';
4
5
  import { ELEMENT, ELEMENT_TEXT_AS_ICON } from '@/types/Element';
5
6
 
6
7
  // Extensible object of key/value pairs
@@ -42,12 +43,23 @@ export const argTypeDimension = {
42
43
  },
43
44
  };
44
45
 
46
+ export const change = {
47
+ control: 'text',
48
+ description: 'JS code or function to execute on change event',
49
+ isEmit: true,
50
+ name: 'change',
51
+ table: {
52
+ defaultValue: { summary: 'None' },
53
+ type: { summary: '(event: Event) => void' },
54
+ },
55
+ };
56
+
45
57
  export const click = {
46
58
  control: 'text',
47
- description: 'JS function to execute on click',
59
+ description: 'JS code or function to execute on click event',
48
60
  table: {
49
61
  defaultValue: { summary: 'None' },
50
- type: { summary: 'function' },
62
+ type: { summary: '(event: Event) => void' },
51
63
  },
52
64
  };
53
65
 
@@ -60,11 +72,17 @@ export const dataTrack = {
60
72
  },
61
73
  };
62
74
 
75
+ export const disabledArgType = {
76
+ table: {
77
+ disable: true,
78
+ },
79
+ };
80
+
63
81
  export const doSomething = () => {
64
82
  alert('Did something.');
65
83
  };
66
84
 
67
- // Accept a KeyValue as the value of an object with a retrievable key as a Storybook argType
85
+ // Accept a KeyValue as the value of an object with a retrievable key as a Storybook argType.
68
86
  export const formatArgType = (collection: KeyValueNamed) => {
69
87
  const constant = getKey(collection);
70
88
  const keyValues: KeyValue = collection[constant];
@@ -138,8 +156,9 @@ export const formatSnippet = (code: string, context: StoryContext) => {
138
156
  const isCustom = argType.isCustom;
139
157
  const isDynamic = argType.isDynamic || isConstant || isConstants || typeof value === 'boolean';
140
158
  const isEmpty = !isDynamic && value === '';
141
- const isSlot = key === 'default';
142
159
  const isExcluded = value === undefined || (Array.isArray(value) && !value.length);
160
+ const isEmit = argType.isEmit;
161
+ const isSlot = key === 'default';
143
162
 
144
163
  if (argType.isCss) {
145
164
  classNames.push(value);
@@ -169,10 +188,6 @@ export const formatSnippet = (code: string, context: StoryContext) => {
169
188
  value = `[${constantSlots.join(', ')}]`;
170
189
  }
171
190
 
172
- if (isConditionMet && !isClick && !isCustom && !isEmpty && !isExcluded && !isSlot) {
173
- return `${isDynamic ? ':' : ''}${formatKebabCase(key)}="${value}"`;
174
- }
175
-
176
191
  if (isCustom) {
177
192
  return `:${formatKebabCase(key)}="${key}"`;
178
193
  }
@@ -184,6 +199,16 @@ export const formatSnippet = (code: string, context: StoryContext) => {
184
199
  ) {
185
200
  return `@click="${value}"`;
186
201
  }
202
+
203
+ if (isEmit) {
204
+ if (value) {
205
+ return `@change="${value}"`;
206
+ }
207
+ }
208
+
209
+ if (isConditionMet && !isClick && !isCustom && !isEmpty && !isExcluded && !isSlot) {
210
+ return `${isDynamic ? ':' : ''}${formatKebabCase(key)}="${value}"`;
211
+ }
187
212
  });
188
213
 
189
214
  classNames = classNames.filter((className) => !!className);
@@ -203,6 +228,39 @@ export const formatSnippetMinimal = (code: string) => {
203
228
  return code.replace(/<[/]*template>/g, '');
204
229
  };
205
230
 
231
+ export const getConstantByValue = (value: string) => {
232
+ const compareRecursively = (basis: string, shape: string | object, depth: number = 0): string | void => {
233
+ let output;
234
+
235
+ Object.entries(shape).forEach((entry: string[]) => {
236
+ const key = entry[0];
237
+ const value = entry[1];
238
+ const type = typeof value;
239
+
240
+ if (type === 'string' && basis === value) {
241
+ output = key;
242
+ return;
243
+ } else if (type === 'object') {
244
+ const match = compareRecursively(basis, value, depth + 1);
245
+
246
+ if (match) {
247
+ output = `${key}.${match}`;
248
+ return;
249
+ }
250
+ }
251
+ });
252
+
253
+ return output;
254
+ };
255
+
256
+ const constant = compareRecursively(value, CSS);
257
+
258
+ return constant ? `CSS.${constant}` : undefined;
259
+ };
260
+
261
+ export const getConstantsByValues = (classNames: string[]) =>
262
+ classNames.map((className) => getConstantByValue(className));
263
+
206
264
  export const getKey = (input: any) => Object.keys(input)[0];
207
265
 
208
266
  // Invert key/value pairs bc Storybook control option format is unintuitive.
@@ -1,13 +1,22 @@
1
1
  import type { Ref } from 'vue';
2
- import type { StringField } from '@/types/Field';
3
- import type { ValidationResult } from '@/types/Validation';
4
2
 
5
- export function checkFormat(format: RegExp) {
3
+ import type { RangeData } from '@/types/FacetRange';
4
+ import type { SelectOption } from '@/types/Select';
5
+ import type { StringInput } from '@/types/Form';
6
+ import type { ValidationError, ValidationLength, ValidationResult, Validator } from '@/types/Validation';
7
+
8
+ import { priceToNumber } from '@/utilities/format';
9
+
10
+ export const errorMessageDefault = 'Please enter a valid value.';
11
+
12
+ export const noError = {
13
+ message: '',
14
+ valid: true,
15
+ } as Readonly<ValidationResult>;
16
+
17
+ export const checkFormat = (format: RegExp) => {
6
18
  return (value: string): ValidationResult => {
7
- let result = {
8
- message: '',
9
- valid: true,
10
- };
19
+ let result = noError;
11
20
 
12
21
  if (!value.trim().match(format)) {
13
22
  result = {
@@ -18,68 +27,164 @@ export function checkFormat(format: RegExp) {
18
27
 
19
28
  return result;
20
29
  };
21
- }
30
+ };
22
31
 
23
- export function checkLength(minlength?: number, maxlength?: number) {
24
- return (value: string): ValidationResult => {
25
- const valid = getFieldLengthIsValid({
26
- maxlength,
27
- minlength,
28
- value,
29
- });
32
+ export const getErrorMessage = (errorFromProps: ValidationError, errorFromRef: ValidationError) => {
33
+ // error in props takes precedence over validation error
34
+ if (typeof errorFromProps === 'string' && errorFromProps.length > 0) return errorFromProps;
30
35
 
31
- let message = valid ? '' : errorMessageDefault;
36
+ return typeof errorFromRef === 'string' && errorFromRef.length > 0 ? errorFromRef : errorMessageDefault;
37
+ };
32
38
 
33
- if (maxlength && minlength) {
34
- message = `Please enter a value between ${minlength} and ${maxlength} characters in length.`;
39
+ export const getFieldHasError = (errorFromProps: ValidationError, errorFromRef: ValidationError) =>
40
+ errorFromProps !== false || errorFromRef !== false;
41
+
42
+ export const getFieldLengthIsValid = ({ maxlength, minlength, value }: ValidationLength) => {
43
+ const tooShort = maxlength && value.length > maxlength;
44
+ const tooLong = minlength && value.length < minlength;
45
+
46
+ return !tooShort && !tooLong;
47
+ };
48
+
49
+ export const getMaxRangeIsValid = ({ min }: Pick<RangeData, 'min'>, type?: 'price') => {
50
+ return (value: string): ValidationResult => {
51
+ let newMax: number | null = type === 'price' ? priceToNumber(value) : Number(value);
52
+ newMax = !isNaN(newMax) ? newMax : null;
53
+ if (min && newMax) {
54
+ if (newMax >= min) {
55
+ return {
56
+ message: '',
57
+ valid: true,
58
+ };
59
+ } else {
60
+ return {
61
+ message: `Must be greater than min`,
62
+ valid: false,
63
+ };
64
+ }
65
+ } else {
66
+ return noError;
35
67
  }
68
+ };
69
+ };
36
70
 
37
- return {
38
- message,
39
- valid,
40
- };
71
+ export const getMinRangeIsValid = ({ max }: Pick<RangeData, 'max'>, type?: 'price') => {
72
+ return (value: string): ValidationResult => {
73
+ let newMin: number | null = type === 'price' ? priceToNumber(value) : Number(value);
74
+ newMin = !isNaN(newMin) ? newMin : null;
75
+ if (max && newMin) {
76
+ if (newMin <= max) {
77
+ return {
78
+ message: '',
79
+ valid: true,
80
+ };
81
+ } else {
82
+ return {
83
+ message: `Must be less than max`,
84
+ valid: false,
85
+ };
86
+ }
87
+ } else {
88
+ return noError;
89
+ }
41
90
  };
42
- }
91
+ };
43
92
 
44
- export const getFieldLengthIsValid = ({
93
+ export const getSelectOptionsFromStrings = (strings: string[]) =>
94
+ strings.map(
95
+ (option) =>
96
+ ({
97
+ label: option,
98
+ value: option,
99
+ } as SelectOption)
100
+ );
101
+
102
+ export const handleFieldValidation = ({
103
+ error,
104
+ errorFromProps,
45
105
  maxlength,
46
106
  minlength,
107
+ validators,
47
108
  value,
48
109
  }: {
110
+ error: Ref<ValidationError>;
111
+ errorFromProps: ValidationError;
49
112
  maxlength?: number;
50
113
  minlength?: number;
51
- value: string;
114
+ validators?: Validator[];
115
+ value: Ref<string>;
52
116
  }) => {
53
- const tooShort = maxlength && value.length > maxlength;
54
- const tooLong = minlength && value.length < minlength;
117
+ // error in props takes precedence over validation error
55
118
 
56
- return !tooShort && !tooLong;
57
- };
119
+ error.value = errorFromProps ? errorFromProps : false;
58
120
 
59
- export const errorMessageDefault = 'Please enter a valid value.';
121
+ if (!error.value && validators) {
122
+ const validation = validateProperty(value.value, validators);
123
+
124
+ if (!validation.valid) {
125
+ error.value = validation.message;
126
+ }
127
+ }
128
+
129
+ if (!error.value && (maxlength || minlength)) {
130
+ const lengthvalidation = validateLength({
131
+ maxlength,
132
+ minlength,
133
+ value: value.value,
134
+ });
135
+
136
+ if (!lengthvalidation.valid) {
137
+ error.value = lengthvalidation.message;
138
+ }
139
+ }
140
+ };
60
141
 
61
- export function validateFieldsFromRefs(fields: { [key: string]: Ref<StringField | null> }) {
142
+ export function validateFieldsFromRefs(fields: { [key: string]: Ref<StringInput | null> }) {
62
143
  let valid = true;
63
144
 
64
145
  for (const key in fields) {
65
146
  if (fields[key].value?.required) {
66
147
  const value = fields[key].value?.value;
67
-
68
148
  valid = valid && !!value && value.trim() !== '';
69
149
  }
70
150
 
71
151
  const error = fields[key].value?.error;
72
-
73
152
  valid = valid && !error;
74
153
  }
75
154
 
76
155
  return valid;
77
156
  }
78
157
 
158
+ export const validateLength = ({ maxlength, minlength, value }: ValidationLength): ValidationResult => {
159
+ const response = {
160
+ message: '',
161
+ valid: true,
162
+ };
163
+
164
+ response.valid = getFieldLengthIsValid({
165
+ maxlength,
166
+ minlength,
167
+ value,
168
+ });
169
+
170
+ if (response.valid) return response;
171
+
172
+ response.message = errorMessageDefault;
173
+
174
+ if (maxlength && minlength) {
175
+ response.message = `Please enter a value between ${minlength} and ${maxlength} characters in length.`;
176
+ } else if (maxlength) {
177
+ response.message = `Please enter a value no more than ${maxlength} characters in length.`;
178
+ } else if (minlength) {
179
+ response.message = `Please enter a value no less than ${minlength} characters in length.`;
180
+ }
181
+
182
+ return response;
183
+ };
184
+
79
185
  export function validateProperty(value: string, validators: ((value: string) => ValidationResult)[]): ValidationResult {
80
186
  for (const validator of validators) {
81
187
  const validation = validator(value);
82
-
83
188
  if (!validation.valid) {
84
189
  return validation;
85
190
  }
package/package.json CHANGED
@@ -49,15 +49,16 @@
49
49
  "scripts": {
50
50
  "build": "run-p type-check build-only",
51
51
  "build-only": "vite build && cp -r src/assets/css/ dist/css/ && cp -r src/types/ dist/types/ && cp -r src/utilities/ dist/utilities/",
52
- "build-storybook": "storybook build",
52
+ "build-storybook": "storybook build && cp -r src/assets/css/realm/ storybook-static/public/",
53
53
  "coverage": "vitest run --coverage",
54
54
  "lint": "eslint . --ext .js,.ts,.vue --ignore-path .gitignore",
55
55
  "lint:fix": "eslint . --ext .js,.ts,.vue --ignore-path .gitignore --fix",
56
+ "preview": "npm run build-storybook && npx http-server -a localhost storybook-static/",
56
57
  "sb": "npm run storybook",
57
58
  "storybook": "storybook dev -p 6006",
58
59
  "test": "vitest run",
59
60
  "type-check": "vue-tsc --noEmit"
60
61
  },
61
62
  "type": "module",
62
- "version": "2.0.0"
63
+ "version": "2.0.2"
63
64
  }
@@ -1,10 +0,0 @@
1
- import { openBlock as c, createElementBlock as e } from "vue";
2
- import { _ as o } from "./index-c5bc4216.js";
3
- const t = {}, n = { d: "M5.5 17v-7h2v7h-2Zm6 0v-7h2v7h-2Zm-9 4v-2h20v2h-20Zm15-4v-7h2v7h-2Zm-15-9V6l10-5 10 5v2h-20Zm4.45-2h11.1L12.5 3.25 6.95 6Z" };
4
- function h(r, m) {
5
- return c(), e("path", n);
6
- }
7
- const s = /* @__PURE__ */ o(t, [["render", h]]);
8
- export {
9
- s as default
10
- };
@@ -1,10 +0,0 @@
1
- import { openBlock as e, createElementBlock as o } from "vue";
2
- import { _ as t } from "./index-c5bc4216.js";
3
- const c = {}, r = { d: "M11 13H5v-2h6V5h2v6h6v2h-6v6h-2v-6Z" };
4
- function n(_, s) {
5
- return e(), o("path", r);
6
- }
7
- const d = /* @__PURE__ */ t(c, [["render", n]]);
8
- export {
9
- d as default
10
- };
@@ -1,22 +0,0 @@
1
- import { openBlock as c, createElementBlock as l, Fragment as n, createElementVNode as e } from "vue";
2
- import { _ as o } from "./index-c5bc4216.js";
3
- const t = {}, r = /* @__PURE__ */ e("path", { d: "M18.111 18V6.389H16V18h2.111Z" }, null, -1), d = /* @__PURE__ */ e("path", {
4
- "clip-rule": "evenodd",
5
- d: "M14.485 18.111 11.325 6.5h-3.54L4.626 18.111H6.5L7.111 15H12l.5 3.111h1.985Zm-3.16-4.889h-3.54L9 8.5h1l1.326 4.722Z",
6
- "fill-rule": "evenodd"
7
- }, null, -1), a = /* @__PURE__ */ e("path", {
8
- "clip-rule": "evenodd",
9
- d: "M8.038 1h7.924c.983 0 1.795 0 2.457.054.687.056 1.318.177 1.912.479a4.889 4.889 0 0 1 2.136 2.136c.302.594.423 1.225.479 1.912C23 6.243 23 7.055 23 8.038v7.924c0 .983 0 1.795-.054 2.457-.056.687-.177 1.318-.479 1.912a4.889 4.889 0 0 1-2.136 2.136c-.594.302-1.225.423-1.912.479-.662.054-1.474.054-2.457.054H8.038c-.983 0-1.795 0-2.457-.054-.687-.056-1.318-.177-1.912-.479a4.889 4.889 0 0 1-2.136-2.136c-.302-.594-.423-1.225-.479-1.912C1 17.757 1 16.945 1 15.962V8.038c0-.983 0-1.795.054-2.457.056-.687.177-1.318.479-1.912a4.889 4.889 0 0 1 2.136-2.136c.594-.302 1.225-.423 1.912-.479C6.243 1 7.055 1 8.038 1ZM5.78 3.046c-.536.044-.81.123-1 .22-.46.235-1.28 1.053-1.514 1.513-.097.191-.176.465-.22 1.001C3 6.33 3 7.042 3 8.09v7.822c0 .98 0 1.665.038 2.2H3l.044.089.002.02c.044.536.123.81.22 1 .235.46.609 1.28 1.069 1.514.19.097.91.176 1.445.22.358.03.784.04 1.326.044L7.11 21h8.8c1.047 0 1.759 0 2.309-.046.536-.044.81-.123 1-.22.46-.235 1.28-1.053 1.514-1.513.097-.191.176-.465.22-1.001.045-.55.046-1.262.046-2.309V8.09c0-1.047 0-1.759-.046-2.309-.044-.536-.123-.81-.22-1-.235-.46-1.053-1.28-1.513-1.514-.191-.097-.465-.176-1.001-.22C17.67 3 16.958 3 15.91 3H8.09c-1.047 0-1.759.001-2.309.046Z",
10
- "fill-rule": "evenodd"
11
- }, null, -1);
12
- function _(h, s) {
13
- return c(), l(n, null, [
14
- r,
15
- d,
16
- a
17
- ], 64);
18
- }
19
- const p = /* @__PURE__ */ o(t, [["render", _]]);
20
- export {
21
- p as default
22
- };