winduum 0.7.2 → 0.8.0-next.10

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 (46) hide show
  1. package/dist/main.css +1 -1
  2. package/dist/tailwind.css +1 -1
  3. package/package.json +58 -19
  4. package/plugin/{tailwind.cjs → index.cjs} +2 -2
  5. package/plugin/{tailwind.d.ts → index.d.ts} +6 -0
  6. package/plugin/{tailwind.js → index.js} +2 -2
  7. package/src/common.d.ts +2 -0
  8. package/src/components/carousel/content.css +30 -0
  9. package/src/components/carousel/default.css +3 -0
  10. package/src/components/carousel/index.css +2 -0
  11. package/src/components/carousel/index.d.ts +40 -0
  12. package/src/components/carousel/index.js +221 -0
  13. package/src/components/compare/index.d.ts +8 -0
  14. package/src/components/compare/index.js +9 -15
  15. package/src/components/details/index.d.ts +9 -0
  16. package/src/components/details/index.js +4 -10
  17. package/src/components/dialog/index.d.ts +30 -0
  18. package/src/components/dialog/index.js +32 -40
  19. package/src/components/drawer/content.css +10 -0
  20. package/src/components/drawer/default.css +37 -0
  21. package/src/components/drawer/index.css +2 -0
  22. package/src/components/drawer/index.d.ts +13 -0
  23. package/src/components/drawer/index.js +52 -0
  24. package/src/components/form/index.d.ts +26 -0
  25. package/src/components/form/index.js +105 -0
  26. package/src/components/index.css +2 -0
  27. package/src/components/toaster/index.d.ts +30 -0
  28. package/src/components/toaster/index.js +6 -14
  29. package/src/ui/control/invalid.css +1 -1
  30. package/src/ui/control/select.css +2 -2
  31. package/src/ui/progress/default.css +6 -3
  32. package/src/ui/range/index.d.ts +19 -0
  33. package/src/ui/range/index.js +24 -38
  34. package/src/utilities/ripple/index.d.ts +1 -0
  35. package/src/utilities/ripple/index.js +11 -15
  36. package/src/utilities/swap/index.d.ts +1 -0
  37. package/src/utilities/swap/index.js +0 -4
  38. package/tailwind.config.js +1 -1
  39. package/types/index.d.ts +235 -0
  40. package/types/index.d.ts.map +92 -0
  41. package/src/components/compare/types/index.d.ts +0 -17
  42. package/src/components/details/types/index.d.ts +0 -4
  43. package/src/components/dialog/types/index.d.ts +0 -22
  44. package/src/components/toaster/types/index.d.ts +0 -25
  45. package/src/main.js +0 -1
  46. package/src/ui/range/types/index.d.ts +0 -16
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @param {HTMLElement | Element} element
3
+ * @param {number} distance
4
+ * @param {'top' | 'left'} direction
5
+ * @returns void
6
+ */
7
+ export const showDrawer = (element, distance = 0, direction = 'left') => {
8
+ element.scroll({ [direction]: distance })
9
+ }
10
+
11
+ /**
12
+ * @param {HTMLElement | Element} element
13
+ * @param {number} distance
14
+ * @param {'top' | 'left'} direction
15
+ * @returns void
16
+ */
17
+ export const closeDrawer = (element, distance = element.scrollWidth, direction = 'left') => {
18
+ element.scroll({ [direction]: distance })
19
+ }
20
+
21
+ /**
22
+ * @param {HTMLElement | Element} element
23
+ * @param {import("./").ScrollDrawerOptions} options
24
+ * @returns void
25
+ */
26
+ export const scrollDrawer = (element, options = {}) => {
27
+ options = {
28
+ snapClass: 'snap-x snap-mandatory',
29
+ opacityProperty: '--tw-bg-opacity',
30
+ opacityRatio: 1,
31
+ scrollOpen: 0,
32
+ scrollClose: element.scrollWidth - element.clientWidth,
33
+ scrollSize: element.scrollWidth - element.clientWidth,
34
+ scrollDirection: element.scrollLeft,
35
+ ...options
36
+ }
37
+
38
+ element.style.setProperty(
39
+ options.opacityProperty,
40
+ `${Math.min(Math.abs((options.scrollDirection / options.scrollSize) - options.opacityRatio), 1)}`
41
+ )
42
+
43
+ if (options.scrollDirection === options.scrollOpen) {
44
+ element.classList.add(...options.snapClass.split(/\s/))
45
+ element.inert = false
46
+ }
47
+
48
+ if ((options.scrollDirection === options.scrollClose)) {
49
+ element.classList.remove(...options.snapClass.split(/\s/))
50
+ element.inert = true
51
+ }
52
+ }
@@ -0,0 +1,26 @@
1
+ export interface ValidateFormOptions {
2
+ validateSelectors?: string
3
+ validateOptions?: ValidateFieldOptions
4
+ submitterLoadingClass?: string
5
+ }
6
+
7
+ export interface ValidateFieldOptions {
8
+ validate?: boolean
9
+ selector?: string
10
+ ignoreMatch?: RegExp
11
+ validitySelector?: string
12
+ infoParentSelector?: string
13
+ infoSelector?: string
14
+ infoContent?: string
15
+ endParentSelector?: string
16
+ endSelector?: string
17
+ endContent?: string
18
+ validClass?: string
19
+ validIcon?: string | null
20
+ invalidClass?: string
21
+ invalidIcon?: string
22
+ activeClass?: string
23
+ }
24
+
25
+ export function validateForm(event: Event | SubmitEvent, options?: ValidateFormOptions): void
26
+ export function validateField(element: HTMLElement | SubmitEvent, options?: ValidateFieldOptions): void
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @param {Event | SubmitEvent} event
3
+ * @param {import("./").ValidateFormOptions} options
4
+ * @returns void
5
+ */
6
+ export const validateForm = (event, options = {}) => {
7
+ options = {
8
+ validateSelectors: '.ui-control, .ui-check, .ui-switch, .ui-rating, .ui-color',
9
+ validateOptions: {},
10
+ submitterLoadingClass: 'loading',
11
+ ...options
12
+ }
13
+
14
+ if (!event.target.checkValidity()) {
15
+ event.preventDefault()
16
+ event.stopImmediatePropagation()
17
+
18
+ event.target.querySelector(':invalid').scrollIntoView({ behavior: 'smooth', block: 'center' })
19
+ event.target.querySelector(':invalid').focus()
20
+ } else {
21
+ event?.submitter?.classList.add(options.submitterLoadingClass)
22
+ }
23
+
24
+ event.target.querySelectorAll(options.validateSelectors).forEach(element => {
25
+ validateField(element, options.validateOptions)
26
+ })
27
+ }
28
+
29
+ /**
30
+ * @param {HTMLElement} element
31
+ * @param {import("./").ValidateFieldOptions} options
32
+ * @returns void
33
+ */
34
+ export const validateField = (element, options = {}) => {
35
+ options = {
36
+ validate: true,
37
+ selector: 'input:not([type="hidden"]), textarea, select',
38
+ ignoreMatch: /(data-novalidate|readonly)/,
39
+ validitySelector: '.validity',
40
+ infoParentSelector: '.c-field',
41
+ infoSelector: '.ui-info',
42
+ infoContent: '<div class="ui-info text-error validity"></div>',
43
+ endParentSelector: '.ui-control',
44
+ endSelector: '.ms-auto',
45
+ endContent: '<div class="ms-auto"></div>',
46
+ validClass: 'valid',
47
+ validIcon: null,
48
+ invalidClass: 'invalid',
49
+ invalidIcon: '<svg class="text-error validity" aria-hidden="true"><use href="#icon-exclamation-circle"></use></svg>',
50
+ activeClass: 'active',
51
+ ...options
52
+ }
53
+
54
+ if (!element.querySelector(options.selector)) return
55
+
56
+ const validationElement = element.querySelector(options.selector)
57
+ const validationMessage = validationElement.dataset.validationMessage ?? validationElement.validationMessage
58
+ const infoParentElement = validationElement?.closest(options.infoParentSelector)
59
+ const endParentElement = validationElement.closest(options.endParentSelector)
60
+ const infoSelector = options.infoSelector + options.validitySelector
61
+ const endSelector = `${options.endSelector} ${options.validitySelector}`
62
+
63
+ const insertIcon = icon => {
64
+ if (!endParentElement || !icon) return
65
+
66
+ if (!element?.querySelector(options.endSelector)) {
67
+ element?.insertAdjacentHTML('beforeend', options.endContent)
68
+ }
69
+
70
+ element.querySelector(options.endSelector).insertAdjacentHTML('afterbegin', icon)
71
+ }
72
+
73
+ if (validationElement.value !== '') {
74
+ element.classList.add(options.activeClass)
75
+ } else {
76
+ element.classList.remove(options.activeClass)
77
+ }
78
+
79
+ if (!validationElement.outerHTML.match(options.ignoreMatch) && options.validate) {
80
+ element?.classList?.remove(options.validClass, options.invalidClass)
81
+
82
+ infoParentElement?.querySelector(infoSelector)?.remove()
83
+ endParentElement?.querySelector(endSelector)?.remove()
84
+
85
+ if (validationElement.checkValidity()) {
86
+ element.classList.add(options.validClass)
87
+
88
+ insertIcon(options.validIcon)
89
+ } else {
90
+ element.classList.add(options.invalidClass)
91
+
92
+ insertIcon(options.invalidIcon)
93
+
94
+ if (infoParentElement) {
95
+ infoParentElement.insertAdjacentHTML('beforeend', options.infoContent)
96
+ infoParentElement.querySelector(infoSelector).textContent = validationMessage
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ export default {
103
+ validateForm,
104
+ validateField
105
+ }
@@ -1,6 +1,8 @@
1
1
  @import "card/index.css";
2
2
  @import "dialog/index.css";
3
+ @import "drawer/index.css";
3
4
  @import "field/index.css";
5
+ @import "carousel/index.css";
4
6
  @import "compare/index.css";
5
7
  @import "popover/index.css";
6
8
  @import "tooltip/index.css";
@@ -0,0 +1,30 @@
1
+ export interface ShowToastOptions {
2
+ visibleClass?: string
3
+ autoHide?: number | null
4
+ heightProperty?: string
5
+ close?: CloseToastOptions
6
+ }
7
+
8
+ export interface CloseToastOptions {
9
+ hiddenClass?: string
10
+ heightProperty?: string
11
+ }
12
+
13
+ export interface InsertToasterOptions {
14
+ classes?: string
15
+ }
16
+
17
+ export interface InsertToastOptions {
18
+ classes?: string
19
+ title?: string
20
+ text?: string
21
+ start?: string
22
+ end?: string
23
+ show?: ShowToastOptions
24
+ }
25
+
26
+ export function closeToast(element: HTMLElement, options?: CloseToastOptions): Promise<void>
27
+ export function showToast(element: HTMLElement, options?: ShowToastOptions): Promise<void>
28
+ export function insertToaster(element: HTMLElement, options?: InsertToasterOptions): Promise<void>
29
+ export function insertToast(element: HTMLElement, options?: InsertToastOptions): Promise<void>
30
+ export function closeToaster(element: HTMLElement, options?: CloseToastOptions): Promise<void>
@@ -2,7 +2,7 @@ import { animationsFinished, nextRepaint } from '../../common.js'
2
2
 
3
3
  /**
4
4
  * @param {HTMLElement} element
5
- * @param {import('./types/index').CloseToastOptions} options
5
+ * @param {import('./index').CloseToastOptions} options
6
6
  * @returns Promise<void>
7
7
  */
8
8
  export const closeToast = async (element, options = {}) => {
@@ -14,7 +14,7 @@ export const closeToast = async (element, options = {}) => {
14
14
 
15
15
  const toaster = element.parentElement
16
16
 
17
- element.style.setProperty(options.toastHeightProperty, `${element.offsetHeight}px`)
17
+ element.style.setProperty(options.heightProperty, `${element.offsetHeight}px`)
18
18
 
19
19
  await nextRepaint()
20
20
 
@@ -29,7 +29,7 @@ export const closeToast = async (element, options = {}) => {
29
29
 
30
30
  /**
31
31
  * @param {HTMLElement} element
32
- * @param {import('./types/index').ShowToastOptions} options
32
+ * @param {import('./').ShowToastOptions} options
33
33
  * @returns Promise<void>
34
34
  */
35
35
  export const showToast = async (element, options = {}) => {
@@ -56,7 +56,7 @@ export const showToast = async (element, options = {}) => {
56
56
 
57
57
  /**
58
58
  * @param {HTMLElement} element
59
- * @param {import('./types/index').InsertToasterOptions} options
59
+ * @param {import('./').InsertToasterOptions} options
60
60
  * @returns Promise<void>
61
61
  */
62
62
  export const insertToaster = async (element, options = {}) => {
@@ -72,7 +72,7 @@ export const insertToaster = async (element, options = {}) => {
72
72
 
73
73
  /**
74
74
  * @param {HTMLElement} element
75
- * @param {import('./types/index').InsertToastOptions} options
75
+ * @param {import('./').InsertToastOptions} options
76
76
  * @returns Promise<void>
77
77
  */
78
78
  export const insertToast = async (element, options = {}) => {
@@ -104,7 +104,7 @@ export const insertToast = async (element, options = {}) => {
104
104
 
105
105
  /**
106
106
  * @param {HTMLElement} element
107
- * @param {import('./types/index').CloseToastOptions} options
107
+ * @param {import('./').CloseToastOptions} options
108
108
  * @returns Promise<void>
109
109
  */
110
110
  export const closeToaster = (element, options = {}) => {
@@ -112,11 +112,3 @@ export const closeToaster = (element, options = {}) => {
112
112
  closeToast(toast, options)
113
113
  )
114
114
  }
115
-
116
- export default {
117
- closeToast,
118
- showToast,
119
- insertToast,
120
- insertToaster,
121
- closeToaster
122
- }
@@ -1,5 +1,5 @@
1
1
  .ui-control {
2
- &.invalid, .validated &:has(:invalid) {
2
+ &.invalid, &:has(:user-invalid), .validated &:has(:invalid) {
3
3
  --ui-control-border-color: var(--color-error);
4
4
  --ui-control-color: var(--color-error);
5
5
  --ui-control-bg: var(--color-error);
@@ -1,7 +1,7 @@
1
1
  .ui-control:has(select:not([multiple])) {
2
2
  --ui-control-pe: calc(var(--ui-control-select-icon-size) + var(--ui-control-select-icon-me));
3
3
 
4
- &:has(.end) {
4
+ &:has(.ms-auto) {
5
5
  --ui-control-icon-count-e: 2;
6
6
  }
7
7
 
@@ -17,7 +17,7 @@
17
17
  content: "";
18
18
  }
19
19
 
20
- :where(.end) {
20
+ :where(.ms-auto) {
21
21
  margin-inline-end: var(--ui-control-select-icon-size);
22
22
  }
23
23
  }
@@ -1,6 +1,7 @@
1
1
  .ui-progress {
2
2
  --tw-bg-opacity: 0.1;
3
3
  --tw-bg-mix: var(--color-body);
4
+ --duration: 0.3s;
4
5
 
5
6
  inline-size: 100%;
6
7
  block-size: var(--ui-progress-height);
@@ -12,20 +13,22 @@
12
13
  &::-moz-progress-bar {
13
14
  background-color: var(--color-accent);
14
15
  border-radius: inherit;
16
+ transition: all var(--duration) var(--ease-in-out);
15
17
  }
16
18
 
17
- &::-webkit-progress-inner-element {
19
+ &::-webkit-progress-bar {
20
+ background-color: transparent;
18
21
  border-radius: inherit;
19
22
  }
20
23
 
21
- &::-webkit-progress-bar {
22
- background-color: transparent;
24
+ &::-webkit-progress-inner-element {
23
25
  border-radius: inherit;
24
26
  }
25
27
 
26
28
  &::-webkit-progress-value {
27
29
  background-color: var(--color-accent);
28
30
  border-radius: inherit;
31
+ transition: all var(--duration) var(--ease-in-out);
29
32
  }
30
33
 
31
34
  &:indeterminate {
@@ -0,0 +1,19 @@
1
+ export interface SetTrackPropertyOptions {
2
+ element: HTMLElement | Element
3
+ value: string
4
+ max?: number
5
+ }
6
+
7
+ export interface SetValueOptions {
8
+ selector?: string
9
+ track?: 'start' | 'end'
10
+ }
11
+
12
+ export interface SetOutputOptions {
13
+ lang?: string
14
+ formatOptions?: Intl.NumberFormatOptions
15
+ }
16
+
17
+ export function setTrackProperty(options: SetTrackPropertyOptions, track: 'start' | 'end'): void
18
+ export function setValue(element: HTMLInputElement, options?: SetValueOptions): void
19
+ export function setOutputValue(element: HTMLInputElement, outputElement: HTMLOutputElement | Element, options?: SetOutputOptions): void
@@ -1,66 +1,59 @@
1
1
  /**
2
- * @type {import("./types/index").DefaultOptions}
3
- */
4
- export const defaultOptions = {
5
- selector: '.ui-range',
6
- track: 'start'
7
- }
8
-
9
- /**
10
- * @param {import("./types/index").TrackOptions} options
2
+ * @param {import("./").SetTrackPropertyOptions} options
11
3
  * @param {'start' | 'end'} track
12
4
  * @returns void
13
5
  */
14
- export const setTrackProperty = ({ element, value, max }, track = defaultOptions.track) => {
6
+ export const setTrackProperty = ({ element, value, max }, track = 'start') => {
15
7
  element.style.setProperty('--ui-range-track-' + track, `${(value / max * 100).toString()}%`)
16
8
  }
17
9
 
18
10
  /**
19
- * @param {HTMLInputElement} target
20
- * @param {import("./types/index").DefaultOptions} options
11
+ * @param {HTMLInputElement} element
12
+ * @param {import("./").SetValueOptions} options
21
13
  * @returns void
22
14
  */
23
- export const setValue = (target, options = {}) => {
15
+ export const setValue = (element, options = {}) => {
24
16
  const { selector, track } = {
25
- ...defaultOptions,
17
+ selector: '.ui-range',
18
+ track: 'start',
26
19
  ...options
27
20
  }
28
21
 
29
- const element = target.closest(selector)
22
+ const parentElement = element.closest(selector)
30
23
 
31
- if (!element._trackValues) {
32
- element._trackValues = {
24
+ if (!parentElement._trackValues) {
25
+ parentElement._trackValues = {
33
26
  start: 0,
34
27
  end: Infinity
35
28
  }
36
29
  }
37
30
 
38
- if (Object.keys(element._trackValues).length > 1) {
31
+ if (Object.keys(parentElement._trackValues).length > 1) {
39
32
  if (
40
- (track === 'start' && target.value < element._trackValues.end) ||
41
- (track === 'end' && element._trackValues.start < target.value)
33
+ (track === 'start' && element.value < parentElement._trackValues.end) ||
34
+ (track === 'end' && parentElement._trackValues.start < element.value)
42
35
  ) {
43
- element._trackValues[track] = Number(target.value)
36
+ parentElement._trackValues[track] = Number(element.value)
44
37
  }
45
38
 
46
- target.value = element._trackValues[track]
39
+ element.value = parentElement._trackValues[track]
47
40
  }
48
41
 
49
42
  setTrackProperty({
50
- element,
51
- value: target.value,
52
- max: target.max || 100
43
+ element: parentElement,
44
+ value: element.value,
45
+ max: Number(element.max) || 100
53
46
  }, track)
54
47
  }
55
48
 
56
49
  /**
57
- * @param {HTMLInputElement} target
58
- * @param {import("./types/index").OutputOptions} options
50
+ * @param {HTMLInputElement} element
51
+ * @param {HTMLOutputElement | Element} outputElement
52
+ * @param {import("./").SetOutputOptions} options
59
53
  * @returns void
60
54
  */
61
- export const setOutputValue = (target, options = {}) => {
62
- const { element, lang, formatOptions } = {
63
- element: null,
55
+ export const setOutputValue = (element, outputElement, options = {}) => {
56
+ options = {
64
57
  lang: document.documentElement.lang,
65
58
  formatOptions: {
66
59
  style: 'decimal',
@@ -70,12 +63,5 @@ export const setOutputValue = (target, options = {}) => {
70
63
  ...options
71
64
  }
72
65
 
73
- element.innerHTML = Number(target.value).toLocaleString(lang, formatOptions)
74
- }
75
-
76
- export default {
77
- defaultOptions,
78
- setTrackProperty,
79
- setValue,
80
- setOutputValue
66
+ outputElement.innerHTML = Number(element.value).toLocaleString(options.lang, options.formatOptions)
81
67
  }
@@ -0,0 +1 @@
1
+ export function showRipple(event: MouseEvent, element: HTMLElement): void
@@ -1,29 +1,25 @@
1
1
  /**
2
2
  * Shows a ripple effect.
3
3
  * @param {MouseEvent<HTMLElement>} event - The dialog element to dismiss.
4
- * @param {HTMLElement} selector - The options for closing the dialog.
4
+ * @param {HTMLElement} element - The options for closing the dialog.
5
5
  * @returns void
6
6
  */
7
- export const showRipple = ({ currentTarget, offsetX, offsetY }, selector = currentTarget.querySelector('.ripple')) => {
8
- if (!selector) {
7
+ export const showRipple = ({ currentTarget, offsetX, offsetY }, element = currentTarget.querySelector('.ripple')) => {
8
+ if (!element) {
9
9
  currentTarget.insertAdjacentHTML('beforeend', "<div class='ripple'></div>")
10
- selector = currentTarget.querySelector('.ripple')
10
+ element = currentTarget.querySelector('.ripple')
11
11
  }
12
12
 
13
- selector.classList.remove('animation-ripple')
13
+ element.classList.remove('animation-ripple')
14
14
 
15
- if (selector.clientWidth === 0 && selector.clientHeight === 0) {
15
+ if (element.clientWidth === 0 && element.clientHeight === 0) {
16
16
  const d = Math.max(currentTarget.offsetWidth, currentTarget.offsetHeight)
17
17
 
18
- selector.style.width = d + 'px'
19
- selector.style.height = d + 'px'
18
+ element.style.width = d + 'px'
19
+ element.style.height = d + 'px'
20
20
  }
21
21
 
22
- selector.style.top = offsetY - (selector.clientHeight / 2) + 'px'
23
- selector.style.left = offsetX - (selector.clientWidth / 2) + 'px'
24
- selector.classList.add('animation-ripple')
25
- }
26
-
27
- export default {
28
- showRipple
22
+ element.style.top = offsetY - (element.clientHeight / 2) + 'px'
23
+ element.style.left = offsetX - (element.clientWidth / 2) + 'px'
24
+ element.classList.add('animation-ripple')
29
25
  }
@@ -0,0 +1 @@
1
+ export function toggleSwap(element: HTMLElement): void
@@ -10,7 +10,3 @@ export const toggleSwap = (element) => {
10
10
  ariaVisible.ariaHidden = 'true'
11
11
  ariaHidden.ariaHidden = 'false'
12
12
  }
13
-
14
- export default {
15
- toggleSwap
16
- }
@@ -1,4 +1,4 @@
1
- import winduum from './plugin/tailwind.js'
1
+ import winduum from './plugin/index.js'
2
2
  import containerQueries from '@tailwindcss/container-queries'
3
3
  import animate from 'tailwindcss-animate'
4
4