whelk-ui 0.0.2 → 0.0.3

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 (30) hide show
  1. package/README.md +2 -0
  2. package/package.json +4 -3
  3. package/playwright.config.ts +79 -0
  4. package/src/components/add_object/AddObject.vue +40 -0
  5. package/src/components/card/CardComponent.vue +14 -0
  6. package/src/components/card/card_footer/CardFooter.vue +19 -0
  7. package/src/components/card/card_header/CardHeader.vue +16 -0
  8. package/src/components/check_box/CheckBox.vue +42 -0
  9. package/src/components/datetime/DatetimeComponent.vue +147 -0
  10. package/src/components/drop_down/DropDown.vue +104 -0
  11. package/src/components/drop_down/drop_down_item/DropDownItem.vue +58 -0
  12. package/src/components/form_group/FormGroup.spec.ts +16 -0
  13. package/src/components/form_group/FormGroup.vue +19 -0
  14. package/src/components/number_input/NumberInput.spec.ts +712 -0
  15. package/src/components/number_input/NumberInput.vue +264 -0
  16. package/src/components/password_input/PasswordInput.vue +166 -0
  17. package/src/components/render_error_message/RenderErrorMessage.spec.ts +0 -0
  18. package/src/components/render_error_message/RenderErrorMessage.vue +32 -0
  19. package/src/components/switch/SwitchComponent.vue +152 -0
  20. package/src/components/text_area/TextArea.vue +151 -0
  21. package/src/components/text_input/TextInput.vue +178 -0
  22. package/src/components/tool_tip/ToolTip.vue +96 -0
  23. package/src/utils/enums/ObjectTitleCaseEnums.ts +17 -0
  24. package/src/utils/enums/ObjectTypeEnums.ts +15 -0
  25. package/src/utils/interfaces/DocumentItemInterface.ts +5 -0
  26. package/src/utils/interfaces/DropDownItemsInterface.ts +8 -0
  27. package/src/utils/interfaces/FolderItemInterface.ts +4 -0
  28. package/src/utils/interfaces/MenuItemInterface.ts +10 -0
  29. package/tests/example.spec.ts +18 -0
  30. package/vite.config.ts +40 -1
@@ -0,0 +1,264 @@
1
+ <script setup lang="ts">
2
+ import FormGroup from '../form_group/FormGroup.vue';
3
+ import {computed, ref} from 'vue';
4
+ import ToolTip from '../tool_tip/ToolTip.vue';
5
+ import RenderErrorMessage from "../render_error_message/RenderErrorMessage.vue";
6
+
7
+ // Define Props
8
+ const props = defineProps({
9
+ label: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ maxValue: {
14
+ type: Number,
15
+ default: Number.MAX_SAFE_INTEGER,
16
+ },
17
+ minValue: {
18
+ type: Number,
19
+ default: Number.MIN_SAFE_INTEGER,
20
+ },
21
+ stepIncrement: {
22
+ type: Number,
23
+ default: 1,
24
+ },
25
+ tooltipMessage: {
26
+ type: String,
27
+ required: false,
28
+ default: '',
29
+ },
30
+ tooltipTitle: {
31
+ type: String,
32
+ required: false,
33
+ default: '',
34
+ },
35
+ });
36
+
37
+ // Define Models
38
+ const model = defineModel({
39
+ type: Number,
40
+ default: 0,
41
+ });
42
+
43
+ // Define Ref
44
+ const errorMessage = ref('');
45
+
46
+ // Computed
47
+ const getId = computed(() => {
48
+ // Return an id made up of input- + title
49
+ return 'input-' + props.label?.toLowerCase()?.replace(' ', '-');
50
+ });
51
+
52
+ const isMax = computed(() => {
53
+ return model.value >= props.maxValue;
54
+ });
55
+
56
+ const isMin = computed(() => {
57
+ return model.value <= props.minValue;
58
+ });
59
+
60
+ // FUNCTIONS
61
+ function applyDecrement() {
62
+ // Reset error messages
63
+ errorMessage.value = '';
64
+
65
+ // Do nothing if we reach the lowest value
66
+ if (isMin.value) {
67
+ errorMessage.value = "Reached Lowest Value";
68
+ return;
69
+ }
70
+
71
+ // We don't want to increment past the min value - so we define a local increment
72
+ let increment = Math.abs(props.minValue - model.value);
73
+ increment =
74
+ increment < Math.abs(props.stepIncrement)
75
+ ? increment
76
+ : Math.abs(props.stepIncrement);
77
+
78
+ // Apply the incrementation, and make sure it is not bigger than the max value
79
+ let update_value = model.value - increment;
80
+ update_value =
81
+ update_value > props.maxValue ? props.maxValue : update_value;
82
+
83
+ //Mutate the value
84
+ model.value = update_value;
85
+ }
86
+
87
+ function applyIncrement() {
88
+ // Reset error messages
89
+ errorMessage.value = '';
90
+
91
+ // Do nothing if we reach the highest value
92
+ if (isMax.value) {
93
+ errorMessage.value = "Reached Maxium Value";
94
+ return;
95
+ }
96
+
97
+ // We don't want to increment past the max value - so we define a local increment
98
+ let increment = Math.abs(props.maxValue - model.value);
99
+ increment =
100
+ increment < Math.abs(props.stepIncrement)
101
+ ? increment
102
+ : Math.abs(props.stepIncrement);
103
+
104
+ // Apply the incrementation, and make sure it is not lower than the min value
105
+ let update_value = model.value + increment;
106
+ update_value =
107
+ update_value < props.minValue ? props.minValue : update_value;
108
+
109
+ // Mutate the value
110
+ model.value = update_value;
111
+ }
112
+
113
+ function manualUpdate(event: Event) {
114
+ // Reset error messages
115
+ errorMessage.value = '';
116
+
117
+ // Handle manual event
118
+ const target = event.target as HTMLInputElement;
119
+ let update_value: number | undefined = target?.valueAsNumber;
120
+ if (update_value === undefined) {
121
+ errorMessage.value = "Model is undefined - default 0";
122
+ update_value = 0;
123
+ }
124
+ if (isNaN(update_value)) {
125
+ errorMessage.value = "Not a number - default 0";
126
+ update_value = 0;
127
+ }
128
+
129
+
130
+ // Make sure the values fall within the min and max
131
+ update_value =
132
+ update_value > props.maxValue ? props.maxValue : update_value;
133
+ update_value =
134
+ update_value < props.minValue ? props.minValue : update_value;
135
+
136
+ // Mutate the value
137
+ model.value = update_value;
138
+ }
139
+ </script>
140
+
141
+ <template>
142
+ <FormGroup class="number-input">
143
+ <label :for="getId">
144
+ <ToolTip
145
+ v-if="props.tooltipMessage !== ''"
146
+ :title="tooltipTitle"
147
+ :message="tooltipMessage"
148
+ :id="getId"
149
+ />
150
+ {{ label }}
151
+ </label>
152
+ <div class="number-input-row">
153
+ <button
154
+ type="button"
155
+ class="negative"
156
+ v-bind:aria-label="`Decrement current value of ${model} by ${props.stepIncrement}`"
157
+ v-bind:disabled="isMin"
158
+ v-on:click="applyDecrement"
159
+ >
160
+ -
161
+ </button>
162
+ <input
163
+ type="number"
164
+ aria-label="Current value picked"
165
+ aria-describedby="helper-text-explanation"
166
+ v-model="model"
167
+ v-on:change="manualUpdate($event)"
168
+ v-on:keyup="manualUpdate($event)"
169
+ />
170
+ <button
171
+ type="button"
172
+ class="positive"
173
+ v-bind:aria-label="`Increment current value of ${model} by ${props.stepIncrement}`"
174
+ v-bind:disabled="isMax"
175
+ v-on:click="applyIncrement"
176
+ >
177
+ +
178
+ </button>
179
+ </div>
180
+ <RenderErrorMessage :error-message="errorMessage"/>
181
+ </FormGroup>
182
+ </template>
183
+
184
+ <style scoped>
185
+ .number-input {
186
+ > label {
187
+ margin-bottom: 6px;
188
+ }
189
+
190
+ > .number-input-row {
191
+ display: grid;
192
+ grid-template-columns: 3rem minmax(0, 1fr) 3rem;
193
+
194
+ > .negative {
195
+ border-radius: var(--border-radius) 0 0 var(--border-radius);
196
+ border-width: var(--border-width) 0 var(--border-width) var(--border-width);
197
+ }
198
+
199
+ > input {
200
+ border-style: var(--border-style);
201
+ border-width: var(--border-width);
202
+ border-radius: 0;
203
+ border-color: var(--border);
204
+ box-sizing: border-box;
205
+ -moz-box-sizing: border-box;
206
+ -webkit-box-sizing: border-box;
207
+
208
+ &:focus {
209
+ border-color: var(--secondary);
210
+ border-width: 2px;
211
+ outline: none;
212
+ padding: calc(0.5rem - 1px);
213
+ }
214
+ }
215
+
216
+ > .positive {
217
+ border-radius: 0 var(--border-radius) var(--border-radius) 0;
218
+ border-width: var(--border-width) var(--border-width) var(--border-width) 0;
219
+ }
220
+ }
221
+
222
+ &.compact {
223
+ > label {
224
+ font-size: 1rem;
225
+ line-height: 1.25rem;
226
+ margin-bottom: 2px;
227
+
228
+ @media (--large-screen) {
229
+ font-size: 0.75rem;
230
+ line-height: 1rem;
231
+ }
232
+ }
233
+
234
+ > .number-input-row {
235
+ grid-template-columns: 2.5rem minmax(0, 1fr) 2.5rem;
236
+
237
+ > input {
238
+
239
+
240
+ font-size: 1.25rem;
241
+ line-height: 1.5rem;
242
+ padding: 0.25rem;
243
+
244
+ @media (--large-screen) {
245
+ font-size: 1rem;
246
+ line-height: 1.25rem;
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ /* Hide the spin buttons in WebKit browsers */
254
+ input::-webkit-outer-spin-button,
255
+ input::-webkit-inner-spin-button {
256
+ -webkit-appearance: none;
257
+ margin: 0;
258
+ }
259
+
260
+ /* Hide spin buttons in Firefox */
261
+ input[type='number'] {
262
+ -moz-appearance: textfield;
263
+ }
264
+ </style>
@@ -0,0 +1,166 @@
1
+ <script setup lang="ts">
2
+ import FormGroup from '../form_group/FormGroup.vue';
3
+ import {computed, ref} from 'vue';
4
+ import RenderErrorMessage from '../render_error_message/RenderErrorMessage.vue';
5
+ import ToolTip from '../tool_tip/ToolTip.vue';
6
+
7
+ // Define Emits
8
+ const emit = defineEmits(['isValid']);
9
+
10
+ // Define Props
11
+ const props = defineProps({
12
+ isRequired: {
13
+ type: Boolean,
14
+ default: false,
15
+ },
16
+ label: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ minLength: {
21
+ type: Number,
22
+ default: 0,
23
+ required: false,
24
+ validator: (val) => !Number.isNaN(val),
25
+ },
26
+ placeholderText: {
27
+ type: String,
28
+ required: false,
29
+ default: '',
30
+ },
31
+ tooltipMessage: {
32
+ type: String,
33
+ required: false,
34
+ default: '',
35
+ },
36
+ tooltipTitle: {
37
+ type: String,
38
+ required: false,
39
+ default: '',
40
+ },
41
+ });
42
+
43
+ // Define Models
44
+ const model = defineModel();
45
+
46
+ // Define ref
47
+ const hasError = ref(false);
48
+ const errorMessage = ref('');
49
+
50
+ // Computed
51
+ const getId = computed(() => {
52
+ // Return an id made up of input- + title
53
+ return 'input-' + props.label?.toLowerCase()?.replace(' ', '-');
54
+ });
55
+
56
+ function checkValidation() {
57
+ // Fall back to defaults
58
+ hasError.value = false;
59
+ errorMessage.value = '';
60
+
61
+ // Get the length of the model and if NaN fallback to 0
62
+ let modelLength: number = Number(model?.value?.toString().length);
63
+ modelLength = isNaN(modelLength) ? 0 : modelLength;
64
+
65
+ // Check the first "required" condition
66
+ if (props.isRequired && modelLength === 0) {
67
+ hasError.value = true;
68
+ errorMessage.value = 'This field is required';
69
+ }
70
+
71
+ // Check the minimum "required" condition
72
+ if (props.minLength > 0 && modelLength < props.minLength) {
73
+ hasError.value = true;
74
+ errorMessage.value = `This field has a minimum length ${modelLength} / ${props.minLength}`;
75
+ }
76
+
77
+ // Set the defined ref and tell parent
78
+ emit('isValid', !hasError.value);
79
+ }
80
+ </script>
81
+
82
+ <template>
83
+ <FormGroup class="text-input">
84
+ <label :for="getId">
85
+ <ToolTip
86
+ v-if="props.tooltipMessage !== ''"
87
+ :title="tooltipTitle"
88
+ :message="tooltipMessage"
89
+ :id="getId"
90
+ />
91
+ {{
92
+ label
93
+ }}<span v-if="isRequired" aria-description="Field is required"
94
+ >*</span
95
+ >
96
+ </label>
97
+ <input
98
+ :id="getId"
99
+ type="password"
100
+ :name="props.label"
101
+ :placeholder="props.placeholderText"
102
+ v-model="model"
103
+ v-on:keyup="checkValidation"
104
+ v-on:focusout="checkValidation"
105
+ />
106
+ <RenderErrorMessage :error-message="errorMessage"/>
107
+ </FormGroup>
108
+ </template>
109
+
110
+ <style scoped>
111
+ .text-input {
112
+
113
+ > label {
114
+ margin-bottom: 6px;
115
+ }
116
+
117
+ > span {
118
+ color: var(--text-red);
119
+ }
120
+
121
+ > input {
122
+ border-style: var(--border-style);
123
+ border-width: var(--border-width);
124
+ border-radius: var(--border-radius);
125
+ border-color: var(--border);
126
+ box-sizing: border-box;
127
+ -moz-box-sizing: border-box;
128
+ -webkit-box-sizing: border-box;
129
+
130
+ &:focus {
131
+ border-color: var(--secondary);
132
+ border-width: 2px;
133
+ outline: none;
134
+ padding: calc(0.5rem - 1px);
135
+ }
136
+ }
137
+
138
+ &.compact {
139
+ > label {
140
+ font-size: 1rem;
141
+ line-height: 1.25rem;
142
+ margin-bottom: 2px;
143
+
144
+ @media (--large-screen) {
145
+ font-size: 0.75rem;
146
+ line-height: 1rem;
147
+ }
148
+ }
149
+
150
+ > input {
151
+ font-size: 1.25rem;
152
+ line-height: 1.5rem;
153
+ padding: 0.25rem;
154
+
155
+ &:focus {
156
+ padding: calc(0.25rem - 1px);
157
+ }
158
+
159
+ @media (--large-screen) {
160
+ font-size: 1rem;
161
+ line-height: 1.25rem;
162
+ }
163
+ }
164
+ }
165
+ }
166
+ </style>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <span class="render-error-message">
6
+ <slot />
7
+ </span>
8
+ </template>
9
+
10
+ <style scoped>
11
+ .render-error-message {
12
+ color: var(--text-red);
13
+ font-weight: lighter;
14
+ font-size: 1rem;
15
+ line-height: 1.125rem;
16
+ padding: 0;
17
+ margin: 0;
18
+
19
+ > svg {
20
+ transform: translateY(0.125rem);
21
+ }
22
+
23
+ @media (--large-screen) {
24
+ font-size: 0.75rem;
25
+ }
26
+ }
27
+
28
+ p:before {
29
+ content: ' ';
30
+ white-space: pre;
31
+ }
32
+ </style>
@@ -0,0 +1,152 @@
1
+ <script setup lang="ts">
2
+ import {computed} from "vue";
3
+ import FormGroup from "../form_group/FormGroup.vue";
4
+ import ToolTip from "../tool_tip/ToolTip.vue";
5
+
6
+ // Define props
7
+ const props = defineProps({
8
+ label: {
9
+ type: String,
10
+ required: true,
11
+ },
12
+ offText: {
13
+ type: String,
14
+ required: false,
15
+ default: "",
16
+ },
17
+ onText: {
18
+ type: String,
19
+ required: false,
20
+ default: "",
21
+ },
22
+ tooltipMessage: {
23
+ type: String,
24
+ required: false,
25
+ default: '',
26
+ },
27
+ tooltipTitle: {
28
+ type: String,
29
+ required: false,
30
+ default: '',
31
+ },
32
+ })
33
+
34
+ // Define computed
35
+ const getClass = computed(() => {
36
+ return model.value ? "switch on" : "switch off";
37
+ });
38
+
39
+ const getId = computed(() => {
40
+ // Return an id made up of input- + title
41
+ return 'input-' + props.label?.toLowerCase()?.replace(' ', '-');
42
+ });
43
+
44
+ const getText = computed(() => {
45
+ return model.value ? props.onText : props.offText;
46
+ });
47
+
48
+ // Define model
49
+ const model = defineModel({
50
+ type: Boolean
51
+ });
52
+
53
+ // Define functions
54
+ function switchClicked() {
55
+ model.value = !model.value;
56
+ }
57
+ </script>
58
+
59
+ <template>
60
+
61
+ <FormGroup class="switch-component">
62
+ <label :for="getId">
63
+ <ToolTip
64
+ v-if="props.tooltipMessage !== ''"
65
+ :title="tooltipTitle"
66
+ :message="tooltipMessage"
67
+ :id="getId"
68
+ />
69
+ {{
70
+ label
71
+ }}
72
+ </label>
73
+ <div
74
+ v-on:click="switchClicked"
75
+ :class="getClass"
76
+ >
77
+ <div class="switch-text">{{ getText }}</div>
78
+ <div class="switch-block"></div>
79
+ </div>
80
+ </FormGroup>
81
+ </template>
82
+
83
+ <style scoped>
84
+ .switch-component {
85
+ width: 100%;
86
+
87
+ > .switch {
88
+ display: grid;
89
+ grid-template-columns: [on] 2rem [text] minmax(0, 1fr) [off] 2rem;
90
+ border-style: var(--border-style);
91
+ border-width: var(--border-width);
92
+ border-radius: var(--border-radius);
93
+ border-color: var(--border);
94
+ box-sizing: border-box;
95
+ -moz-box-sizing: border-box;
96
+ -webkit-box-sizing: border-box;
97
+
98
+ > .switch-block {
99
+ width: 2rem;
100
+ height: 2rem;
101
+ background-color: hotpink;
102
+ }
103
+
104
+ > .switch-text {
105
+ display: grid;
106
+ grid-area: text;
107
+ text-align: center;
108
+ align-items: center;
109
+ overflow: hidden;
110
+ white-space: nowrap;
111
+ text-overflow: ellipsis;
112
+ }
113
+
114
+ &.on {
115
+ > .switch-block {
116
+ grid-area: on;
117
+ background-color: var(--success);
118
+ }
119
+ }
120
+
121
+ &.off {
122
+ > .switch-block {
123
+ grid-area: off;
124
+ background-color: var(--info);
125
+ }
126
+ }
127
+ }
128
+
129
+ &.compact {
130
+ > label {
131
+ font-size: 1rem;
132
+ line-height: 1.25rem;
133
+ }
134
+
135
+ > .switch {
136
+
137
+ grid-template-columns: [on] 1rem [text] minmax(0, 1fr) [off] 1rem;
138
+
139
+ > .switch-block {
140
+ width: 1rem;
141
+ height: 1rem;
142
+ }
143
+
144
+ > .switch-text {
145
+ font-size: 0.75rem;
146
+ line-height: 1rem;
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ </style>