@weser/dates 0.0.10 → 0.0.12
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.
- package/dist/format.js +25 -23
- package/package.json +3 -3
- package/dist/invariant.d.ts +0 -1
- package/dist/invariant.js +0 -5
- package/dist/useAction.d.ts +0 -12
- package/dist/useAction.js +0 -40
- package/dist/useClickAway.d.ts +0 -2
- package/dist/useClickAway.js +0 -19
- package/dist/useClientOnly.d.ts +0 -1
- package/dist/useClientOnly.js +0 -6
- package/dist/useDisclosure.d.ts +0 -16
- package/dist/useDisclosure.js +0 -25
- package/dist/useFocusTrap.d.ts +0 -7
- package/dist/useFocusTrap.js +0 -77
- package/dist/useKeyDown.d.ts +0 -7
- package/dist/useKeyDown.js +0 -22
- package/dist/useRouteChange.d.ts +0 -1
- package/dist/useRouteChange.js +0 -19
- package/dist/useScrollBlocking.d.ts +0 -1
- package/dist/useScrollBlocking.js +0 -28
- package/dist/useTrigger.d.ts +0 -7
- package/dist/useTrigger.js +0 -24
package/dist/format.js
CHANGED
|
@@ -30,14 +30,23 @@ const values = {
|
|
|
30
30
|
m: ['numeric', '2-digit'],
|
|
31
31
|
s: ['numeric', '2-digit'],
|
|
32
32
|
};
|
|
33
|
-
function
|
|
34
|
-
return
|
|
33
|
+
export default function format(date, pattern, config = {}) {
|
|
34
|
+
return pattern
|
|
35
|
+
.split(ESCAPE_REGEX)
|
|
36
|
+
.filter((sub) => sub !== undefined)
|
|
37
|
+
.map((sub, index) => {
|
|
38
|
+
// keep escaped strings as is
|
|
39
|
+
if (index % 2 !== 0) {
|
|
40
|
+
return sub;
|
|
41
|
+
}
|
|
42
|
+
return sub.replace(PATTERN_REGEX, (match) => {
|
|
43
|
+
const type = match.charAt(0);
|
|
44
|
+
return String(formatType(date, type, match.length, config) || match);
|
|
45
|
+
});
|
|
46
|
+
})
|
|
47
|
+
.join('');
|
|
35
48
|
}
|
|
36
|
-
function formatType(date, type, length,
|
|
37
|
-
if (!Intl.DateTimeFormat) {
|
|
38
|
-
throw new Error('Intl.DateTimeFormat is not supported');
|
|
39
|
-
}
|
|
40
|
-
const { locale, timeZone } = config;
|
|
49
|
+
function formatType(date, type, length, { locale, timeZone } = {}) {
|
|
41
50
|
const option = optionNames[type];
|
|
42
51
|
const value = values[type][length - 1];
|
|
43
52
|
if (!value) {
|
|
@@ -49,6 +58,7 @@ function formatType(date, type, length, config = {}) {
|
|
|
49
58
|
};
|
|
50
59
|
if (type === 'a') {
|
|
51
60
|
return Intl.DateTimeFormat(locale, {
|
|
61
|
+
...options,
|
|
52
62
|
hour: 'numeric',
|
|
53
63
|
})
|
|
54
64
|
.formatToParts(date)
|
|
@@ -67,21 +77,13 @@ function formatType(date, type, length, config = {}) {
|
|
|
67
77
|
.replace(' am', '')
|
|
68
78
|
.replace(' pm', '');
|
|
69
79
|
}
|
|
70
|
-
return padIf(['m', 's'].includes(type) && value === '2-digit',
|
|
80
|
+
return padIf(['m', 's'].includes(type) && value === '2-digit', Intl.DateTimeFormat(locale, options).format(date), 2);
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return sub;
|
|
80
|
-
}
|
|
81
|
-
return sub.replace(PATTERN_REGEX, (match) => {
|
|
82
|
-
const type = match.charAt(0);
|
|
83
|
-
return String(formatType(date, type, match.length, config) || match);
|
|
84
|
-
});
|
|
85
|
-
})
|
|
86
|
-
.join('');
|
|
82
|
+
function padIf(condition, value, length) {
|
|
83
|
+
if (typeof value === 'string') {
|
|
84
|
+
return condition && length === 2 && parseInt(value) / 10 < 1
|
|
85
|
+
? '0' + value
|
|
86
|
+
: value;
|
|
87
|
+
}
|
|
88
|
+
return condition && length === 2 && value / 10 < 1 ? '0' + value : value;
|
|
87
89
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weser/dates",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Tiny date formatting library with built-in i18n",
|
|
5
5
|
"author": "Robin Weser <robin@weser.io>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"clean": "rimraf dist",
|
|
34
34
|
"build": "tsc -b",
|
|
35
35
|
"dev": "pnpm build -w",
|
|
36
|
-
"test": "
|
|
36
|
+
"test": "ava"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"date",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"rimraf": "^3.0.2",
|
|
48
48
|
"typescript": "^5.4.5"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "8a4cafba91346a2a5bb4301317df661e176eb1d3"
|
|
51
51
|
}
|
package/dist/invariant.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function invariant(condition: boolean, message: string): void;
|
package/dist/invariant.js
DELETED
package/dist/useAction.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export type T_ActionResponse<T = any> = T extends any ? [string] | [null, T?] : [string] | [null, T];
|
|
2
|
-
type State<T> = {
|
|
3
|
-
loading: boolean;
|
|
4
|
-
data?: T | null;
|
|
5
|
-
error: string | null;
|
|
6
|
-
};
|
|
7
|
-
type T_ActionConfig<T> = {
|
|
8
|
-
onSuccess?: (data?: T) => void;
|
|
9
|
-
onError?: (error: string) => void;
|
|
10
|
-
};
|
|
11
|
-
export default function useAction<T, P extends Array<any>>(action: (...payload: P) => Promise<T_ActionResponse<T>>, config?: T_ActionConfig<T>): [State<T>, (...payload: P) => void];
|
|
12
|
-
export {};
|
package/dist/useAction.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
const initialState = {
|
|
3
|
-
loading: false,
|
|
4
|
-
data: null,
|
|
5
|
-
error: null,
|
|
6
|
-
};
|
|
7
|
-
export default function useAction(action, config = {}) {
|
|
8
|
-
const { onSuccess, onError } = config;
|
|
9
|
-
const [state, setState] = useState(initialState);
|
|
10
|
-
async function run(...payload) {
|
|
11
|
-
setState({
|
|
12
|
-
...state,
|
|
13
|
-
loading: true,
|
|
14
|
-
});
|
|
15
|
-
const [error, data] = await action(...payload);
|
|
16
|
-
if (error !== null) {
|
|
17
|
-
if (onError) {
|
|
18
|
-
onError(error);
|
|
19
|
-
}
|
|
20
|
-
setState({
|
|
21
|
-
...state,
|
|
22
|
-
error,
|
|
23
|
-
data: null,
|
|
24
|
-
loading: false,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
if (onSuccess) {
|
|
29
|
-
onSuccess(data);
|
|
30
|
-
}
|
|
31
|
-
setState({
|
|
32
|
-
...state,
|
|
33
|
-
error: null,
|
|
34
|
-
data,
|
|
35
|
-
loading: false,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return [state, run];
|
|
40
|
-
}
|
package/dist/useClickAway.d.ts
DELETED
package/dist/useClickAway.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
export default function useClickAway(ref, callback, active = true) {
|
|
3
|
-
useEffect(() => {
|
|
4
|
-
const onClickAway = (e) => {
|
|
5
|
-
if (active && ref.current) {
|
|
6
|
-
const isClickOnInner = ref.current.contains(e.target);
|
|
7
|
-
if (!isClickOnInner) {
|
|
8
|
-
setTimeout(callback, 0);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
document.addEventListener('mousedown', onClickAway);
|
|
13
|
-
document.addEventListener('touchstart', onClickAway);
|
|
14
|
-
return () => {
|
|
15
|
-
document.removeEventListener('mousedown', onClickAway);
|
|
16
|
-
document.removeEventListener('touchstart', onClickAway);
|
|
17
|
-
};
|
|
18
|
-
}, [ref, active, callback]);
|
|
19
|
-
}
|
package/dist/useClientOnly.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function useClientSide(): boolean;
|
package/dist/useClientOnly.js
DELETED
package/dist/useDisclosure.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export default function useDisclosure(defaultExpanded?: boolean): {
|
|
2
|
-
toggleProps: {
|
|
3
|
-
id: string;
|
|
4
|
-
onClick: void;
|
|
5
|
-
type: string;
|
|
6
|
-
'aria-expanded': boolean;
|
|
7
|
-
'aria-controls': string;
|
|
8
|
-
};
|
|
9
|
-
contentProps: {
|
|
10
|
-
id: string;
|
|
11
|
-
'aria-hidden': boolean;
|
|
12
|
-
'aria-labelledby': string;
|
|
13
|
-
};
|
|
14
|
-
isExpanded: boolean;
|
|
15
|
-
toggle: void;
|
|
16
|
-
};
|
package/dist/useDisclosure.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useId } from 'react';
|
|
2
|
-
export default function useDisclosure(defaultExpanded = false) {
|
|
3
|
-
const [isExpanded, setExpanded] = useState(defaultExpanded);
|
|
4
|
-
const id = useId();
|
|
5
|
-
useEffect(() => setExpanded(defaultExpanded), [defaultExpanded]);
|
|
6
|
-
const toggle = setExpanded((isExpanded) => !isExpanded);
|
|
7
|
-
const toggleProps = {
|
|
8
|
-
id: id + '-toggle',
|
|
9
|
-
onClick: toggle,
|
|
10
|
-
type: 'button',
|
|
11
|
-
'aria-expanded': isExpanded,
|
|
12
|
-
'aria-controls': id + '-content',
|
|
13
|
-
};
|
|
14
|
-
const contentProps = {
|
|
15
|
-
id: id + '-content',
|
|
16
|
-
'aria-hidden': !isExpanded,
|
|
17
|
-
'aria-labelledby': id + '-toggle',
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
toggleProps,
|
|
21
|
-
contentProps,
|
|
22
|
-
isExpanded,
|
|
23
|
-
toggle,
|
|
24
|
-
};
|
|
25
|
-
}
|
package/dist/useFocusTrap.d.ts
DELETED
package/dist/useFocusTrap.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import useKeyDown from './useKeyDown.js';
|
|
3
|
-
const focusableSelector = `:is(
|
|
4
|
-
a[href],
|
|
5
|
-
area[href],
|
|
6
|
-
input:not([disabled]),
|
|
7
|
-
select:not([disabled]),
|
|
8
|
-
textarea:not([disabled]),
|
|
9
|
-
button:not([disabled]),
|
|
10
|
-
[tabindex]:not([tabindex="-1"]),
|
|
11
|
-
[contenteditable]
|
|
12
|
-
)`;
|
|
13
|
-
export default function useFocusTrap(ref, active, config = {}) {
|
|
14
|
-
const { visible, autoFocus = true } = config;
|
|
15
|
-
useKeyDown('Tab', (e) => {
|
|
16
|
-
const element = ref.current;
|
|
17
|
-
if (!active || !element) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const focusables = [
|
|
21
|
-
...element.querySelectorAll(focusableSelector),
|
|
22
|
-
].filter((el) => el.hasAttribute('tabindex')
|
|
23
|
-
? el.getAttribute('tabindex') !== '-1'
|
|
24
|
-
: true);
|
|
25
|
-
const firstFocusable = focusables[0];
|
|
26
|
-
const lastFocusable = focusables[focusables.length - 1];
|
|
27
|
-
const activeElement = document.activeElement;
|
|
28
|
-
let nextElement;
|
|
29
|
-
if (activeElement && focusables.includes(activeElement)) {
|
|
30
|
-
const index = focusables.indexOf(activeElement);
|
|
31
|
-
if (e.shiftKey) {
|
|
32
|
-
if (index === 0) {
|
|
33
|
-
nextElement = lastFocusable;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
if (index === focusables.length - 1) {
|
|
38
|
-
nextElement = firstFocusable;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
if (e.shiftKey) {
|
|
44
|
-
nextElement = lastFocusable;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
nextElement = firstFocusable;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (nextElement) {
|
|
51
|
-
;
|
|
52
|
-
nextElement.focus();
|
|
53
|
-
e.preventDefault();
|
|
54
|
-
}
|
|
55
|
-
}, {
|
|
56
|
-
active,
|
|
57
|
-
});
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const element = ref.current;
|
|
60
|
-
if (!autoFocus || !visible || !element) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const autoFocusElement = element.querySelector('[data-autofocus="true"]' + focusableSelector);
|
|
64
|
-
if (autoFocusElement) {
|
|
65
|
-
// @ts-ignore
|
|
66
|
-
autoFocusElement.focus();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const nodeList = element.querySelectorAll(focusableSelector);
|
|
70
|
-
const elements = Array.from(nodeList);
|
|
71
|
-
const focusableElements = elements.filter((element) => !element.hasAttribute('tabindex') ||
|
|
72
|
-
element.getAttribute('tabindex') === '0');
|
|
73
|
-
// 1. focus the first focusable
|
|
74
|
-
// @ts-ignore, TODO: fix typing
|
|
75
|
-
focusableElements[0]?.focus();
|
|
76
|
-
}, [visible, ref, autoFocus]);
|
|
77
|
-
}
|
package/dist/useKeyDown.d.ts
DELETED
package/dist/useKeyDown.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
export default function useKeyDown(keyCode, callback, options = {}) {
|
|
3
|
-
const { active = true, target } = options;
|
|
4
|
-
const keyCodes = [].concat(keyCode);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
if (!active || target === null || target?.current === null) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
const hasRef = target && target.current;
|
|
10
|
-
const element = hasRef ? target.current : document;
|
|
11
|
-
if (element) {
|
|
12
|
-
const handleKeyDown = (e) => {
|
|
13
|
-
if (keyCodes.includes(e.code) &&
|
|
14
|
-
(!hasRef || (hasRef && document.activeElement === element))) {
|
|
15
|
-
callback(e);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
element.addEventListener('keydown', handleKeyDown);
|
|
19
|
-
return () => element.removeEventListener('keydown', handleKeyDown);
|
|
20
|
-
}
|
|
21
|
-
}, [target, callback, active, keyCode]);
|
|
22
|
-
}
|
package/dist/useRouteChange.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function useRouteChange(onRouteChange: (path: string) => void, pathname?: string): void;
|
package/dist/useRouteChange.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
export default function useRouteChange(onRouteChange, pathname) {
|
|
3
|
-
useEffect(() => {
|
|
4
|
-
if (pathname) {
|
|
5
|
-
onRouteChange(pathname);
|
|
6
|
-
}
|
|
7
|
-
}, [pathname]);
|
|
8
|
-
// track clicks on links with the current path
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const onClick = (e) => {
|
|
11
|
-
const target = e.target;
|
|
12
|
-
if (target.tagName === 'A' && target.href === pathname) {
|
|
13
|
-
onRouteChange(pathname);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
window.addEventListener('click', onClick);
|
|
17
|
-
return () => window.removeEventListener('click', onClick);
|
|
18
|
-
}, []);
|
|
19
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function useScrollBlocking(active: boolean): void;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
let scrollTop;
|
|
3
|
-
function blockScrolling(scrollElement) {
|
|
4
|
-
scrollTop = window.scrollY;
|
|
5
|
-
scrollElement.style.overflow = 'hidden';
|
|
6
|
-
scrollElement.style.position = 'fixed';
|
|
7
|
-
scrollElement.style.width = '100%';
|
|
8
|
-
scrollElement.style.top = -scrollTop + 'px';
|
|
9
|
-
}
|
|
10
|
-
function enableScrolling(scrollElement) {
|
|
11
|
-
scrollElement.style.removeProperty('position');
|
|
12
|
-
scrollElement.style.removeProperty('overflow');
|
|
13
|
-
scrollElement.style.removeProperty('top');
|
|
14
|
-
scrollElement.style.removeProperty('width');
|
|
15
|
-
window.scrollTo(0, scrollTop);
|
|
16
|
-
}
|
|
17
|
-
function toggleScrolling(isBlocked) {
|
|
18
|
-
const scrollElement = document.scrollingElement;
|
|
19
|
-
if (isBlocked) {
|
|
20
|
-
blockScrolling(scrollElement);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
enableScrolling(scrollElement);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
export default function useScrollBlocking(active) {
|
|
27
|
-
useEffect(() => toggleScrolling(active), [active]);
|
|
28
|
-
}
|
package/dist/useTrigger.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { RefObject } from 'react';
|
|
2
|
-
type Props = {
|
|
3
|
-
defaultVisible?: boolean;
|
|
4
|
-
getTrigger?: () => HTMLElement | null;
|
|
5
|
-
};
|
|
6
|
-
export default function useTrigger({ defaultVisible, getTrigger, }?: Props): [boolean, (visible: boolean) => void, RefObject<HTMLElement>];
|
|
7
|
-
export {};
|
package/dist/useTrigger.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
export default function useTrigger({ defaultVisible = false, getTrigger, } = {}) {
|
|
3
|
-
const triggerRef = useRef(null);
|
|
4
|
-
const [isVisible, _setVisible] = useState(false);
|
|
5
|
-
function getTriggerElement() {
|
|
6
|
-
if (getTrigger) {
|
|
7
|
-
return getTrigger();
|
|
8
|
-
}
|
|
9
|
-
return triggerRef.current;
|
|
10
|
-
}
|
|
11
|
-
function setVisible(visible) {
|
|
12
|
-
if (isVisible !== visible) {
|
|
13
|
-
_setVisible(visible);
|
|
14
|
-
if (!visible) {
|
|
15
|
-
const trigger = getTriggerElement();
|
|
16
|
-
if (trigger) {
|
|
17
|
-
trigger.focus();
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
useEffect(() => _setVisible(defaultVisible), [defaultVisible]);
|
|
23
|
-
return [isVisible, setVisible, triggerRef];
|
|
24
|
-
}
|