tide-design-system 2.0.0 → 2.0.1

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 (101) hide show
  1. package/dist/{IconAccountBalance-91cf067b.js → IconAccountBalance-0c552905.js} +1 -1
  2. package/dist/{IconAdd-95c51c0e.js → IconAdd-50766b43.js} +1 -1
  3. package/dist/{IconAi-08172540.js → IconAi-3f1ad2fe.js} +1 -1
  4. package/dist/{IconAlignSpace-9ab2bdf2.js → IconAlignSpace-804660fd.js} +1 -1
  5. package/dist/{IconApplePay-1ee6317b.js → IconApplePay-75b9850c.js} +1 -1
  6. package/dist/{IconArrowBack-a2226a94.js → IconArrowBack-3bde832a.js} +1 -1
  7. package/dist/{IconArrowForward-e1ca9957.js → IconArrowForward-0582323f.js} +1 -1
  8. package/dist/{IconArrowRight-53382084.js → IconArrowRight-c37875ce.js} +1 -1
  9. package/dist/{IconAssignment-332c2b2b.js → IconAssignment-b9549d2c.js} +1 -1
  10. package/dist/{IconAwardStar-1ca35385.js → IconAwardStar-a1b0a840.js} +1 -1
  11. package/dist/{IconBookmark-49b42628.js → IconBookmark-26e82ff4.js} +1 -1
  12. package/dist/{IconCalendarMonth-22c938d7.js → IconCalendarMonth-55806114.js} +1 -1
  13. package/dist/{IconCall-989a47fc.js → IconCall-dfc29049.js} +1 -1
  14. package/dist/{IconCheck-a3467b47.js → IconCheck-1d4a84f9.js} +1 -1
  15. package/dist/{IconChevronLeft-c1d90bb7.js → IconChevronLeft-8a9307a6.js} +1 -1
  16. package/dist/{IconChevronRight-ad47891f.js → IconChevronRight-46940da9.js} +1 -1
  17. package/dist/{IconClear-7c8fad4e.js → IconClear-1dc6a4df.js} +1 -1
  18. package/dist/{IconClose-4b6c5aed.js → IconClose-93976f13.js} +1 -1
  19. package/dist/{IconCycle-99d40f2d.js → IconCycle-b8bc8146.js} +1 -1
  20. package/dist/{IconDelete-446eff93.js → IconDelete-2970a09b.js} +1 -1
  21. package/dist/{IconDiamond-765a7d8d.js → IconDiamond-ba4bae95.js} +1 -1
  22. package/dist/{IconEdit-ce05f3b5.js → IconEdit-3612d58e.js} +1 -1
  23. package/dist/{IconError-7983707a.js → IconError-643df67d.js} +1 -1
  24. package/dist/{IconExpandContent-8b6e2125.js → IconExpandContent-71109869.js} +1 -1
  25. package/dist/{IconExpandLess-9e23f1e9.js → IconExpandLess-9c6c12b6.js} +1 -1
  26. package/dist/{IconExpandMore-ded098a7.js → IconExpandMore-fcd92910.js} +1 -1
  27. package/dist/{IconFacebook-3cab65a8.js → IconFacebook-b0d62bbb.js} +1 -1
  28. package/dist/{IconFavorite-5fe831f4.js → IconFavorite-d59dfc0b.js} +1 -1
  29. package/dist/{IconFavoriteFilled-58fa0bf7.js → IconFavoriteFilled-fd2c5862.js} +1 -1
  30. package/dist/{IconFormatBold-889f6b8b.js → IconFormatBold-6f1aa639.js} +1 -1
  31. package/dist/{IconFormatItalic-103eba00.js → IconFormatItalic-a82848b2.js} +1 -1
  32. package/dist/{IconFormatListBulleted-4c824025.js → IconFormatListBulleted-4f4a0a99.js} +1 -1
  33. package/dist/{IconForum-abc2fe82.js → IconForum-194dedbd.js} +1 -1
  34. package/dist/{IconGoogle-281b6d80.js → IconGoogle-46e6c4f1.js} +1 -1
  35. package/dist/{IconGooglePay-cc83c5c8.js → IconGooglePay-090b70a8.js} +1 -1
  36. package/dist/{IconGrid-ef763745.js → IconGrid-0f7c079c.js} +1 -1
  37. package/dist/{IconHelp-2ad33f76.js → IconHelp-e91f36a5.js} +1 -1
  38. package/dist/{IconInfo-5878df77.js → IconInfo-df1f06a5.js} +1 -1
  39. package/dist/{IconInsertText-0c62badf.js → IconInsertText-e371c1cd.js} +1 -1
  40. package/dist/{IconInstagram-69e21cfb.js → IconInstagram-af4ea628.js} +1 -1
  41. package/dist/{IconIosShare-be5f117c.js → IconIosShare-7253c1df.js} +1 -1
  42. package/dist/{IconLayout-c1ffbcd3.js → IconLayout-225d9fb6.js} +1 -1
  43. package/dist/{IconLinkedIn-807ef6f5.js → IconLinkedIn-ab7e007c.js} +1 -1
  44. package/dist/{IconLocalShipping-2c6d71e0.js → IconLocalShipping-2c8ac96c.js} +1 -1
  45. package/dist/{IconLock-9178e816.js → IconLock-b4e1bd5d.js} +1 -1
  46. package/dist/{IconMail-0123a7c6.js → IconMail-50eca4b4.js} +1 -1
  47. package/dist/{IconMenu-33ed2d99.js → IconMenu-7789a1f9.js} +1 -1
  48. package/dist/{IconMoreHoriz-c281099f.js → IconMoreHoriz-35960212.js} +1 -1
  49. package/dist/{IconNotifications-89f80e0f.js → IconNotifications-a6690c77.js} +1 -1
  50. package/dist/{IconOpenInNew-87ad0454.js → IconOpenInNew-8b812d7f.js} +1 -1
  51. package/dist/{IconPalette-7ee5b40c.js → IconPalette-18b6e766.js} +1 -1
  52. package/dist/{IconPaypal-e311eadd.js → IconPaypal-75ff8ed5.js} +1 -1
  53. package/dist/{IconPerson-932fbcbc.js → IconPerson-3c8a4c27.js} +1 -1
  54. package/dist/{IconPhotoCamera-fdbd5767.js → IconPhotoCamera-0fbb9344.js} +1 -1
  55. package/dist/{IconPinterest-2d19c2eb.js → IconPinterest-28ae7c92.js} +1 -1
  56. package/dist/{IconPlayArrow-9837a5c0.js → IconPlayArrow-6d779a71.js} +1 -1
  57. package/dist/{IconRemove-29ef8f82.js → IconRemove-1f655305.js} +1 -1
  58. package/dist/{IconRoundedCorners-8ad194fc.js → IconRoundedCorners-1f9eba28.js} +1 -1
  59. package/dist/{IconSearch-5ff23d26.js → IconSearch-c6843cf3.js} +1 -1
  60. package/dist/{IconSell-0e0ecd20.js → IconSell-c7818ac8.js} +1 -1
  61. package/dist/{IconShare-47084765.js → IconShare-b81bc4cd.js} +1 -1
  62. package/dist/{IconShoppingCart-9d6495b3.js → IconShoppingCart-b6cc2022.js} +1 -1
  63. package/dist/{IconSms-5ba18382.js → IconSms-dc4c1454.js} +1 -1
  64. package/dist/{IconStar-ef69284b.js → IconStar-c4305bd6.js} +1 -1
  65. package/dist/{IconSwapVert-05e14e3d.js → IconSwapVert-00e66af6.js} +1 -1
  66. package/dist/{IconThreeDRotation-2433b2e8.js → IconThreeDRotation-b19a9312.js} +1 -1
  67. package/dist/{IconTune-3c6452f0.js → IconTune-dee47734.js} +1 -1
  68. package/dist/{IconTwitter-a634cef4.js → IconTwitter-2ab4e06b.js} +1 -1
  69. package/dist/{IconVideocam-5712435e.js → IconVideocam-0d0142aa.js} +1 -1
  70. package/dist/{IconViewInAr-d38a23d5.js → IconViewInAr-7cd92579.js} +1 -1
  71. package/dist/{IconVisibility-8cdf7151.js → IconVisibility-84655778.js} +1 -1
  72. package/dist/{IconWarning-b9e61180.js → IconWarning-cab4938a.js} +1 -1
  73. package/dist/{IconYoutube-92447826.js → IconYoutube-ab7e33d3.js} +1 -1
  74. package/dist/css/dynamic-utilities.css +0 -1
  75. package/dist/css/main.css +0 -1
  76. package/dist/css/utilities.css +9 -8
  77. package/dist/css/variables.css +21 -17
  78. package/dist/index-32686488.js +2106 -0
  79. package/dist/style.css +1 -1
  80. package/dist/tide-design-system.js +48 -0
  81. package/dist/types/FacetRange.ts +84 -0
  82. package/dist/types/Field.ts +0 -1
  83. package/dist/types/Form.ts +57 -0
  84. package/dist/types/Realm.ts +1 -0
  85. package/dist/types/Select.ts +6 -0
  86. package/dist/types/Storybook.ts +206 -1
  87. package/dist/types/Styles.ts +64 -30
  88. package/dist/types/Validation.ts +7 -1
  89. package/dist/utilities/format.ts +75 -39
  90. package/dist/utilities/storybook.ts +35 -1
  91. package/dist/utilities/validation.ts +139 -34
  92. package/package.json +1 -1
  93. package/dist/css/dynamic-inputs.css +0 -43
  94. package/dist/index-c5bc4216.js +0 -1910
  95. package/dist/tide2-design-system.js +0 -36
  96. package/dist/types/StorybookStyles.ts +0 -209
  97. package/dist/types/Vehicle.ts +0 -139
  98. package/dist/types/VehicleDetail.ts +0 -44
  99. package/dist/types/index.d.ts +0 -7
  100. package/dist/utilities/forms.ts +0 -47
  101. package/dist/utilities/media.ts +0 -77
@@ -22,18 +22,26 @@ export const CSS = {
22
22
  END: 'tide-axis2-end',
23
23
  START: 'tide-axis2-start',
24
24
  },
25
- BORDER: {
26
- BOTTOM: {
27
- ONE: 'tide-border-bottom-1',
28
- TWO: 'tide-border-bottom-2',
29
- },
30
- FULL: {
31
- ONE: 'tide-border-1',
32
- TWO: 'tide-border-2',
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',
33
36
  },
34
- LEFT: {
35
- ONE: 'tide-border-left-1',
36
- TWO: 'tide-border-left-2',
37
+ },
38
+ BORDER: {
39
+ COLOR: {
40
+ DEFAULT: 'tide-border',
41
+ FLOATING: 'tide-border-floating',
42
+ HIGH: 'tide-border-high',
43
+ LOW: 'tide-border-low',
44
+ PRIMARY: 'tide-border-primary',
37
45
  },
38
46
  RADIUS: {
39
47
  FULL: 'tide-radius-full',
@@ -41,13 +49,27 @@ export const CSS = {
41
49
  ONE: 'tide-radius-1',
42
50
  QUARTER: 'tide-radius-1/4',
43
51
  },
44
- RIGHT: {
45
- ONE: 'tide-border-right-1',
46
- TWO: 'tide-border-right-2',
47
- },
48
- TOP: {
49
- ONE: 'tide-border-top-1',
50
- TWO: 'tide-border-top-2',
52
+ SIDE: {
53
+ BOTTOM: {
54
+ ONE: 'tide-border-bottom-1',
55
+ TWO: 'tide-border-bottom-2',
56
+ },
57
+ FULL: {
58
+ ONE: 'tide-border-1',
59
+ TWO: 'tide-border-2',
60
+ },
61
+ LEFT: {
62
+ ONE: 'tide-border-left-1',
63
+ TWO: 'tide-border-left-2',
64
+ },
65
+ RIGHT: {
66
+ ONE: 'tide-border-right-1',
67
+ TWO: 'tide-border-right-2',
68
+ },
69
+ TOP: {
70
+ ONE: 'tide-border-top-1',
71
+ TWO: 'tide-border-top-2',
72
+ },
51
73
  },
52
74
  },
53
75
  BOX_SIZING: {
@@ -60,17 +82,18 @@ export const CSS = {
60
82
  CURSOR: {
61
83
  NOT_ALLOWED: 'tide-cursor-not-allowed',
62
84
  POINTER: 'tide-cursor-pointer',
85
+ TEXT: 'tide-cursor-text',
63
86
  },
64
87
  DISPLAY: {
65
88
  BLOCK: 'tide-display-block',
66
89
  CONTENTS: 'tide-contents',
67
90
  FLEX: 'tide-display-flex',
68
91
  GRID: 'tide-display-grid',
69
- HIDDEN: 'tide-display-hidden',
70
92
  INITIAL: 'initial',
71
93
  INLINE: 'tide-display-inline',
72
94
  INLINE_BLOCK: 'tide-display-inline-block',
73
95
  INLINE_FLEX: 'tide-display-inline-flex',
96
+ NONE: 'tide-display-none',
74
97
  },
75
98
  FLEX: {
76
99
  DIRECTION: {
@@ -90,6 +113,17 @@ export const CSS = {
90
113
  WRAP: 'tide-flex-wrap',
91
114
  },
92
115
  FONT: {
116
+ COLOR: {
117
+ PRIMARY: 'tide-font-on-primary',
118
+ SECONDARY: 'tide-font-on-secondary',
119
+ SURFACE: {
120
+ BRAND: 'tide-font-on-surface-brand',
121
+ DEFAULT: 'tide-font-on-surface',
122
+ INVERSE: 'tide-font-on-surface-inverse',
123
+ VARIANT: 'tide-font-on-surface-variant',
124
+ VARIANT_INVERSE: 'tide-font-on-surface-variant-inverse',
125
+ },
126
+ },
93
127
  SIZE: {
94
128
  FOURTEEN: 'tide-font-14',
95
129
  SIXTEEN: 'tide-font-16',
@@ -303,7 +337,7 @@ export const cssSortOrder = [
303
337
  CSS.DISPLAY.CONTENTS,
304
338
  CSS.DISPLAY.FLEX,
305
339
  CSS.DISPLAY.GRID,
306
- CSS.DISPLAY.HIDDEN,
340
+ CSS.DISPLAY.NONE,
307
341
  CSS.DISPLAY.INITIAL,
308
342
  CSS.DISPLAY.INLINE,
309
343
  CSS.DISPLAY.INLINE_BLOCK,
@@ -379,16 +413,16 @@ export const cssSortOrder = [
379
413
  CSS.MARGIN.LEFT.TWO,
380
414
  CSS.MARGIN.LEFT.FOUR,
381
415
  CSS.MARGIN.LEFT.AUTO,
382
- CSS.BORDER.FULL.ONE,
383
- CSS.BORDER.FULL.TWO,
384
- CSS.BORDER.TOP.ONE,
385
- CSS.BORDER.TOP.TWO,
386
- CSS.BORDER.RIGHT.ONE,
387
- CSS.BORDER.RIGHT.TWO,
388
- CSS.BORDER.BOTTOM.ONE,
389
- CSS.BORDER.BOTTOM.TWO,
390
- CSS.BORDER.LEFT.ONE,
391
- CSS.BORDER.LEFT.TWO,
416
+ CSS.BORDER.SIDE.FULL.ONE,
417
+ CSS.BORDER.SIDE.FULL.TWO,
418
+ CSS.BORDER.SIDE.TOP.ONE,
419
+ CSS.BORDER.SIDE.TOP.TWO,
420
+ CSS.BORDER.SIDE.RIGHT.ONE,
421
+ CSS.BORDER.SIDE.RIGHT.TWO,
422
+ CSS.BORDER.SIDE.BOTTOM.ONE,
423
+ CSS.BORDER.SIDE.BOTTOM.TWO,
424
+ CSS.BORDER.SIDE.LEFT.ONE,
425
+ CSS.BORDER.SIDE.LEFT.TWO,
392
426
  CSS.BORDER.RADIUS.ONE,
393
427
  CSS.BORDER.RADIUS.HALF,
394
428
  CSS.BORDER.RADIUS.QUARTER,
@@ -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
@@ -64,7 +65,7 @@ export const doSomething = () => {
64
65
  alert('Did something.');
65
66
  };
66
67
 
67
- // Accept a KeyValue as the value of an object with a retrievable key as a Storybook argType
68
+ // Accept a KeyValue as the value of an object with a retrievable key as a Storybook argType.
68
69
  export const formatArgType = (collection: KeyValueNamed) => {
69
70
  const constant = getKey(collection);
70
71
  const keyValues: KeyValue = collection[constant];
@@ -203,6 +204,39 @@ export const formatSnippetMinimal = (code: string) => {
203
204
  return code.replace(/<[/]*template>/g, '');
204
205
  };
205
206
 
207
+ export const getConstantByValue = (value: string) => {
208
+ const compareRecursively = (basis: string, shape: string | object, depth: number = 0): string | void => {
209
+ let output;
210
+
211
+ Object.entries(shape).forEach((entry: string[]) => {
212
+ const key = entry[0];
213
+ const value = entry[1];
214
+ const type = typeof value;
215
+
216
+ if (type === 'string' && basis === value) {
217
+ output = key;
218
+ return;
219
+ } else if (type === 'object') {
220
+ const match = compareRecursively(basis, value, depth + 1);
221
+
222
+ if (match) {
223
+ output = `${key}.${match}`;
224
+ return;
225
+ }
226
+ }
227
+ });
228
+
229
+ return output;
230
+ };
231
+
232
+ const constant = compareRecursively(value, CSS);
233
+
234
+ return constant ? `CSS.${constant}` : undefined;
235
+ };
236
+
237
+ export const getConstantsByValues = (classNames: string[]) =>
238
+ classNames.map((className) => getConstantByValue(className));
239
+
206
240
  export const getKey = (input: any) => Object.keys(input)[0];
207
241
 
208
242
  // 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
@@ -59,5 +59,5 @@
59
59
  "type-check": "vue-tsc --noEmit"
60
60
  },
61
61
  "type": "module",
62
- "version": "2.0.0"
62
+ "version": "2.0.1"
63
63
  }
@@ -1,43 +0,0 @@
1
- .block-field .field {
2
- --input-outline-width: var(--border-w-1);
3
- outline: var(--input-outline-width) solid var(--ti-surface-border-rest);
4
- outline-offset: calc(var(--input-outline-width) * -1);
5
- color: var(--ti-surface-foreground);
6
- }
7
-
8
- .block-field .field::placeholder {
9
- color: var(--ti-surface-variant-foreground);
10
- }
11
-
12
- .block-field.error {
13
- color: var(--error-on-surface);
14
- }
15
-
16
- .block-field.error .field {
17
- outline-color: var(--error-border-rest);
18
- }
19
-
20
- .block-field.error:focus-within .field {
21
- outline-color: var(--error-border-rest);
22
- }
23
-
24
- .block-field.error:hover {
25
- color: var(--error-on-surface-variant);
26
- }
27
-
28
- .block-field.error:hover .field {
29
- outline-color: var(--error-border-high);
30
- }
31
-
32
- .block-field.disabled {
33
- opacity: 0.333;
34
- }
35
-
36
- .block-field:hover .field {
37
- outline-color: var(--ti-surface-border-high);
38
- }
39
-
40
- .block-field:focus-within .field {
41
- --input-outline-width: var(--border-w-2);
42
- outline-color: var(--ti-surface-border-high);
43
- }