@weser/dates 0.0.10 → 0.0.11

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 CHANGED
@@ -30,14 +30,23 @@ const values = {
30
30
  m: ['numeric', '2-digit'],
31
31
  s: ['numeric', '2-digit'],
32
32
  };
33
- function padIf(condition, value, length) {
34
- return condition && length === 2 && value / 10 < 1 ? '0' + value : value;
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, config = {}) {
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', parseInt(Intl.DateTimeFormat(locale, options).format(date)), 2);
80
+ return padIf(['m', 's'].includes(type) && value === '2-digit', Intl.DateTimeFormat(locale, options).format(date), 2);
71
81
  }
72
- export default function format(date, pattern, config = {}) {
73
- return pattern
74
- .split(ESCAPE_REGEX)
75
- .filter((sub) => sub !== undefined)
76
- .map((sub, index) => {
77
- // keep escaped strings as is
78
- if (index % 2 !== 0) {
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.10",
3
+ "version": "0.0.11",
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": "echo 1"
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": "6dd1ad00ab8dacea2878edc99085324ea5d03588"
50
+ "gitHead": "e2935b6b1b52be5a254d6b249ac61d62a012a3b5"
51
51
  }
@@ -1 +0,0 @@
1
- export default function invariant(condition: boolean, message: string): void;
package/dist/invariant.js DELETED
@@ -1,5 +0,0 @@
1
- export default function invariant(condition, message) {
2
- if (!condition) {
3
- throw new Error(message);
4
- }
5
- }
@@ -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
- }
@@ -1,2 +0,0 @@
1
- import { RefObject } from 'react';
2
- export default function useClickAway(ref: RefObject<HTMLElement | null>, callback: () => void, active?: boolean): void;
@@ -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
- }
@@ -1 +0,0 @@
1
- export default function useClientSide(): boolean;
@@ -1,6 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- export default function useClientSide() {
3
- const [isClientSide, setClientSide] = useState(false);
4
- useEffect(() => setClientSide(true), []);
5
- return isClientSide;
6
- }
@@ -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
- };
@@ -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
- }
@@ -1,7 +0,0 @@
1
- import { RefObject } from 'react';
2
- type Config = {
3
- visible?: boolean;
4
- autoFocus?: boolean;
5
- };
6
- export default function useFocusTrap(ref: RefObject<HTMLElement | null>, active: boolean, config?: Config): void;
7
- export {};
@@ -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
- }
@@ -1,7 +0,0 @@
1
- import { RefObject } from 'react';
2
- type Options = {
3
- target?: RefObject<HTMLElement>;
4
- active?: boolean;
5
- };
6
- export default function useKeyDown(keyCode: string | Array<string>, callback: (e: KeyboardEvent) => void, options?: Options): void;
7
- export {};
@@ -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
- }
@@ -1 +0,0 @@
1
- export default function useRouteChange(onRouteChange: (path: string) => void, pathname?: string): void;
@@ -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
- }
@@ -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 {};
@@ -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
- }