@citruslime/ui 1.2.1-beta.0 → 2.0.0-beta.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 (52) hide show
  1. package/dist/.eslintrc.js +8 -2
  2. package/dist/@types/appUser.d.ts +1 -0
  3. package/dist/@types/components/grid/column.d.ts +2 -1
  4. package/dist/@types/components/header/index.d.ts +0 -1
  5. package/dist/@types/components/{header/navigation.d.ts → navigation/index.d.ts} +7 -4
  6. package/dist/@types/index.d.ts +1 -0
  7. package/dist/components/index.d.ts +17 -14
  8. package/dist/main.d.ts +1 -1
  9. package/dist/style.css +1 -1
  10. package/dist/theme.js +2 -4
  11. package/dist/ui.es.js +1 -1
  12. package/dist/ui.umd.js +1 -1
  13. package/package.json +7 -4
  14. package/src/components/accordion/cl-ui-accordion.vue +89 -0
  15. package/src/components/app/cl-ui-app.vue +35 -0
  16. package/src/components/button/{button.vue → cl-ui-button.vue} +26 -6
  17. package/src/components/calendar/cl-ui-calendar.vue +277 -0
  18. package/src/components/card/{card.vue → cl-ui-card.vue} +17 -1
  19. package/src/components/combo-box/cl-ui-combo-box.vue +357 -0
  20. package/src/components/combo-box/search-container/cl-ui-combo-box-search.vue +279 -0
  21. package/src/components/combo-box/search-container/{header-option/header-option.vue → header/cl-ui-combo-box-header.vue} +17 -2
  22. package/src/components/combo-box/search-container/selectable/cl-ui-combo-box-selectable.vue +99 -0
  23. package/src/components/footer/{footer.vue → cl-ui-footer.vue} +10 -2
  24. package/src/components/grid/cell/{cell.vue → cl-ui-grid-cell.vue} +90 -1
  25. package/src/components/grid/cl-ui-grid.vue +477 -0
  26. package/src/components/grid/filter/cl-ui-grid-filter.vue +270 -0
  27. package/src/components/grid/footer/{footer.vue → cl-ui-grid-footer.vue} +100 -5
  28. package/src/components/grid/header/cl-ui-grid-header.vue +76 -0
  29. package/src/components/grid/view-manager/cl-ui-grid-view-manager.vue +145 -0
  30. package/src/components/header/cl-ui-header.vue +11 -0
  31. package/src/components/header-helper/cl-ui-header-helper.vue +50 -0
  32. package/src/components/language-switcher/{language-switcher.vue → cl-ui-language-switcher.vue} +49 -3
  33. package/src/components/loading-spinner/cl-ui-loading-spinner.vue +16 -0
  34. package/src/components/login/{login.vue → cl-ui-login.vue} +101 -19
  35. package/src/components/modal/{modal.vue → cl-ui-modal.vue} +74 -2
  36. package/src/components/navigation/cl-ui-navigation.vue +124 -0
  37. package/src/components/notification/{notification.vue → cl-ui-notification.vue} +21 -2
  38. package/src/components/slider/cl-ui-slider.vue +145 -0
  39. package/src/components/accordion/accordion.vue +0 -30
  40. package/src/components/calendar/calendar.vue +0 -35
  41. package/src/components/combo-box/combo-box.vue +0 -79
  42. package/src/components/combo-box/search-container/search-container.vue +0 -57
  43. package/src/components/combo-box/search-container/selectable-option/selectable-option.vue +0 -27
  44. package/src/components/grid/filter/filter.vue +0 -93
  45. package/src/components/grid/grid.vue +0 -194
  46. package/src/components/grid/header/header.vue +0 -39
  47. package/src/components/grid/view-manager/view-manager.vue +0 -73
  48. package/src/components/header/header-helper/header-helper.vue +0 -95
  49. package/src/components/header/header.vue +0 -33
  50. package/src/components/header/navigation/navigation.vue +0 -84
  51. package/src/components/loading-spinner/loading-spinner.vue +0 -8
  52. package/src/components/slider/slider.vue +0 -41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citruslime/ui",
3
- "version": "1.2.1-beta.0",
3
+ "version": "2.0.0-beta.3",
4
4
  "author": {
5
5
  "name": "Citrus-Lime Ltd",
6
6
  "url": "https://citruslime.com"
@@ -24,11 +24,14 @@
24
24
  "build": "vite build && tsc -p tsconfig.build.json && copyfiles -f ../../theme.js ../../.eslintrc.js ../../.stylelintrc.js dist"
25
25
  },
26
26
  "dependencies": {
27
- "flatpickr": "^4.6.9",
28
- "vue": "^3.1.5",
29
- "vue-i18n": "^9.1.7"
27
+ "flatpickr": "^4.6.9"
30
28
  },
31
29
  "devDependencies": {
32
30
  "copyfiles": "^2.4.1"
31
+ },
32
+ "peerDependencies": {
33
+ "@iconify/vue": "^3.0.0",
34
+ "vue": "^3.2.11",
35
+ "vue-i18n": "^9.1.7"
33
36
  }
34
37
  }
@@ -0,0 +1,89 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch } from 'vue';
3
+
4
+ import { AccordionItem } from '../../@types';
5
+
6
+ interface Item {
7
+ id: string;
8
+ title: string;
9
+ open: boolean;
10
+ }
11
+
12
+ const props = withDefaults(defineProps<{
13
+ items: AccordionItem[];
14
+ exclusive?: boolean;
15
+ openFirst?: boolean;
16
+ }>(), {
17
+ exclusive: false,
18
+ openFirst: false
19
+ });
20
+
21
+ const itemList = ref<Item[]>([]);
22
+
23
+ /**
24
+ * Initialises the internal item list, from the provided list of items.
25
+ */
26
+ function initialiseItems (): void {
27
+ itemList.value = props.items.map(v => {
28
+ return {
29
+ id: v.id,
30
+ title: v.title,
31
+ open: false
32
+ };
33
+ });
34
+
35
+ if (props.openFirst && itemList.value.length > 0) {
36
+ itemList.value[0].open = true;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Opens/closes the item.
42
+ *
43
+ * @param {Item} item The item to open/close.
44
+ */
45
+ function toggleItem (item: Item): void {
46
+ if (!item.open) {
47
+ itemList.value.forEach(i => {
48
+ if (props.exclusive) {
49
+ i.open = false;
50
+ }
51
+ });
52
+ }
53
+
54
+ item.open = !item.open;
55
+ }
56
+
57
+ watch(() => props.items, () => initialiseItems(), {
58
+ deep: true,
59
+ immediate: true
60
+ });
61
+ </script>
62
+
63
+ <template>
64
+ <ul class="border border-grey-2">
65
+ <li v-for="(item, index) in itemList"
66
+ :key="index"
67
+ class="relative">
68
+ <div class="bg-white border border-grey-2 cursor-pointer ease-in-out focus:border-blue-light focus:outline-none font-semibold hover:scale-101 p-4 scale-100 transform-gpu transition-transform"
69
+ :class="{
70
+ 'border-b-2': item.open
71
+ }"
72
+ :tabindex="index + 1"
73
+ @click="toggleItem(item)"
74
+ @keyup.enter="toggleItem(item)">
75
+ {{ item.title }}
76
+ </div>
77
+
78
+ <div v-show="item.open"
79
+ class="bg-white border-b border-grey-2 p-6 text-sm">
80
+ <slot :name="item.id"></slot>
81
+ </div>
82
+
83
+ <icon class="absolute cursor-pointer right-4 top-4 transform transition-transform"
84
+ :class="{ 'rotate-180': item.open, 'rotate-0': !item.open }"
85
+ icon="ph:caret-down"
86
+ @click="toggleItem(item)" />
87
+ </li>
88
+ </ul>
89
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import { computed, useSlots } from 'vue';
3
+
4
+ import ClUiNotification from '../notification/cl-ui-notification.vue';
5
+
6
+ withDefaults(defineProps<{
7
+ removePadding?: boolean;
8
+ }>(), {
9
+ removePadding: false
10
+ });
11
+
12
+ const slots = useSlots();
13
+
14
+ const hasAppHeader = computed(() => slots?.header !== undefined);
15
+ </script>
16
+
17
+ <template>
18
+ <cl-ui-notification />
19
+
20
+ <div class="absolute flex flex-wrap h-screen max-h-screen max-w-full min-h-screen min-w-full w-full">
21
+ <slot name="header"></slot>
22
+
23
+ <div class="content-start flex flex-nowrap items-stretch max-h-screen relative w-full"
24
+ :class="{ 'pt-20' : hasAppHeader }">
25
+ <slot name="navigation"></slot>
26
+
27
+ <div class="flex-grow max-h-full overflow-x-hidden overflow-y-auto w-auto"
28
+ :class="{
29
+ 'md:p-8': !removePadding
30
+ }">
31
+ <slot></slot>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </template>
@@ -1,8 +1,32 @@
1
- <script lang="ts" src="./button"></script>
1
+ <script lang="ts">
2
+ export default {
3
+ inheritAttrs: false
4
+ };
5
+ </script>
2
6
 
3
- <style scoped src="./button.css"></style>
7
+ <script setup lang="ts">
8
+ import ClUiLoadingSpinner from '../loading-spinner/cl-ui-loading-spinner.vue';
9
+
10
+ type ButtonColour = 'default' | 'primary' | 'secondary' | 'danger' | 'blue';
11
+ type ButtonSize = 'small' | 'medium' | 'large';
12
+
13
+ withDefaults(defineProps<{
14
+ colour?: ButtonColour;
15
+ size?: ButtonSize;
16
+ loading?: boolean;
17
+ disabled?: boolean;
18
+ }>(), {
19
+ colour: 'default',
20
+ size: 'medium',
21
+ loading: false,
22
+ disabled: false
23
+ });
24
+ </script>
4
25
 
5
26
  <template>
27
+ <cl-ui-loading-spinner v-show="loading"
28
+ class="absolute" />
29
+
6
30
  <button v-show="!loading"
7
31
  v-bind="$attrs"
8
32
  class="align-middle border focus:outline-none font-semibold inline-block overflow-visible rounded shadow text-center"
@@ -21,8 +45,4 @@
21
45
  :disabled="disabled">
22
46
  <slot></slot>
23
47
  </button>
24
-
25
- <div v-show="loading"
26
- class="loading-spinner relative">
27
- </div>
28
48
  </template>
@@ -0,0 +1,277 @@
1
+ <script setup lang="ts">
2
+ import Flatpickr from 'flatpickr';
3
+ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
4
+ import { useI18n } from 'vue-i18n';
5
+
6
+ import { DateFormat } from '../../@types';
7
+
8
+ import { generateFixedDate, getLocale, replaceDateWithPlaceholders } from './calendar-utilities';
9
+
10
+ type CalendarType = 'date' | 'datetime';
11
+
12
+ const props = withDefaults(defineProps<{
13
+ date?: Date | null;
14
+ currentLocale?: string;
15
+ datePlaceholder?: string;
16
+ dateTimePlaceholder?: string;
17
+ type?: CalendarType;
18
+ disabled?: boolean;
19
+ }>(), {
20
+ date: () => null,
21
+ currentLocale: 'en-GB',
22
+ datePlaceholder: 'Select date.',
23
+ dateTimePlaceholder: 'Select date & time.',
24
+ type: 'datetime',
25
+ disabled: false
26
+ });
27
+
28
+ const emit = defineEmits({
29
+ 'update:date': null
30
+ });
31
+
32
+ const { d, getDateTimeFormat } = useI18n();
33
+
34
+ const flatpickrInstance = ref<Flatpickr.Instance | null>(null);
35
+ const element = ref<Node | null>(null);
36
+
37
+ const date = computed<string>(() => d(generateFixedDate(), props.type, props.currentLocale));
38
+ const dateFormat = computed(() => getDateTimeFormat(props.currentLocale)[props.type]);
39
+ const format = computed<string>(() => {
40
+ let hourPlaceholder = 'H';
41
+ let yearPlaceholder = 'Y';
42
+
43
+ if (dateFormat.value?.hour12 === true) {
44
+ hourPlaceholder = 'h';
45
+ }
46
+
47
+ if (dateFormat.value.year === '2-digit') {
48
+ yearPlaceholder = 'y';
49
+ }
50
+
51
+ const format = replaceDateWithPlaceholders(date.value, hourPlaceholder, yearPlaceholder);
52
+
53
+ return format;
54
+ });
55
+ const options = computed<Flatpickr.Options.Options>(() => {
56
+ const config: Flatpickr.Options.Options = {
57
+ ...Flatpickr.defaultConfig,
58
+ allowInput: true,
59
+ dateFormat: format.value,
60
+ disableMobile: true,
61
+ enableTime: props.type === DateFormat.DATETIME,
62
+ locale: getLocale(props.currentLocale),
63
+ minuteIncrement: 1,
64
+ monthSelectorType: 'static',
65
+ // eslint-disable-next-line @typescript-eslint/naming-convention
66
+ time_24hr: typeof dateFormat.value.hour12 === 'undefined' || dateFormat.value.hour12 === false
67
+ };
68
+
69
+ if (props.date !== null) {
70
+ config.defaultDate = props.date;
71
+ }
72
+
73
+ return config;
74
+ });
75
+
76
+ /**
77
+ * Emits an update to the model value, for the value provided from flatpickr.
78
+ *
79
+ * @param selectedDates List of raw selected values.
80
+ * @param _date Formatted selected value.
81
+ * @param _instance The current flatpickr instance.
82
+ */
83
+ function onChange (selectedDates: Date[], _date: string, _instance: Flatpickr.Instance): void {
84
+ emit('update:date', selectedDates.length > 0 ? selectedDates[0] : null);
85
+ }
86
+
87
+ /**
88
+ * Emits an update to the model value, to reset it.
89
+ */
90
+ function clearValue (): void {
91
+ emit('update:date', null);
92
+ }
93
+
94
+ /**
95
+ * Reset the value to the current day.
96
+ */
97
+ function resetValue (): void {
98
+ if (flatpickrInstance.value !== null) {
99
+ const date = new Date();
100
+
101
+ date.setHours(12, 0, 0, 0);
102
+
103
+ emit('update:date', date);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Creates a new flatpickr instance and adds an event handler for the OnChange event.
109
+ */
110
+ function createInstance (): void {
111
+ if (flatpickrInstance.value === null && element.value !== null) {
112
+ flatpickrInstance.value = Flatpickr(element.value as Node, options.value);
113
+
114
+ flatpickrInstance.value.config.onChange.push(onChange);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Updates the current flatpickr instance and re-adds an event handler for the OnChange event.
120
+ */
121
+ function updateInstance (): void {
122
+ if (flatpickrInstance.value !== null) {
123
+ flatpickrInstance.value.set(options.value);
124
+
125
+ flatpickrInstance.value.config.onChange.push(onChange);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Updates the selected value, in the flatpickr instance.
131
+ *
132
+ * @param value The new value.
133
+ */
134
+ function updateInstanceValue (value: Date): void {
135
+ if (flatpickrInstance.value !== null) {
136
+ flatpickrInstance.value.setDate(value, true);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Removes the selected value, in the flatpickr instance.
142
+ */
143
+ function clearInstanceValue (): void {
144
+ if (flatpickrInstance.value !== null) {
145
+ flatpickrInstance.value.clear();
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Destroys the current flatpickr instance.
151
+ */
152
+ function destroyInstance (): void {
153
+ if (flatpickrInstance.value !== null) {
154
+ flatpickrInstance.value.destroy();
155
+
156
+ flatpickrInstance.value = null;
157
+ }
158
+ }
159
+
160
+ watch(() => props.date, (newValue, oldValue) => {
161
+ if (newValue !== null && oldValue !== null) {
162
+ if (newValue.getTime() !== oldValue.getTime()) {
163
+ updateInstanceValue(newValue);
164
+ }
165
+ }
166
+ else if (newValue === null && oldValue !== null) {
167
+ clearInstanceValue();
168
+ }
169
+ }, {
170
+ deep: true
171
+ });
172
+
173
+ watch(() => options.value, updateInstance, {
174
+ deep: true
175
+ });
176
+
177
+ watch(() => props.type, () => {
178
+ destroyInstance();
179
+
180
+ nextTick(() => createInstance());
181
+ }, {
182
+ immediate: false
183
+ });
184
+
185
+ onMounted(() => createInstance());
186
+
187
+ onUnmounted(() => destroyInstance());
188
+ </script>
189
+
190
+ <style lang="postcss">
191
+ @import 'flatpickr/dist/flatpickr.css';
192
+
193
+ .flatpickr-calendar {
194
+ @apply shadow-lg;
195
+ }
196
+
197
+ .flatpickr-months {
198
+ @apply border-b border-grey-1 py-2 px-1;
199
+ }
200
+
201
+ .flatpickr-prev-month,
202
+ .flatpickr-next-month {
203
+ @apply mt-2;
204
+ }
205
+
206
+ .flatpickr-months .flatpickr-next-month:hover svg,
207
+ .flatpickr-months .flatpickr-prev-month:hover svg {
208
+ @apply text-primary-default fill-current;
209
+ }
210
+
211
+ .cur-month {
212
+ @apply mr-1;
213
+ }
214
+
215
+ .flatpickr-current-month span.cur-month:hover {
216
+ @apply bg-white;
217
+ }
218
+
219
+ /* stylelint-disable selector-class-pattern */
220
+
221
+ .flatpickr-innerContainer {
222
+ @apply mb-1;
223
+ }
224
+
225
+ /* stylelint-enable selector-class-pattern */
226
+
227
+ .flatpickr-weekdays {
228
+ @apply bg-off-white mb-1 py-2;
229
+ }
230
+
231
+ .flatpickr-day.selected {
232
+ @apply bg-primary-default border-primary-default hover:bg-primary-light hover:border-primary-light;
233
+ }
234
+
235
+ .flatpickr-day.today {
236
+ @apply border-primary-default;
237
+ }
238
+
239
+ .flatpickr-day.today:hover {
240
+ @apply bg-grey-1 text-grey-7;
241
+ }
242
+ </style>
243
+
244
+ <template>
245
+ <div v-bind="$attrs"
246
+ class="relative">
247
+ <input ref="element"
248
+ v-bind="$attrs"
249
+ class="!text-base !text-sm"
250
+ :class="{
251
+ '!pr-12': date !== null,
252
+ '!pr-8': date === null
253
+ }"
254
+ type="text"
255
+ :placeholder="type === 'date' ? datePlaceholder : dateTimePlaceholder"
256
+ :disabled="disabled">
257
+
258
+ <icon v-show="date === null || disabled"
259
+ class="absolute bg-white right-2 text-grey-4 top-3"
260
+ icon="ph:calendar"
261
+ weight="light"
262
+ :size="18" />
263
+
264
+ <div v-show="date !== null && !disabled"
265
+ class="absolute bg-white flex right-2 text-grey-4 top-3">
266
+ <icon class="cursor-pointer md:mr-1 mr-0.5"
267
+ icon="ph:arrow-counter-clockwise"
268
+ :size="16"
269
+ @click="resetValue" />
270
+
271
+ <icon class="cursor-pointer"
272
+ icon="ph:x"
273
+ :size="16"
274
+ @click="clearValue" />
275
+ </div>
276
+ </div>
277
+ </template>
@@ -1,4 +1,20 @@
1
- <script lang="ts" src="./card"></script>
1
+ <script setup lang="ts">
2
+ type CardSize = 'small' | 'medium' | 'large';
3
+
4
+ withDefaults(defineProps<{
5
+ size?: CardSize;
6
+ title?: string;
7
+ hover?: boolean;
8
+ shadowSize?: CardSize;
9
+ padImage?: boolean;
10
+ }>(), {
11
+ size: 'medium',
12
+ title: '',
13
+ hover: false,
14
+ shadowSize: 'medium',
15
+ padImage: false
16
+ });
17
+ </script>
2
18
 
3
19
  <template>
4
20
  <div class="overflow-hidden relative"