@central-design-system/components 3.0.0-alpha.2 → 3.0.0-alpha.4

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/cds.css +1 -1
  2. package/dist/cds.es.js +101 -101
  3. package/dist/cds.umd.js +1 -1
  4. package/dist/components/CdsCheckbox/CdsCheckbox.vue.d.ts +20 -4
  5. package/dist/components/CdsCombobox/CdsCombobox.vue.d.ts +125 -64
  6. package/dist/components/CdsDatePicker/CdsDatePicker.vue.d.ts +35 -5
  7. package/dist/components/CdsDrawer/CdsDrawer.vue.d.ts +20 -4
  8. package/dist/components/CdsDropdown/CdsDropdown.vue.d.ts +133 -116
  9. package/dist/components/CdsForm/CdsForm.vue.d.ts +13 -3
  10. package/dist/components/CdsInput/CdsInput.vue.d.ts +8 -2
  11. package/dist/components/CdsLink/CdsLink.vue.d.ts +9 -2
  12. package/dist/components/CdsList/CdsList.vue.d.ts +46 -13
  13. package/dist/components/CdsList/CdsListItem.vue.d.ts +8 -2
  14. package/dist/components/CdsMenu/CdsMenu.vue.d.ts +16 -6
  15. package/dist/components/CdsOverlay/CdsOverlay.vue.d.ts +20 -10
  16. package/dist/components/CdsRadioButton/CdsRadio.vue.d.ts +9 -3
  17. package/dist/components/CdsRadioButton/CdsRadioButton.vue.d.ts +9 -3
  18. package/dist/components/CdsSelect/CdsSelect.vue.d.ts +57 -36
  19. package/dist/components/CdsSidebar/CdsSidebar.vue.d.ts +14 -4
  20. package/dist/components/CdsTag/CdsTag.vue.d.ts +9 -2
  21. package/dist/components/CdsTextArea/CdsTextArea.vue.d.ts +63 -21
  22. package/dist/components/CdsTextInput/CdsTextInput.vue.d.ts +91 -26
  23. package/package.json +3 -2
  24. package/src/scss/themes/index.ts +1 -1
  25. package/src/utils/__test__/helper.test.ts +59 -0
  26. package/src/utils/__test__/mocks/helper.mock.ts +362 -0
  27. package/src/utils/__test__/mocks/propFactory.mock.ts +88 -0
  28. package/src/utils/__test__/propFactory.test.ts +9 -0
  29. package/src/utils/anchor.ts +76 -0
  30. package/src/utils/animation.ts +62 -0
  31. package/src/utils/box.ts +39 -0
  32. package/src/utils/cache.ts +12 -0
  33. package/src/utils/changeCase/__test__/lowerCase.test.ts +12 -0
  34. package/src/utils/changeCase/__test__/mocks/lowerCase.mock.ts +80 -0
  35. package/src/utils/changeCase/__test__/mocks/noCase.mock.ts +131 -0
  36. package/src/utils/changeCase/__test__/mocks/paramCase.mock.ts +39 -0
  37. package/src/utils/changeCase/__test__/mocks/pascalCase.mock.ts +46 -0
  38. package/src/utils/changeCase/__test__/noCase.test.ts +8 -0
  39. package/src/utils/changeCase/__test__/paramCase.test.ts +8 -0
  40. package/src/utils/changeCase/__test__/pascalCase.test.ts +8 -0
  41. package/src/utils/changeCase/lowerCase.ts +56 -0
  42. package/src/utils/changeCase/noCase.ts +45 -0
  43. package/src/utils/changeCase/paramCase.ts +11 -0
  44. package/src/utils/changeCase/pascalCase.ts +24 -0
  45. package/src/utils/date.ts +1100 -0
  46. package/src/utils/dom.ts +24 -0
  47. package/src/utils/getCurrentInstance.ts +43 -0
  48. package/src/utils/getScrollParent.ts +29 -0
  49. package/src/utils/globals.ts +8 -0
  50. package/src/utils/helpers.ts +311 -0
  51. package/src/utils/index.ts +19 -0
  52. package/src/utils/propsFactory.ts +92 -0
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Returns:
3
+ * - 'null' if the node is not attached to the DOM
4
+ * - the root node (HTMLDocument | ShadowRoot) otherwise
5
+ */
6
+ export function attachedRoot(node: Node): null | HTMLDocument | ShadowRoot {
7
+ /* istanbul ignore next */
8
+ if (typeof node.getRootNode !== 'function') {
9
+ // Shadow DOM not supported (IE11), lets find the root of this node
10
+ while (node.parentNode) node = node.parentNode;
11
+
12
+ // The root parent is the document if the node is attached to the DOM
13
+ if (node !== document) return null;
14
+
15
+ return document;
16
+ }
17
+
18
+ const root = node.getRootNode();
19
+
20
+ // The composed root node is the document if the node is attached to the DOM
21
+ if (root !== document && root.getRootNode({ composed: true }) !== document) return null;
22
+
23
+ return root as HTMLDocument | ShadowRoot;
24
+ }
@@ -0,0 +1,43 @@
1
+ import { getCurrentInstance as _getCurrentInstance } from 'vue';
2
+ import { paramCase } from './';
3
+
4
+ import type { ComponentInternalInstance } from 'vue';
5
+
6
+ /**
7
+ * This is a wrapper around Vue's getCurrentInstance
8
+ * WARNING! It's not a public API and can be removed in the future
9
+ * @param name
10
+ * @param message
11
+ */
12
+ export function getCurrentInstance(name: string, message?: string) {
13
+ const vm = _getCurrentInstance();
14
+
15
+ if (!vm) {
16
+ throw new Error(`[CDS] ${name} ${message || 'must be called from inside a setup function'}`);
17
+ }
18
+
19
+ return vm;
20
+ }
21
+
22
+ export function getCurrentInstanceName(name = 'composable') {
23
+ const vm = getCurrentInstance(name);
24
+ return paramCase(vm.type?.name || '');
25
+ }
26
+
27
+ let _uid = 0;
28
+ let _map = new WeakMap<ComponentInternalInstance, number>();
29
+ export function getUid() {
30
+ const vm = getCurrentInstance('getUid');
31
+
32
+ if (_map.has(vm)) {
33
+ return _map.get(vm)!;
34
+ } else {
35
+ const uid = _uid++;
36
+ _map.set(vm, uid);
37
+ return uid;
38
+ }
39
+ }
40
+ getUid.reset = () => {
41
+ _uid = 0;
42
+ _map = new WeakMap();
43
+ };
@@ -0,0 +1,29 @@
1
+ export function getScrollParent(el?: HTMLElement) {
2
+ while (el) {
3
+ if (hasScrollbar(el)) return el;
4
+ el = el.parentElement!;
5
+ }
6
+
7
+ return document.scrollingElement as HTMLElement;
8
+ }
9
+
10
+ export function getScrollParents(el?: Element | null, stopAt?: Element | null) {
11
+ const elements: HTMLElement[] = [];
12
+
13
+ if (stopAt && el && !stopAt.contains(el)) return elements;
14
+
15
+ while (el) {
16
+ if (hasScrollbar(el)) elements.push(el as HTMLElement);
17
+ if (el === stopAt) break;
18
+ el = el.parentElement!;
19
+ }
20
+
21
+ return elements;
22
+ }
23
+
24
+ export function hasScrollbar(el?: Element | null) {
25
+ if (!el || el.nodeType !== Node.ELEMENT_NODE) return false;
26
+
27
+ const style = window.getComputedStyle(el);
28
+ return style.overflowY === 'scroll' || (style.overflowY === 'auto' && el.scrollHeight > el.clientHeight);
29
+ }
@@ -0,0 +1,8 @@
1
+ export const IN_BROWSER = typeof window !== 'undefined';
2
+ export const SUPPORTS_INTERSECTION = IN_BROWSER && 'IntersectionObserver' in window;
3
+ export const SUPPORTS_TOUCH = IN_BROWSER && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0);
4
+ export const SUPPORTS_FOCUS_VISIBLE =
5
+ IN_BROWSER &&
6
+ typeof CSS !== 'undefined' &&
7
+ typeof CSS.supports !== 'undefined' &&
8
+ CSS.supports('selector(:focus-visible)');
@@ -0,0 +1,311 @@
1
+ import { computed, reactive, watchEffect, toRefs } from 'vue';
2
+
3
+ import type { ComponentPublicInstance, ComputedGetter, PropType, Ref, ToRefs } from 'vue';
4
+ import type { TItemKey } from '@components/composable';
5
+
6
+ export const keyNames = Object.freeze({
7
+ enter: 'Enter',
8
+ tab: 'Tab',
9
+ delete: 'Delete',
10
+ esc: 'Escape',
11
+ space: ' ',
12
+ up: 'ArrowUp',
13
+ down: 'ArrowDown',
14
+ left: 'ArrowLeft',
15
+ right: 'ArrowRight',
16
+ end: 'End',
17
+ home: 'Home',
18
+ del: 'Del',
19
+ backspace: 'Backspace',
20
+ insert: 'Insert',
21
+ pageup: 'PageUp',
22
+ pagedown: 'PageDown',
23
+ shift: 'Shift'
24
+ });
25
+
26
+ export const classesStates = Object.freeze({
27
+ hover: 'cds-state-hover',
28
+ focus: 'cds-state-focus',
29
+ active: 'cds-state-active',
30
+ visited: 'cds-state-visited',
31
+ disabled: 'cds-state-disabled',
32
+ checked: 'cds-state-checked',
33
+ indeterminate: 'cds-state-indeterminate',
34
+ invalid: 'cds-state-invalid',
35
+ empty: 'cds-state-empty',
36
+ loading: 'cds-state-loading'
37
+ });
38
+
39
+ /**
40
+ * Check if value is a object.
41
+ * @param obj
42
+ */
43
+ export function isObject(obj: any): obj is object {
44
+ return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
45
+ }
46
+
47
+ /**
48
+ * Check if value is a Date.
49
+ * @param value
50
+ */
51
+ export function isDate(value: any) {
52
+ return Object.prototype.toString.call(value) === '[object Date]';
53
+ }
54
+
55
+ /**
56
+ * Deep merge two objects.
57
+ * @param source
58
+ * @param target
59
+ * @param arrayFn
60
+ */
61
+ export function mergeDeep(
62
+ source: Record<string, any> = {},
63
+ target: Record<string, any> = {},
64
+ arrayFn?: (a: unknown[], b: unknown[]) => unknown[]
65
+ ) {
66
+ const out: Record<string, any> = {};
67
+
68
+ for (const key in source) {
69
+ out[key] = source[key];
70
+ }
71
+
72
+ for (const key in target) {
73
+ const sourceProperty = source[key];
74
+ const targetProperty = target[key];
75
+
76
+ // Only continue deep merging if
77
+ // both properties are objects
78
+ if (isObject(sourceProperty) && isObject(targetProperty)) {
79
+ out[key] = mergeDeep(sourceProperty, targetProperty, arrayFn);
80
+
81
+ continue;
82
+ }
83
+
84
+ if (Array.isArray(sourceProperty) && Array.isArray(targetProperty) && arrayFn) {
85
+ out[key] = arrayFn(sourceProperty, targetProperty);
86
+
87
+ continue;
88
+ }
89
+
90
+ out[key] = targetProperty;
91
+ }
92
+
93
+ return out;
94
+ }
95
+
96
+ /**
97
+ * Deep compare two values to determine if they are equivalent.
98
+ * @param a
99
+ * @param b
100
+ */
101
+ export function deepEqual(a: any, b: any): boolean {
102
+ if (a === b) return true;
103
+
104
+ if (a instanceof Date && b instanceof Date && a.getTime() !== b.getTime()) {
105
+ // If the values are Date, compare them as timestamps
106
+ return false;
107
+ }
108
+
109
+ if (a !== Object(a) || b !== Object(b)) {
110
+ // If the values aren't objects, they were already checked for equality
111
+ return false;
112
+ }
113
+
114
+ const props = Object.keys(a);
115
+
116
+ if (props.length !== Object.keys(b).length) {
117
+ // Different number of props, don't bother to check
118
+ return false;
119
+ }
120
+
121
+ return props.every((p) => deepEqual(a[p], b[p]));
122
+ }
123
+
124
+ /**
125
+ * Wrap value in array if it's not already an array
126
+ * @param value
127
+ */
128
+ export function wrapInArray<T>(value: T | T[] | null | undefined): T[] {
129
+ return value == null ? [] : Array.isArray(value) ? value : [value];
130
+ }
131
+
132
+ /**
133
+ * Get the nested value of an object at a given path.
134
+ * @param obj
135
+ * @param path
136
+ * @param fallback
137
+ */
138
+ export function getNestedValue(obj: any, path: (string | number)[], fallback?: any): any {
139
+ const last = path.length - 1;
140
+
141
+ if (last < 0) return obj === undefined ? fallback : obj;
142
+
143
+ for (let i = 0; i < last; i++) {
144
+ if (obj == null) {
145
+ return fallback;
146
+ }
147
+ obj = obj[path[i]];
148
+ }
149
+
150
+ if (obj == null) return fallback;
151
+
152
+ return obj[path[last]] === undefined ? fallback : obj[path[last]];
153
+ }
154
+
155
+ /**
156
+ * Get the value of an object at a given path.
157
+ * @param obj
158
+ * @param path
159
+ * @param fallback
160
+ */
161
+ export function getObjectValueByPath(obj: any, path: string, fallback?: any) {
162
+ if (obj == null || !path || typeof path !== 'string') return fallback;
163
+ if (obj[path] !== undefined) return obj[path];
164
+ path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
165
+ path = path.replace(/^\./, ''); // strip a leading dot
166
+ return getNestedValue(obj, path.split('.'), fallback);
167
+ }
168
+
169
+ export function getPropertyFromItem(item: any, property: SelectItemKey, fallback?: any): any {
170
+ if (property == null) return item === undefined ? fallback : item;
171
+ if (item !== Object(item)) return fallback;
172
+ if (typeof property === 'string') return getObjectValueByPath(item, property, fallback);
173
+ if (Array.isArray(property)) return getNestedValue(item, property, fallback);
174
+ if (typeof property !== 'function') return fallback;
175
+
176
+ const value = property(item, fallback);
177
+ return typeof value === 'undefined' ? fallback : value;
178
+ }
179
+
180
+ /**
181
+ * @description This function is used to return a copy of the object that is composed of the picked object properties.
182
+ * @param obj
183
+ * @param paths
184
+ */
185
+ export function pick<T extends object, U extends Extract<keyof T, string>>(
186
+ obj: T,
187
+ paths: U[]
188
+ ): [yes: MaybePick<T, U>, no: Omit<T, U>];
189
+ export function pick<T extends object, U extends Extract<keyof T, string>>(
190
+ obj: T,
191
+ paths: (U | RegExp)[]
192
+ ): [yes: Partial<T>, no: Partial<T>] {
193
+ const found = Object.create(null);
194
+ const rest = Object.create(null);
195
+
196
+ for (const key in obj) {
197
+ if (paths.some((path) => (path instanceof RegExp ? path.test(key) : path === key))) {
198
+ found[key] = obj[key];
199
+ } else {
200
+ rest[key] = obj[key];
201
+ }
202
+ }
203
+
204
+ return [found, rest];
205
+ }
206
+
207
+ /**
208
+ * Convert a computed ref to a record of refs.
209
+ * The getter function must always return an object with the same keys.
210
+ */
211
+ export function destructComputed<T extends object>(getter: ComputedGetter<T & NotAUnion<T>>): ToRefs<T>;
212
+ export function destructComputed<T extends object>(getter: ComputedGetter<T>) {
213
+ const refs = reactive({}) as T;
214
+ const base = computed(getter);
215
+ watchEffect(
216
+ () => {
217
+ for (const key in base.value) {
218
+ refs[key] = base.value[key];
219
+ }
220
+ },
221
+ { flush: 'sync' }
222
+ );
223
+ return toRefs(refs);
224
+ }
225
+
226
+ /**
227
+ * Array.includes but value can be any type
228
+ * @param arr
229
+ * @param val
230
+ */
231
+ export function includes(arr: readonly any[], val: any) {
232
+ return arr.includes(val);
233
+ }
234
+
235
+ export function convertToUnit(str: number, unit?: string): string;
236
+ export function convertToUnit(str: string | number | null | undefined, unit?: string): string | undefined;
237
+ export function convertToUnit(str: string | number | null | undefined, unit = 'px'): string | undefined {
238
+ if (str == null || str === '') {
239
+ return undefined;
240
+ } else if (isNaN(+str!)) {
241
+ return String(str);
242
+ } else if (!isFinite(+str!)) {
243
+ return undefined;
244
+ } else {
245
+ return `${Number(str)}${unit}`;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Clamp a value between a min and max
251
+ * @param value
252
+ * @param min
253
+ * @param max
254
+ */
255
+ export function clamp(value: number, min = 0, max = 1) {
256
+ return Math.max(min, Math.min(max, value));
257
+ }
258
+
259
+ /**
260
+ * Is value an like event
261
+ * @param key
262
+ */
263
+ export const isOn = (key: string) => key.startsWith('on');
264
+
265
+ export const EventProp = [Function, Array] as PropType<EventProp>;
266
+
267
+ export function isComponentInstance(obj: any): obj is ComponentPublicInstance {
268
+ return obj?.$el;
269
+ }
270
+
271
+ export function isFixedPosition(el?: HTMLElement) {
272
+ while (el) {
273
+ if (window.getComputedStyle(el).position === 'fixed') {
274
+ return true;
275
+ }
276
+ el = el.offsetParent as HTMLElement;
277
+ }
278
+ return false;
279
+ }
280
+
281
+ export function padStart(string: number | string, targetLength: number, padString: string) {
282
+ targetLength = targetLength >> 0;
283
+ string = String(string);
284
+ padString = String(padString);
285
+ if (string.length > targetLength) {
286
+ return String(string);
287
+ }
288
+
289
+ targetLength = targetLength - string.length;
290
+ if (targetLength > padString.length) {
291
+ padString += padString.repeat(targetLength / padString.length);
292
+ }
293
+ return padString.slice(0, targetLength) + String(string);
294
+ }
295
+
296
+ export function pad(n: string | number, length = 2) {
297
+ return padStart(n, length, '0');
298
+ }
299
+
300
+ /**
301
+ * Types
302
+ */
303
+ export type MaybeRef<T> = T | Ref<T>;
304
+ export type SelectItemKey = TItemKey;
305
+ export type MaybePick<T extends object, U extends Extract<keyof T, string>> = Record<string, unknown> extends T
306
+ ? Partial<Pick<T, U>>
307
+ : Pick<T, U>;
308
+ export type EventProp<T = (...args: any[]) => any> = T;
309
+ // Only allow a single return type
310
+ type NotAUnion<T> = [T] extends [infer U] ? _NotAUnion<U, U> : never;
311
+ type _NotAUnion<T, U> = U extends any ? ([T] extends [U] ? unknown : never) : never;
@@ -0,0 +1,19 @@
1
+ export * from './propsFactory';
2
+ export * from './helpers';
3
+ export * from './getCurrentInstance';
4
+ export * from './getScrollParent';
5
+ export * from './globals';
6
+ export * from './dom';
7
+ export * from './anchor';
8
+ export * from './box';
9
+ export * from './animation';
10
+ export * from './cache';
11
+
12
+ // change case
13
+ export * from './changeCase/noCase';
14
+ export * from './changeCase/lowerCase';
15
+ export * from './changeCase/pascalCase';
16
+ export * from './changeCase/paramCase';
17
+
18
+ // date
19
+ export * from './date';
@@ -0,0 +1,92 @@
1
+ import type { PropType } from 'vue';
2
+ import type { Prop, ComponentObjectPropsOptions } from '../shims-vue';
3
+
4
+ /**
5
+ * Creates a factory function for props definitions.
6
+ * This is used to define props in a composable then override
7
+ * default values in an implementing component.
8
+ *
9
+ * @example Simplified signature
10
+ * (props: Props) => (defaults?: Record<keyof props, any>) => Props
11
+ *
12
+ * @example Usage
13
+ * const makeProps = propsFactory({
14
+ * foo: String,
15
+ * })
16
+ *
17
+ * defineComponent({
18
+ * props: {
19
+ * ...makeProps({
20
+ * foo: 'a',
21
+ * }),
22
+ * },
23
+ * setup (props) {
24
+ * // would be "string | undefined", now "string" because a default has been provided
25
+ * props.foo
26
+ * },
27
+ * }
28
+ */
29
+ export function propsFactory<PropsOptions extends ComponentObjectPropsOptions = ComponentObjectPropsOptions>(
30
+ props: PropsOptions,
31
+ source: string
32
+ ) {
33
+ return <Defaults extends PartialKeys<PropsOptions> = {}>(
34
+ defaults?: Defaults
35
+ ): AppendDefault<PropsOptions, Defaults> => {
36
+ return Object.keys(props).reduce<any>((obj, prop) => {
37
+ const isObjectDefinition = typeof props[prop] === 'object' && props[prop] != null && !Array.isArray(props[prop]);
38
+ const definition = isObjectDefinition ? props[prop] : { type: props[prop] };
39
+
40
+ if (defaults && prop in defaults) {
41
+ obj[prop] = {
42
+ ...definition,
43
+ default: defaults[prop]
44
+ };
45
+ } else {
46
+ obj[prop] = definition;
47
+ }
48
+
49
+ if (source && !obj[prop].source) {
50
+ obj[prop].source = source;
51
+ }
52
+
53
+ return obj;
54
+ }, {});
55
+ };
56
+ }
57
+
58
+ export type AppendDefault<T extends ComponentObjectPropsOptions, D extends PartialKeys<T>> = {
59
+ [P in keyof T]-?: unknown extends D[P]
60
+ ? T[P]
61
+ : T[P] extends Record<string, unknown>
62
+ ? Omit<T[P], 'type' | 'default'> & {
63
+ type: PropType<MergeDefaults<T[P], D[P]>>;
64
+ default: MergeDefaults<T[P], D[P]>;
65
+ }
66
+ : {
67
+ type: PropType<MergeDefaults<T[P], D[P]>>;
68
+ default: MergeDefaults<T[P], D[P]>;
69
+ };
70
+ };
71
+
72
+ type MergeDefaults<T, D> = unknown extends D ? InferPropType<T> : NonNullable<InferPropType<T>> | D;
73
+
74
+ /**
75
+ * Like `Partial<T>` but doesn't care what the value is
76
+ */
77
+ type PartialKeys<T> = { [P in keyof T]?: unknown };
78
+
79
+ // Copied from Vue
80
+ type InferPropType<T> = T extends null
81
+ ? any // null & true would fail to infer
82
+ : T extends { type: null | true }
83
+ ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
84
+ : T extends ObjectConstructor | { type: ObjectConstructor }
85
+ ? Record<string, any>
86
+ : T extends BooleanConstructor | { type: BooleanConstructor }
87
+ ? boolean
88
+ : T extends Prop<infer V, infer D>
89
+ ? unknown extends V
90
+ ? D
91
+ : V
92
+ : T;