@dxos/react-ui 0.1.2 → 0.1.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 (69) hide show
  1. package/dist/src/components/AlertDialog/AlertDialog.js +1 -1
  2. package/dist/src/components/Heading/Heading.d.ts +1 -1
  3. package/dist/src/components/Heading/Heading.stories.d.ts +1 -1
  4. package/dist/src/components/Input/BarePinInput.d.ts +310 -0
  5. package/dist/src/components/Input/BarePinInput.d.ts.map +1 -0
  6. package/dist/src/components/Input/BarePinInput.js +62 -0
  7. package/dist/src/components/Input/BarePinInput.js.map +1 -0
  8. package/dist/src/components/Input/BareTextInput.d.ts +5 -0
  9. package/dist/src/components/Input/BareTextInput.d.ts.map +1 -0
  10. package/dist/src/components/Input/BareTextInput.js +22 -0
  11. package/dist/src/components/Input/BareTextInput.js.map +1 -0
  12. package/dist/src/components/Input/Input.d.ts +3 -16
  13. package/dist/src/components/Input/Input.d.ts.map +1 -1
  14. package/dist/src/components/Input/Input.js +22 -10
  15. package/dist/src/components/Input/Input.js.map +1 -1
  16. package/dist/src/components/Input/Input.stories.d.ts +7 -3
  17. package/dist/src/components/Input/Input.stories.d.ts.map +1 -1
  18. package/dist/src/components/Input/Input.stories.js +15 -2
  19. package/dist/src/components/Input/Input.stories.js.map +1 -1
  20. package/dist/src/components/Input/InputProps.d.ts +17 -0
  21. package/dist/src/components/Input/InputProps.d.ts.map +1 -0
  22. package/dist/src/components/Input/InputProps.js +6 -0
  23. package/dist/src/components/Input/InputProps.js.map +1 -0
  24. package/dist/src/hooks/index.d.ts +2 -0
  25. package/dist/src/hooks/index.d.ts.map +1 -1
  26. package/dist/src/hooks/index.js +2 -0
  27. package/dist/src/hooks/index.js.map +1 -1
  28. package/dist/src/hooks/useForwardedRef.d.ts +2 -0
  29. package/dist/src/hooks/useForwardedRef.d.ts.map +1 -0
  30. package/dist/src/hooks/useForwardedRef.js +24 -0
  31. package/dist/src/hooks/useForwardedRef.js.map +1 -0
  32. package/dist/src/hooks/useIsFocused.d.ts +3 -0
  33. package/dist/src/hooks/useIsFocused.d.ts.map +1 -0
  34. package/dist/src/hooks/useIsFocused.js +34 -0
  35. package/dist/src/hooks/useIsFocused.js.map +1 -0
  36. package/dist/src/plugin.d.ts.map +1 -1
  37. package/dist/src/plugin.js +9 -1
  38. package/dist/src/plugin.js.map +1 -1
  39. package/dist/src/styles/focus.d.ts +1 -0
  40. package/dist/src/styles/focus.d.ts.map +1 -1
  41. package/dist/src/styles/focus.js +2 -1
  42. package/dist/src/styles/focus.js.map +1 -1
  43. package/dist/src/styles/index.d.ts +1 -0
  44. package/dist/src/styles/index.d.ts.map +1 -1
  45. package/dist/src/styles/index.js +1 -0
  46. package/dist/src/styles/index.js.map +1 -1
  47. package/dist/src/styles/input.d.ts +11 -0
  48. package/dist/src/styles/input.d.ts.map +1 -0
  49. package/dist/src/styles/input.js +24 -0
  50. package/dist/src/styles/input.js.map +1 -0
  51. package/dist/src/styles/shimmer.d.ts +2 -0
  52. package/dist/src/styles/shimmer.d.ts.map +1 -0
  53. package/dist/src/styles/shimmer.js +8 -0
  54. package/dist/src/styles/shimmer.js.map +1 -0
  55. package/package.json +3 -2
  56. package/src/components/AlertDialog/AlertDialog.tsx +1 -1
  57. package/src/components/Input/BarePinInput.tsx +62 -0
  58. package/src/components/Input/BareTextInput.tsx +31 -0
  59. package/src/components/Input/Input.stories.tsx +18 -1
  60. package/src/components/Input/Input.tsx +33 -54
  61. package/src/components/Input/InputProps.ts +23 -0
  62. package/src/hooks/index.ts +2 -0
  63. package/src/hooks/useForwardedRef.ts +22 -0
  64. package/src/hooks/useIsFocused.ts +38 -0
  65. package/src/plugin.ts +9 -1
  66. package/src/styles/focus.ts +3 -0
  67. package/src/styles/index.ts +1 -0
  68. package/src/styles/input.ts +47 -0
  69. package/src/styles/shimmer.ts +6 -0
@@ -3,39 +3,15 @@
3
3
  //
4
4
 
5
5
  import cx from 'classnames';
6
- import React, { ChangeEvent, ComponentProps, ReactNode, useCallback, useState, useTransition } from 'react';
6
+ import React, { ChangeEvent, useCallback, useState, useTransition } from 'react';
7
7
 
8
8
  import { useId } from '../../hooks';
9
- import { MessageValence } from '../../props';
10
- import {
11
- defaultDescription,
12
- defaultDisabled,
13
- defaultFocus,
14
- defaultHover,
15
- defaultPlaceholder,
16
- valenceColorText,
17
- valenceInputBorder
18
- } from '../../styles';
9
+ import { defaultDescription, valenceColorText } from '../../styles';
10
+ import { BarePinInput } from './BarePinInput';
11
+ import { BareTextInput } from './BareTextInput';
12
+ import { InputProps as NaturalInputProps } from './InputProps';
19
13
 
20
- export type InputSize = 'md' | 'lg';
21
-
22
- export interface InputProps extends Omit<ComponentProps<'input'>, 'value' | 'onChange' | 'size'> {
23
- label: ReactNode;
24
- labelVisuallyHidden?: boolean;
25
- description?: ReactNode;
26
- descriptionVisuallyHidden?: boolean;
27
- initialValue?: string;
28
- onChange?: (value: string) => void;
29
- disabled?: boolean;
30
- size?: InputSize;
31
- validationMessage?: ReactNode;
32
- validationValence?: MessageValence;
33
- }
34
-
35
- const sizeMap = new Map<InputSize, string>([
36
- ['md', ''],
37
- ['lg', 'text-base']
38
- ]);
14
+ export type InputProps = NaturalInputProps;
39
15
 
40
16
  export const Input = ({
41
17
  label,
@@ -49,6 +25,7 @@ export const Input = ({
49
25
  disabled,
50
26
  className,
51
27
  size,
28
+ length = 6,
52
29
  validationMessage,
53
30
  validationValence,
54
31
  ...inputProps
@@ -57,6 +34,8 @@ export const Input = ({
57
34
  const descriptionId = useId('input-description');
58
35
  const validationId = useId('input-validation');
59
36
 
37
+ const isInvalid = !!validationMessage && validationValence === 'error';
38
+
60
39
  const [_isPending, startTransition] = useTransition();
61
40
 
62
41
  const [internalValue, setInternalValue] = useState<string>(initialValue?.toString() || '');
@@ -73,7 +52,29 @@ export const Input = ({
73
52
  [onChange]
74
53
  );
75
54
 
76
- const isInvalid = !!validationMessage && validationValence === 'error';
55
+ const bareInputBaseProps = {
56
+ ...inputProps,
57
+ id: inputId,
58
+ ...(required && { required: true }),
59
+ ...(disabled && { disabled: true }),
60
+ ...(description && { 'aria-describedby': descriptionId }),
61
+ ...(isInvalid && {
62
+ 'aria-invalid': 'true' as const,
63
+ 'aria-errormessage': validationId
64
+ }),
65
+ ...(placeholder && { placeholder }),
66
+ value: internalValue,
67
+ onChange: onInternalChange,
68
+ validationMessage,
69
+ validationValence
70
+ };
71
+
72
+ const bareInput =
73
+ size === 'pin' ? (
74
+ <BarePinInput {...bareInputBaseProps} length={length} />
75
+ ) : (
76
+ <BareTextInput {...bareInputBaseProps} size={size} />
77
+ );
77
78
 
78
79
  return (
79
80
  <div className={cx('my-4', className)} role='none'>
@@ -86,29 +87,7 @@ export const Input = ({
86
87
  >
87
88
  {label}
88
89
  </label>
89
- <input
90
- id={inputId}
91
- {...inputProps}
92
- className={cx(
93
- defaultFocus,
94
- defaultPlaceholder,
95
- defaultHover({ disabled }),
96
- sizeMap.get(size ?? 'md'),
97
- 'bg-white/50 border text-neutral-900 text-sm rounded-lg block w-full px-2.5 py-2 dark:bg-neutral-700/50 dark:text-white',
98
- valenceInputBorder(validationMessage ? validationValence : undefined),
99
- disabled && defaultDisabled
100
- )}
101
- {...(required && { required: true })}
102
- {...(disabled && { disabled: true })}
103
- {...(placeholder && { placeholder })}
104
- {...(description && { 'aria-describedby': descriptionId })}
105
- {...(isInvalid && {
106
- 'aria-invalid': 'true',
107
- 'aria-errormessage': validationId
108
- })}
109
- value={internalValue}
110
- onChange={onInternalChange}
111
- />
90
+ {bareInput}
112
91
  {(description || validationMessage) && (
113
92
  <p
114
93
  {...(!isInvalid && { id: descriptionId })}
@@ -0,0 +1,23 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { ComponentProps, ReactNode } from 'react';
6
+
7
+ import { MessageValence } from '../../props';
8
+
9
+ export type InputSize = 'md' | 'lg' | 'pin';
10
+
11
+ export interface InputProps extends Omit<ComponentProps<'input'>, 'value' | 'onChange' | 'size' | 'ref'> {
12
+ label: ReactNode;
13
+ labelVisuallyHidden?: boolean;
14
+ description?: ReactNode;
15
+ descriptionVisuallyHidden?: boolean;
16
+ initialValue?: string;
17
+ onChange?: (value: string) => void;
18
+ disabled?: boolean;
19
+ size?: InputSize;
20
+ validationMessage?: ReactNode;
21
+ validationValence?: MessageValence;
22
+ length?: number;
23
+ }
@@ -3,3 +3,5 @@
3
3
  //
4
4
 
5
5
  export * from './useId';
6
+ export * from './useForwardedRef';
7
+ export * from './useIsFocused';
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import { useRef, useEffect } from 'react';
6
+
7
+ export const useForwardedRef = <T>(ref: React.ForwardedRef<T>) => {
8
+ const innerRef = useRef<T>(null);
9
+
10
+ useEffect(() => {
11
+ if (!ref) {
12
+ return;
13
+ }
14
+ if (typeof ref === 'function') {
15
+ ref(innerRef.current);
16
+ } else {
17
+ ref.current = innerRef.current;
18
+ }
19
+ });
20
+
21
+ return innerRef;
22
+ };
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ // Based upon the useIsFocused hook which is part of the `rci` project:
6
+ /// https://github.com/leonardodino/rci/blob/main/packages/use-is-focused
7
+
8
+ import { useEffect, useRef, useState, RefObject } from 'react';
9
+
10
+ export const useIsFocused = (inputRef: RefObject<HTMLInputElement>) => {
11
+ const [isFocused, setIsFocused] = useState<boolean | undefined>(undefined);
12
+ const isFocusedRef = useRef<boolean | undefined>(isFocused);
13
+
14
+ isFocusedRef.current = isFocused;
15
+
16
+ useEffect(() => {
17
+ const input = inputRef.current;
18
+ if (!input) {
19
+ return;
20
+ }
21
+
22
+ const onFocus = () => setIsFocused(true);
23
+ const onBlur = () => setIsFocused(false);
24
+ input.addEventListener('focus', onFocus);
25
+ input.addEventListener('blur', onBlur);
26
+
27
+ if (isFocusedRef.current === undefined) {
28
+ setIsFocused(document.activeElement === input);
29
+ }
30
+
31
+ return () => {
32
+ input.removeEventListener('focus', onFocus);
33
+ input.removeEventListener('blur', onBlur);
34
+ };
35
+ }, [inputRef, setIsFocused]);
36
+
37
+ return isFocused;
38
+ };
package/src/plugin.ts CHANGED
@@ -223,6 +223,12 @@ export const themePlugin = (options: VitePluginTailwindOptions) => {
223
223
  '100%': {
224
224
  transform: 'translateX(calc(100% + 1rem))'
225
225
  }
226
+ },
227
+ // Shimmer
228
+ 'shimmer-loop': {
229
+ '100%': {
230
+ transform: 'translateX(100%)'
231
+ }
226
232
  }
227
233
  },
228
234
  animation: {
@@ -248,7 +254,9 @@ export const themePlugin = (options: VitePluginTailwindOptions) => {
248
254
  'toast-hide': 'toast-hide 100ms ease-in forwards',
249
255
  'toast-slide-in-right': 'toast-slide-in-right 150ms cubic-bezier(0.16, 1, 0.3, 1)',
250
256
  'toast-slide-in-bottom': 'toast-slide-in-bottom 150ms cubic-bezier(0.16, 1, 0.3, 1)',
251
- 'toast-swipe-out': 'toast-swipe-out 100ms ease-out forwards'
257
+ 'toast-swipe-out': 'toast-swipe-out 100ms ease-out forwards',
258
+ // Shimmer
259
+ shimmer: 'shimmer-loop 2s infinite'
252
260
  }
253
261
  }
254
262
  },
@@ -4,3 +4,6 @@
4
4
 
5
5
  export const defaultFocus =
6
6
  'focus:outline-none focus-visible:hover:outline-none dark:focus-visible:hover:outline-none focus-visible:ring focus-visible:ring-offset-1 focus-visible:ring-primary-500 focus-visible:ring-offset-white dark:focus-visible:ring-primary-200 dark:focus-visible:ring-offset-black';
7
+
8
+ export const staticFocus =
9
+ 'ring ring-offset-1 ring-primary-500 ring-offset-white dark:ring-primary-200 dark:ring-offset-black';
@@ -8,6 +8,7 @@ export * from './focus';
8
8
  export * from './group';
9
9
  export * from './hover';
10
10
  export * from './ornament';
11
+ export * from './shimmer';
11
12
  export * from './size';
12
13
  export * from './text';
13
14
  export * from './valence';
@@ -0,0 +1,47 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import cx from 'classnames';
6
+
7
+ import { MessageValence } from '../props';
8
+ import { defaultDisabled } from './disabled';
9
+ import { defaultFocus, staticFocus } from './focus';
10
+ import { defaultHover } from './hover';
11
+ import { defaultPlaceholder } from './text';
12
+ import { valenceInputBorder } from './valence';
13
+
14
+ export const defaultInput = ({
15
+ disabled,
16
+ validationValence
17
+ }: {
18
+ disabled?: boolean;
19
+ validationValence?: MessageValence;
20
+ }) => {
21
+ return cx(
22
+ defaultFocus,
23
+ defaultPlaceholder,
24
+ defaultHover({ disabled }),
25
+ 'bg-white/50 border text-neutral-900 text-sm rounded-lg dark:bg-neutral-700/50 dark:text-white',
26
+ valenceInputBorder(validationValence),
27
+ disabled && defaultDisabled
28
+ );
29
+ };
30
+
31
+ export const staticInput = ({
32
+ disabled,
33
+ focused,
34
+ validationValence
35
+ }: {
36
+ disabled?: boolean;
37
+ focused?: boolean;
38
+ validationValence?: MessageValence;
39
+ }) => {
40
+ return cx(
41
+ defaultPlaceholder,
42
+ 'bg-white/50 border text-neutral-900 text-sm rounded-lg dark:bg-neutral-700/50 dark:text-white',
43
+ valenceInputBorder(validationValence),
44
+ disabled && defaultDisabled,
45
+ focused && staticFocus
46
+ );
47
+ };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ export const defaultShimmer =
6
+ 'relative before:absolute before:inset-0 before:-translate-x-full before:animate-shimmer before:bg-gradient-to-r before:from-transparent before:via-neutral-100/20 before:to-transparent isolate overflow-hidden before:border-t before:border-neutral-100/20';