@dcrackel/hematournamentui 1.0.707 → 1.0.709

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 (61) hide show
  1. package/AGENTS.md +118 -118
  2. package/README.md +132 -106
  3. package/dist/HemaTournamentUI-lib.es.js +7111 -7006
  4. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  5. package/dist/hematournamentui-tailwind.css +198 -412
  6. package/package.json +10 -10
  7. package/src/index.js +2 -0
  8. package/src/mocks/getDEBouts-42Fencers.js +4 -4
  9. package/src/mocks/getDEBouts-TableOf128.js +37 -0
  10. package/src/mocks/getDEBouts-TableOf64.js +35 -0
  11. package/src/stories/Atoms/Input/BaseInput.test.js +97 -85
  12. package/src/stories/Atoms/Input/BaseInput.vue +149 -150
  13. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -0
  14. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -0
  15. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -0
  16. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +3 -2
  17. package/src/stories/Molecules/Modals/EditBoutModal/EditBoutModal.vue +1 -1
  18. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -0
  19. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -0
  20. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -0
  21. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -278
  22. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +60 -98
  23. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +103 -4
  24. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +256 -215
  25. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -92
  26. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -128
  27. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +14 -4
  28. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -20
  29. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -0
  30. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -133
  31. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +2 -2
  32. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +131 -3
  33. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +287 -89
  34. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -0
  35. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -0
  36. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -0
  37. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -0
  38. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -0
  39. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -0
  40. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +57 -0
  41. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +65 -0
  42. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +31 -29
  43. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +146 -18
  44. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +52 -2
  45. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +53 -17
  46. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +39 -0
  47. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -0
  48. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -0
  49. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +14 -0
  50. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -0
  51. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +13 -3
  52. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -172
  53. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -103
  54. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -0
  55. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -213
  56. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -0
  57. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -29
  58. package/src/stories/Organisms/Cards/TableauBoutCard/SpacingConfig.js +0 -111
  59. package/src/stories/Organisms/Containers/TableauLines/SpacingConfig.js +0 -162
  60. package/src/stories/Organisms/Containers/TableauLines/TableauLines.stories.js +0 -21
  61. package/src/stories/Organisms/Containers/TableauLines/TableauLines.vue +0 -78
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dcrackel/hematournamentui",
3
3
  "private": false,
4
- "version": "1.0.707",
4
+ "version": "1.0.709",
5
5
  "type": "module",
6
6
  "main": "dist/HemaTournamentUI-lib.umd.js",
7
7
  "module": "dist/HemaTournamentUI-lib.es.js",
@@ -58,18 +58,18 @@
58
58
  },
59
59
  "devDependencies": {
60
60
  "@babel/eslint-parser": "^7.28.6",
61
- "@chromatic-com/storybook": "^5.3.0",
61
+ "@chromatic-com/storybook": "^5.5.1",
62
62
  "@eslint/eslintrc": "^3.3.5",
63
- "@storybook/addon-docs": "^10.5.6",
64
- "@storybook/addon-links": "^10.5.6",
65
- "@storybook/react": "^10.5.6",
66
- "@storybook/vue3-vite": "^10.5.6",
63
+ "@storybook/addon-docs": "^10.6.2",
64
+ "@storybook/addon-links": "^10.6.2",
65
+ "@storybook/react": "^10.6.2",
66
+ "@storybook/vue3-vite": "^10.6.2",
67
67
  "@tailwindcss/cli": "^4.3.0",
68
68
  "@tailwindcss/postcss": "^4.3.0",
69
69
  "@typescript-eslint/eslint-plugin": "^8.59.3",
70
70
  "@typescript-eslint/parser": "^8.59.3",
71
71
  "@vitejs/plugin-vue": "^6.0.7",
72
- "@vitest/coverage-v8": "^4.1.6",
72
+ "@vitest/coverage-v8": "^4.1.11",
73
73
  "@vue/test-utils": "^2.4.10",
74
74
  "@vuepic/vue-datepicker": "^12.1.0",
75
75
  "autoprefixer": "^10.5.0",
@@ -77,17 +77,17 @@
77
77
  "eslint": "^9.9.0",
78
78
  "eslint-plugin-jest": "^29.15.2",
79
79
  "eslint-plugin-prettier": "^5.5.5",
80
- "eslint-plugin-storybook": "^10.5.6",
80
+ "eslint-plugin-storybook": "^10.6.2",
81
81
  "eslint-plugin-vue": "^10.9.1",
82
82
  "happy-dom": "^20.9.0",
83
83
  "patch-package": "^8.0.1",
84
84
  "postcss": "^8.5.14",
85
85
  "prettier": "^3.9.6",
86
- "storybook": "^10.5.6",
86
+ "storybook": "^10.6.2",
87
87
  "storybook-vue3-router": "^7.0.0",
88
88
  "tailwindcss": "^4.3.0",
89
89
  "vite": "^8.0.13",
90
- "vitest": "^4.1.6",
90
+ "vitest": "^4.1.11",
91
91
  "vue-eslint-parser": "^10.4.0"
92
92
  },
93
93
  "overrides": {
package/src/index.js CHANGED
@@ -17,6 +17,8 @@ export { default as DatePicker } from './stories/Organisms/ComplexInputs/DatePic
17
17
  export { default as DropDownMenu } from './stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue';
18
18
  export { default as GridContainer } from './stories/Organisms/Grids/GridContainer.vue';
19
19
  export { default as PoolGrid } from './stories/Organisms/Containers/PoolGrid/PoolGrid.vue';
20
+ export { default as BracketDirectorAssignmentCard } from './stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
21
+ export { default as BracketDirectorAssignmentPanel } from './stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue';
20
22
  export { default as ImageCropper } from './stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue';
21
23
  export { default as TextBoxEditor } from './stories/Organisms/ComplexInputs/TextBoxEditor/TextBoxEditor.vue';
22
24
  export { default as EditEventWizard } from './stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue';
@@ -3150,7 +3150,7 @@ const getDEWithBouts42Fencers = {
3150
3150
  TimeLeft: 0,
3151
3151
  CurrentPass: 0,
3152
3152
  MaxPasses: 0,
3153
- RoundLabel: 'Quarterfinal',
3153
+ RoundLabel: 'Table of 8',
3154
3154
  DEBoutId: 57,
3155
3155
  DENextBoutId: 61,
3156
3156
  LoserNextBoutId: null,
@@ -3201,7 +3201,7 @@ const getDEWithBouts42Fencers = {
3201
3201
  TimeLeft: 0,
3202
3202
  CurrentPass: 0,
3203
3203
  MaxPasses: 0,
3204
- RoundLabel: 'Quarterfinal',
3204
+ RoundLabel: 'Table of 8',
3205
3205
  DEBoutId: 58,
3206
3206
  DENextBoutId: 61,
3207
3207
  LoserNextBoutId: null,
@@ -3252,7 +3252,7 @@ const getDEWithBouts42Fencers = {
3252
3252
  TimeLeft: 0,
3253
3253
  CurrentPass: 0,
3254
3254
  MaxPasses: 0,
3255
- RoundLabel: 'Quarterfinal',
3255
+ RoundLabel: 'Table of 8',
3256
3256
  DEBoutId: 59,
3257
3257
  DENextBoutId: 62,
3258
3258
  LoserNextBoutId: null,
@@ -3303,7 +3303,7 @@ const getDEWithBouts42Fencers = {
3303
3303
  TimeLeft: 0,
3304
3304
  CurrentPass: 0,
3305
3305
  MaxPasses: 0,
3306
- RoundLabel: 'Quarterfinal',
3306
+ RoundLabel: 'Table of 8',
3307
3307
  DEBoutId: 60,
3308
3308
  DENextBoutId: 62,
3309
3309
  LoserNextBoutId: null,
@@ -0,0 +1,37 @@
1
+ import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
+
3
+ const roundLabelRules = [
4
+ { matches: boutId => boutId >= 1 && boutId <= 64, label: 'Table of 128' },
5
+ { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
6
+ { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
7
+ { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
8
+ { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
9
+ { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
10
+ { matches: boutId => boutId === 127, label: 'Final' }
11
+ ];
12
+
13
+ const getCurrentRoundLabel = bout => {
14
+ const boutId = Number(bout.DEBoutId);
15
+ return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
16
+ };
17
+
18
+ const normalizeTableOf128Bout = bout => ({
19
+ ...bout,
20
+ RoundLabel: getCurrentRoundLabel(bout)
21
+ });
22
+
23
+ const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
24
+
25
+ const getDEBoutsTableOf128 = {
26
+ ...historicalSixtySevenFencerBracket,
27
+ message: 'Current Table of 128 fixture retrieved',
28
+ pools: [
29
+ {
30
+ ...historicalPool,
31
+ Bouts: historicalPool.Bouts.map(normalizeTableOf128Bout)
32
+ },
33
+ ...otherPools
34
+ ]
35
+ };
36
+
37
+ export default getDEBoutsTableOf128;
@@ -0,0 +1,35 @@
1
+ import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
+
3
+ const FIRST_TABLE_OF_64_BOUT_ID = 65;
4
+
5
+ const roundLabelRules = [
6
+ { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
7
+ { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
8
+ { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
9
+ { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
10
+ { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
11
+ { matches: boutId => boutId === 127, label: 'Final' }
12
+ ];
13
+
14
+ const isTableOf64Bout = bout => Number(bout.DEBoutId) >= FIRST_TABLE_OF_64_BOUT_ID;
15
+
16
+ const getCurrentRoundLabel = bout => {
17
+ const boutId = Number(bout.DEBoutId);
18
+ return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
19
+ };
20
+
21
+ const normalizeTableOf64Bout = bout => ({
22
+ ...bout,
23
+ RoundLabel: getCurrentRoundLabel(bout)
24
+ });
25
+
26
+ const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
27
+ const tableOf64Bouts = historicalPool.Bouts.filter(isTableOf64Bout).map(normalizeTableOf64Bout);
28
+
29
+ const getDEBoutsTableOf64 = {
30
+ ...historicalSixtySevenFencerBracket,
31
+ message: 'Current Table of 64 fixture retrieved',
32
+ pools: [{ ...historicalPool, Bouts: tableOf64Bouts }, ...otherPools]
33
+ };
34
+
35
+ export default getDEBoutsTableOf64;
@@ -1,85 +1,97 @@
1
- import BaseInput from './BaseInput.vue';
2
- import { mount } from '@vue/test-utils';
3
- import { describe, expect, test, it } from 'vitest';
4
-
5
- describe('BaseInput.vue', () => {
6
- it('renders with default props', () => {
7
- const wrapper = mount(BaseInput);
8
- const inputElement = wrapper.find('input');
9
-
10
- expect(inputElement.attributes('placeholder')).toBe('');
11
- expect(wrapper.vm.internalValue).toBe('');
12
- });
13
-
14
- it('initializes with the correct value prop', () => {
15
- const initialValue = 'Hello, World!';
16
- const wrapper = mount(BaseInput, {
17
- props: {
18
- value: initialValue
19
- }
20
- });
21
-
22
- expect(wrapper.vm.internalValue).toBe(initialValue);
23
- });
24
-
25
- it('updates internalValue when value prop changes', async () => {
26
- const wrapper = mount(BaseInput);
27
- await wrapper.setProps({ value: 'New Value' });
28
-
29
- expect(wrapper.vm.internalValue).toBe('New Value');
30
- });
31
-
32
- it('emits update:value with a delay upon input', async () => {
33
- const wrapper = mount(BaseInput);
34
- const inputElement = wrapper.find('input');
35
-
36
- await inputElement.setValue('Test Input');
37
-
38
- // TODO: Handle timer-related functionality with vitest
39
- await new Promise(resolve => setTimeout(resolve, 350));
40
-
41
- expect(wrapper.emitted('update:value')).toBeTruthy();
42
- expect(wrapper.emitted('update:value')[0]).toEqual(['Test Input']);
43
- });
44
-
45
- it('applies formInput styles when type is formInput', () => {
46
- const wrapper = mount(BaseInput, {
47
- props: {
48
- type: 'formInput',
49
- invalid: false
50
- }
51
- });
52
- const inputElement = wrapper.find('input');
53
- expect(inputElement.classes()).toContain('border');
54
- expect(inputElement.classes()).toContain('rounded-lg');
55
- expect(inputElement.classes()).toContain('text-secondary');
56
- expect(inputElement.classes()).toContain('placeholder:text-xs');
57
- expect(inputElement.classes()).toContain('text-sm');
58
- expect(inputElement.classes()).toContain('p-2');
59
- expect(inputElement.classes()).toContain('w-full');
60
- expect(inputElement.classes()).toContain('border-dropdownSelect');
61
- });
62
-
63
- it('applies border-alarm class when invalid is true', () => {
64
- const wrapper = mount(BaseInput, {
65
- props: {
66
- type: 'formInput',
67
- invalid: true
68
- }
69
- });
70
- const inputElement = wrapper.find('input');
71
- expect(inputElement.classes()).toContain('border-alarm');
72
- });
73
-
74
- it('does not apply border-alarm class when invalid is false', () => {
75
- const wrapper = mount(BaseInput, {
76
- props: {
77
- type: 'formInput',
78
- invalid: false
79
- }
80
- });
81
- const inputElement = wrapper.find('input');
82
- expect(inputElement.classes()).not.toContain('border-alarm');
83
- expect(inputElement.classes()).toContain('border-dropdownSelect');
84
- });
85
- });
1
+ import BaseInput from './BaseInput.vue';
2
+ import { mount } from '@vue/test-utils';
3
+ import { describe, expect, test, it } from 'vitest';
4
+
5
+ describe('BaseInput.vue', () => {
6
+ it('renders with default props', () => {
7
+ const wrapper = mount(BaseInput);
8
+ const inputElement = wrapper.find('input');
9
+
10
+ expect(inputElement.attributes('placeholder')).toBe('');
11
+ expect(wrapper.vm.internalValue).toBe('');
12
+ });
13
+
14
+ it('initializes with the correct value prop', () => {
15
+ const initialValue = 'Hello, World!';
16
+ const wrapper = mount(BaseInput, {
17
+ props: {
18
+ value: initialValue
19
+ }
20
+ });
21
+
22
+ expect(wrapper.vm.internalValue).toBe(initialValue);
23
+ });
24
+
25
+ it('updates internalValue when value prop changes', async () => {
26
+ const wrapper = mount(BaseInput);
27
+ await wrapper.setProps({ value: 'New Value' });
28
+
29
+ expect(wrapper.vm.internalValue).toBe('New Value');
30
+ });
31
+
32
+ it('emits the latest value synchronously before the next action', () => {
33
+ const wrapper = mount(BaseInput);
34
+ const inputElement = wrapper.find('input');
35
+
36
+ inputElement.element.value = 'Test Input';
37
+ inputElement.element.dispatchEvent(new Event('input', { bubbles: true }));
38
+ expect(wrapper.emitted('update:value')).toEqual([['Test Input']]);
39
+ });
40
+
41
+ it('applies input border styles when addBorder is enabled', () => {
42
+ const wrapper = mount(BaseInput, {
43
+ props: {
44
+ addBorder: true,
45
+ invalid: false
46
+ }
47
+ });
48
+ const inputElement = wrapper.find('input');
49
+ expect(inputElement.classes()).toContain('border');
50
+ expect(inputElement.classes()).toContain('rounded-lg');
51
+ expect(inputElement.classes()).toContain('text-quinary');
52
+ expect(inputElement.classes()).toContain('text-sm');
53
+ expect(inputElement.classes()).toContain('p-2');
54
+ expect(inputElement.classes()).toContain('w-full');
55
+ expect(inputElement.classes()).toContain('border-dropdownSelect');
56
+ });
57
+
58
+ it('applies border-alarm class when invalid is true', () => {
59
+ const wrapper = mount(BaseInput, {
60
+ props: {
61
+ addBorder: true,
62
+ invalid: true
63
+ }
64
+ });
65
+ const inputElement = wrapper.find('input');
66
+ expect(inputElement.classes()).toContain('border-alarm');
67
+ });
68
+
69
+ it('does not apply border-alarm class when invalid is false', () => {
70
+ const wrapper = mount(BaseInput, {
71
+ props: {
72
+ addBorder: true,
73
+ invalid: false
74
+ }
75
+ });
76
+ const inputElement = wrapper.find('input');
77
+ expect(inputElement.classes()).not.toContain('border-alarm');
78
+ expect(inputElement.classes()).toContain('border-dropdownSelect');
79
+ });
80
+
81
+ it('delivers a cleared value before the input is unmounted', () => {
82
+ const wrapper = mount(BaseInput, { props: { value: '123', inputType: 'number' } });
83
+ const input = wrapper.find('input');
84
+ input.element.value = '';
85
+ input.element.dispatchEvent(new Event('input', { bubbles: true }));
86
+ expect(wrapper.emitted('update:value')).toEqual([['']]);
87
+ wrapper.unmount();
88
+ });
89
+
90
+ it('does not emit edits while disabled', () => {
91
+ const wrapper = mount(BaseInput, { props: { disabled: true, value: 'original' } });
92
+ const input = wrapper.find('input');
93
+ input.element.value = 'changed';
94
+ input.element.dispatchEvent(new Event('input', { bubbles: true }));
95
+ expect(wrapper.emitted('update:value')).toBeUndefined();
96
+ });
97
+ });
@@ -1,150 +1,149 @@
1
- <template>
2
- <div :class="iconName ? '' : ''">
3
- <input
4
- ref="input"
5
- v-model="internalValue"
6
- :class="baseClasses"
7
- :placeholder="placeholder"
8
- :type="inputType"
9
- :disabled="disabled"
10
- @input="onInput"
11
- @focus="onFocus"
12
- @blur="onBlur"
13
- />
14
- </div>
15
- </template>
16
-
17
- <script>
18
- const sizeClasses = {
19
- xs: ' text-xs',
20
- sm: ' text-sm',
21
- md: ' text-md',
22
- lg: ' text-lg',
23
- xl: ' text-xl',
24
- '2xl': ' text-2xl',
25
- '3xl': ' text-3xl',
26
- '4xl': ' text-4xl',
27
- '5xl': ' text-5xl'
28
- };
29
-
30
- export default {
31
- name: 'BaseInput',
32
- emits: ['update:value'],
33
- props: {
34
- placeholder: {
35
- type: [String, Number],
36
- default: ''
37
- },
38
- value: {
39
- type: [String, Number],
40
- default: ''
41
- },
42
- inputType: {
43
- type: String,
44
- default: 'text'
45
- },
46
- invalid: {
47
- type: Boolean,
48
- default: false
49
- },
50
- addBorder: {
51
- type: Boolean,
52
- default: false
53
- },
54
- iconName: {
55
- type: String,
56
- default: ''
57
- },
58
- disabled: {
59
- type: Boolean,
60
- default: false
61
- },
62
- iconStyle: {
63
- type: String,
64
- default: 'fa-solid',
65
- validator: value => ['fa-solid', 'fa-regular', 'fa-light', 'fa-thing'].includes(value)
66
- },
67
- size: {
68
- type: String,
69
- default: 'sm',
70
- validator: value => ['', ' ', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'].includes(value)
71
- },
72
- centerInput: {
73
- type: Boolean,
74
- default: false
75
- },
76
- iconColor: {
77
- type: String,
78
- default: 'primary',
79
- validator: value =>
80
- ['neutral', 'primary', 'secondary', 'tertiary', 'quaternary', 'quinary', 'bright', 'white', 'alarm'].includes(
81
- value
82
- )
83
- },
84
- allowPercent: {
85
- type: Boolean,
86
- default: false
87
- }
88
- },
89
- data() {
90
- return {
91
- internalValue: this.value,
92
- currentPlaceholder: this.placeholder,
93
- timeout: null
94
- };
95
- },
96
- computed: {
97
- baseClasses() {
98
- let classes = 'appearance-none focus:outline-none w-full text-sm rounded-lg text-quinary';
99
- if (this.addBorder) {
100
- classes += this.invalid ? ' border border-alarm p-2' : ' border border-dropdownSelect p-2';
101
- }
102
-
103
- if (this.centerInput) classes += ' text-center';
104
-
105
- classes += sizeClasses[this.size] || '';
106
-
107
- return classes;
108
- }
109
- },
110
- watch: {
111
- value(newValue) {
112
- this.internalValue = newValue;
113
- }
114
- },
115
- methods: {
116
- onInput(event) {
117
- let sanitizedValue = event.target.value;
118
-
119
- if (this.inputType === 'number') {
120
- sanitizedValue = sanitizedValue.replace(/\D/g, '');
121
- }
122
- if (this.inputType === 'text') {
123
- this.allowPercent
124
- ? (sanitizedValue = sanitizedValue.replace(/[\\'"=*<>|#$^(){}\[\]]/g, ''))
125
- : (sanitizedValue = sanitizedValue.replace(/[\\'"=*<>|#$%^(){}\[\]]/g, ''));
126
- }
127
-
128
- this.internalValue = sanitizedValue;
129
-
130
- clearTimeout(this.timeout);
131
- this.timeout = setTimeout(() => {
132
- this.$emit('update:value', this.internalValue);
133
- }, 300);
134
- },
135
- onFocus() {
136
- this.currentPlaceholder = '';
137
- },
138
- onBlur() {
139
- if (!this.internalValue) {
140
- this.currentPlaceholder = this.placeholder;
141
- }
142
- },
143
- focus() {
144
- if (this.$refs.input) {
145
- this.$refs.input.focus();
146
- }
147
- }
148
- }
149
- };
150
- </script>
1
+ <template>
2
+ <div :class="iconName ? '' : ''">
3
+ <input
4
+ ref="input"
5
+ :value="internalValue"
6
+ :class="baseClasses"
7
+ :placeholder="placeholder"
8
+ :type="inputType"
9
+ :disabled="disabled"
10
+ :aria-invalid="invalid"
11
+ @input="onInput"
12
+ @focus="onFocus"
13
+ @blur="onBlur"
14
+ />
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ const sizeClasses = {
20
+ xs: ' text-xs',
21
+ sm: ' text-sm',
22
+ md: ' text-md',
23
+ lg: ' text-lg',
24
+ xl: ' text-xl',
25
+ '2xl': ' text-2xl',
26
+ '3xl': ' text-3xl',
27
+ '4xl': ' text-4xl',
28
+ '5xl': ' text-5xl'
29
+ };
30
+
31
+ export default {
32
+ name: 'BaseInput',
33
+ emits: ['update:value'],
34
+ props: {
35
+ placeholder: {
36
+ type: [String, Number],
37
+ default: ''
38
+ },
39
+ value: {
40
+ type: [String, Number],
41
+ default: ''
42
+ },
43
+ inputType: {
44
+ type: String,
45
+ default: 'text'
46
+ },
47
+ invalid: {
48
+ type: Boolean,
49
+ default: false
50
+ },
51
+ addBorder: {
52
+ type: Boolean,
53
+ default: false
54
+ },
55
+ iconName: {
56
+ type: String,
57
+ default: ''
58
+ },
59
+ disabled: {
60
+ type: Boolean,
61
+ default: false
62
+ },
63
+ iconStyle: {
64
+ type: String,
65
+ default: 'fa-solid',
66
+ validator: value => ['fa-solid', 'fa-regular', 'fa-light', 'fa-thing'].includes(value)
67
+ },
68
+ size: {
69
+ type: String,
70
+ default: 'sm',
71
+ validator: value => ['', ' ', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'].includes(value)
72
+ },
73
+ centerInput: {
74
+ type: Boolean,
75
+ default: false
76
+ },
77
+ iconColor: {
78
+ type: String,
79
+ default: 'primary',
80
+ validator: value =>
81
+ ['neutral', 'primary', 'secondary', 'tertiary', 'quaternary', 'quinary', 'bright', 'white', 'alarm'].includes(
82
+ value
83
+ )
84
+ },
85
+ allowPercent: {
86
+ type: Boolean,
87
+ default: false
88
+ }
89
+ },
90
+ data() {
91
+ return {
92
+ internalValue: this.value,
93
+ currentPlaceholder: this.placeholder
94
+ };
95
+ },
96
+ computed: {
97
+ baseClasses() {
98
+ let classes = 'appearance-none focus:outline-none w-full text-sm rounded-lg text-quinary';
99
+ if (this.addBorder) {
100
+ classes += this.invalid ? ' border border-alarm p-2' : ' border border-dropdownSelect p-2';
101
+ }
102
+
103
+ if (this.centerInput) classes += ' text-center';
104
+
105
+ classes += sizeClasses[this.size] || '';
106
+
107
+ return classes;
108
+ }
109
+ },
110
+ watch: {
111
+ value(newValue) {
112
+ this.internalValue = newValue;
113
+ }
114
+ },
115
+ methods: {
116
+ onInput(event) {
117
+ if (this.disabled) return;
118
+ const input = event.target;
119
+ let sanitizedValue = input.value;
120
+
121
+ if (this.inputType === 'number') {
122
+ sanitizedValue = sanitizedValue.replace(/\D/g, '');
123
+ }
124
+ if (this.inputType === 'text') {
125
+ this.allowPercent
126
+ ? (sanitizedValue = sanitizedValue.replace(/[\\'"=*<>|#$^(){}\[\]]/g, ''))
127
+ : (sanitizedValue = sanitizedValue.replace(/[\\'"=*<>|#$%^(){}\[\]]/g, ''));
128
+ }
129
+
130
+ this.internalValue = sanitizedValue;
131
+ input.value = sanitizedValue;
132
+ this.$emit('update:value', sanitizedValue);
133
+ },
134
+ onFocus() {
135
+ this.currentPlaceholder = '';
136
+ },
137
+ onBlur() {
138
+ if (!this.internalValue) {
139
+ this.currentPlaceholder = this.placeholder;
140
+ }
141
+ },
142
+ focus() {
143
+ if (this.$refs.input) {
144
+ this.$refs.input.focus();
145
+ }
146
+ }
147
+ }
148
+ };
149
+ </script>