@tcn/ui 0.0.4 → 0.2.0
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/README.md +38 -3
- package/dist/actions/button/base_button/base_button.d.ts.map +1 -1
- package/dist/actions/button/base_button/base_button.js +17 -12
- package/dist/actions/button/base_button/base_button.js.map +1 -1
- package/dist/actions/button/button/button.d.ts.map +1 -1
- package/dist/actions/button/button/button.js +7 -7
- package/dist/actions/button/button/button.js.map +1 -1
- package/dist/actions/button/slim_button/slim_button.js +2 -2
- package/dist/actions/button/slim_button/slim_button.js.map +1 -1
- package/dist/button.css +1 -1
- package/dist/draggable.css +1 -0
- package/dist/feedback/progress/progress_bar.js +1 -1
- package/dist/footer.css +1 -1
- package/dist/form/field/common/field_description.js +1 -1
- package/dist/form/field/common/field_error.js +4 -3
- package/dist/form/field/common/field_error.js.map +1 -1
- package/dist/form/field/common/field_label.js +1 -1
- package/dist/inputs/date_picker/date_picker_date.js +1 -1
- package/dist/inputs/date_picker/date_picker_day.js +1 -1
- package/dist/inputs/date_picker/date_picker_time_selector.js +1 -1
- package/dist/inputs/date_picker/date_picker_year_selector.js +1 -1
- package/dist/inputs/input/input.d.ts +2 -2
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/input/input.js.map +1 -1
- package/dist/inputs/options/option.d.ts +1 -0
- package/dist/inputs/options/option.d.ts.map +1 -1
- package/dist/inputs/options/option.js.map +1 -1
- package/dist/inputs/phone_number_input/phone_number_input.d.ts +8 -1
- package/dist/inputs/phone_number_input/phone_number_input.d.ts.map +1 -1
- package/dist/inputs/phone_number_input/phone_number_input.js +187 -137
- package/dist/inputs/phone_number_input/phone_number_input.js.map +1 -1
- package/dist/inputs/suggestions/suggestion_item.d.ts +1 -1
- package/dist/inputs/suggestions/suggestion_item.d.ts.map +1 -1
- package/dist/inputs/suggestions/suggestion_item.js +23 -18
- package/dist/inputs/suggestions/suggestion_item.js.map +1 -1
- package/dist/inputs/suggestions/suggestion_list.d.ts +1 -1
- package/dist/inputs/suggestions/suggestion_list.d.ts.map +1 -1
- package/dist/inputs/suggestions/suggestion_list.js +106 -96
- package/dist/inputs/suggestions/suggestion_list.js.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts +2 -2
- package/dist/inputs/textarea/textarea.d.ts.map +1 -1
- package/dist/inputs/textarea/textarea.js.map +1 -1
- package/dist/layouts/footer/footer.js +5 -5
- package/dist/layouts/footer/footer.js.map +1 -1
- package/dist/layouts/header/header.d.ts.map +1 -1
- package/dist/layouts/header/header.js.map +1 -1
- package/dist/layouts/index.d.ts +3 -2
- package/dist/layouts/index.d.ts.map +1 -1
- package/dist/layouts/index.js +26 -24
- package/dist/layouts/index.js.map +1 -1
- package/dist/layouts/list/item.d.ts +1 -0
- package/dist/layouts/list/item.d.ts.map +1 -1
- package/dist/layouts/list/item.js +17 -6
- package/dist/layouts/list/item.js.map +1 -1
- package/dist/layouts/list/list.js +10 -10
- package/dist/layouts/list/list.js.map +1 -1
- package/dist/overlay/context_menu/context_menu.js +4 -4
- package/dist/overlay/frame/frame.d.ts +11 -0
- package/dist/overlay/frame/frame.d.ts.map +1 -0
- package/dist/overlay/frame/frame.js +18 -0
- package/dist/overlay/frame/frame.js.map +1 -0
- package/dist/overlay/index.d.ts +1 -0
- package/dist/overlay/index.d.ts.map +1 -1
- package/dist/overlay/index.js +5 -3
- package/dist/overlay/index.js.map +1 -1
- package/dist/overlay/popper/popper.js +12 -12
- package/dist/{portal-qqIp4SIl.js → overlay/portal/portal.js} +3 -3
- package/dist/overlay/portal/portal.js.map +1 -0
- package/dist/overlay/portal/portal_platform.js +3 -4
- package/dist/overlay/portal/portal_platform.js.map +1 -1
- package/dist/phone_number_input.css +1 -1
- package/dist/slim_button.css +1 -1
- package/dist/stacks/box/box.d.ts +1 -1
- package/dist/stacks/box/box.d.ts.map +1 -1
- package/dist/stacks/box/box.js.map +1 -1
- package/dist/surfaces/card/card.d.ts.map +1 -1
- package/dist/surfaces/card/card.js +7 -16
- package/dist/surfaces/card/card.js.map +1 -1
- package/dist/surfaces/confirm/confirm.js +4 -4
- package/dist/surfaces/index.d.ts +2 -2
- package/dist/surfaces/index.d.ts.map +1 -1
- package/dist/surfaces/index.js +22 -22
- package/dist/surfaces/modal/modal.d.ts +3 -2
- package/dist/surfaces/modal/modal.d.ts.map +1 -1
- package/dist/surfaces/modal/modal.js +14 -13
- package/dist/surfaces/modal/modal.js.map +1 -1
- package/dist/surfaces/window/window.d.ts +3 -2
- package/dist/surfaces/window/window.d.ts.map +1 -1
- package/dist/surfaces/window/window.js +17 -7
- package/dist/surfaces/window/window.js.map +1 -1
- package/dist/themes/index.js +6 -141
- package/dist/themes/index.js.map +1 -1
- package/dist/themes/stylesheets/reset.js +140 -0
- package/dist/themes/stylesheets/reset.js.map +1 -0
- package/dist/themes/themes/ergo/ergo_theme.js +664 -0
- package/dist/themes/themes/ergo/ergo_theme.js.map +1 -0
- package/dist/tokens/bubble/bubble.js +17 -16
- package/dist/tokens/bubble/bubble.js.map +1 -1
- package/dist/tokens/chip/chip.js +9 -8
- package/dist/tokens/chip/chip.js.map +1 -1
- package/dist/typography/title/title.d.ts +2 -1
- package/dist/typography/title/title.d.ts.map +1 -1
- package/dist/typography/title/title.js +24 -23
- package/dist/typography/title/title.js.map +1 -1
- package/dist/utils/dnd/context.d.ts +4 -0
- package/dist/utils/dnd/context.d.ts.map +1 -0
- package/dist/utils/dnd/context.js +20 -0
- package/dist/utils/dnd/context.js.map +1 -0
- package/dist/utils/dnd/draggable/draggable.d.ts +7 -0
- package/dist/utils/dnd/draggable/draggable.d.ts.map +1 -0
- package/dist/utils/dnd/draggable/draggable.js +27 -0
- package/dist/utils/dnd/draggable/draggable.js.map +1 -0
- package/dist/utils/dnd/handle.d.ts +6 -0
- package/dist/utils/dnd/handle.d.ts.map +1 -0
- package/dist/utils/dnd/handle.js +22 -0
- package/dist/utils/dnd/handle.js.map +1 -0
- package/dist/utils/dnd/hooks/use_drag_container.d.ts +7 -0
- package/dist/utils/dnd/hooks/use_drag_container.d.ts.map +1 -0
- package/dist/utils/dnd/hooks/use_drag_container.js +30 -0
- package/dist/utils/dnd/hooks/use_drag_container.js.map +1 -0
- package/dist/utils/{hooks → dnd/hooks}/use_draggable.d.ts +3 -3
- package/dist/utils/dnd/hooks/use_draggable.d.ts.map +1 -0
- package/dist/utils/dnd/hooks/use_draggable.js +41 -0
- package/dist/utils/dnd/hooks/use_draggable.js.map +1 -0
- package/dist/utils/dnd/types.d.ts +10 -0
- package/dist/utils/dnd/types.d.ts.map +1 -0
- package/dist/utils/dnd/types.js +2 -0
- package/dist/utils/dnd/types.js.map +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +9 -3
- package/src/actions/button/base_button/base_button.tsx +7 -2
- package/src/actions/button/button/button.module.css +0 -78
- package/src/actions/button/button/button.tsx +2 -4
- package/src/actions/button/slim_button/slim_button.module.css +0 -26
- package/src/actions/button/slim_button/slim_button.tsx +1 -1
- package/src/inputs/input/input.tsx +3 -2
- package/src/inputs/options/option.tsx +1 -0
- package/src/inputs/phone_number_input/phone_number_input.module.css +12 -0
- package/src/inputs/phone_number_input/phone_number_input.stories.tsx +8 -0
- package/src/inputs/phone_number_input/phone_number_input.tsx +107 -21
- package/src/inputs/suggestions/suggestion_item.tsx +12 -2
- package/src/inputs/suggestions/suggestion_list.tsx +22 -3
- package/src/inputs/textarea/textarea.tsx +2 -2
- package/src/layouts/footer/footer.module.css +0 -1
- package/src/layouts/footer/footer.tsx +1 -1
- package/src/layouts/header/header.tsx +0 -1
- package/src/layouts/index.ts +3 -2
- package/src/layouts/list/item.tsx +10 -2
- package/src/layouts/list/list.tsx +2 -2
- package/src/overlay/frame/frame.stories.tsx +40 -0
- package/src/overlay/frame/frame.tsx +34 -0
- package/src/overlay/frame/frame_stories.module.css +14 -0
- package/src/overlay/index.ts +1 -0
- package/src/stacks/box/box.tsx +8 -2
- package/src/surfaces/card/card.tsx +2 -8
- package/src/surfaces/index.ts +2 -2
- package/src/surfaces/modal/__stories__/modal.stories.tsx +19 -27
- package/src/surfaces/modal/modal.tsx +13 -10
- package/src/surfaces/panel/__stories__/panel.stories.tsx +13 -12
- package/src/surfaces/window/window.stories.tsx +37 -4
- package/src/surfaces/window/window.tsx +14 -6
- package/src/themes/themes/ergo/__stories__/components/material_picker/sb_inverted_materials.module.css +34 -0
- package/src/themes/themes/ergo/__stories__/components/material_picker/sb_material_picker.tsx +52 -0
- package/src/themes/themes/ergo/__stories__/components/tone_picker/sb_card.module.css +5 -0
- package/src/themes/themes/ergo/__stories__/components/tone_picker/sb_tone_card.tsx +40 -0
- package/src/themes/themes/ergo/__stories__/components/tone_picker/sb_tone_picker.tsx +83 -0
- package/src/themes/themes/ergo/__stories__/components/tone_picker/types.ts +7 -0
- package/src/themes/themes/ergo/__stories__/material.stories.tsx +154 -0
- package/src/themes/themes/ergo/__stories__/sb_materials.module.css +110 -0
- package/src/themes/themes/ergo/__stories__/utils.ts +92 -0
- package/src/themes/themes/ergo/ergo_theme.css +358 -26
- package/src/typography/title/title.tsx +23 -19
- package/src/utils/dnd/__stories__/draggable.stories.tsx +48 -0
- package/src/utils/dnd/__stories__/draggable_stories.module.css +21 -0
- package/src/utils/{__stories__ → dnd/__stories__}/use_draggable.stories.tsx +15 -10
- package/src/utils/dnd/context.ts +24 -0
- package/src/utils/dnd/draggable/draggable.module.css +8 -0
- package/src/utils/dnd/draggable/draggable.tsx +42 -0
- package/src/utils/dnd/handle.tsx +32 -0
- package/src/utils/dnd/hooks/use_drag_container.ts +42 -0
- package/src/utils/{hooks → dnd/hooks}/use_draggable.ts +23 -17
- package/src/utils/dnd/types.ts +6 -0
- package/src/utils/index.ts +1 -1
- package/tsconfig.json +0 -3
- package/dist/card.css +0 -1
- package/dist/portal-qqIp4SIl.js.map +0 -1
- package/dist/themes/stylesheets/reset.css +0 -1
- package/dist/themes/themes/ergo/ergo_theme.css +0 -1
- package/dist/themes/themes/windows_98/windows_98.css +0 -1
- package/dist/title.module-B16de2jd.js +0 -5
- package/dist/title.module-B16de2jd.js.map +0 -1
- package/dist/utils/hooks/use_draggable.d.ts.map +0 -1
- package/dist/utils/hooks/use_draggable.js +0 -30
- package/dist/utils/hooks/use_draggable.js.map +0 -1
- package/src/surfaces/card/card.module.css +0 -5
- /package/dist/{overlay/portal/portal.css → portal_platform.css} +0 -0
|
@@ -6,90 +6,12 @@
|
|
|
6
6
|
--button-color-disabled: var(--status-disabled, #cccccc);
|
|
7
7
|
|
|
8
8
|
min-height: var(--button-height);
|
|
9
|
-
padding: 4px 8px;
|
|
10
|
-
border-radius: 4px;
|
|
11
9
|
width: auto;
|
|
12
|
-
border: 1px solid transparent;
|
|
13
|
-
outline: none;
|
|
14
10
|
cursor: pointer;
|
|
15
|
-
background: transparent;
|
|
16
11
|
font-size: calc(14px * var(--scalar, 1));
|
|
17
12
|
line-height: calc(16px * var(--scalar, 1));
|
|
18
13
|
}
|
|
19
14
|
|
|
20
|
-
.button:focus-visible {
|
|
21
|
-
outline: 3px solid color-mix(in srgb, var(--button-color) 40%, transparent);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/* Primary */
|
|
25
|
-
.button[data-hierarchy="primary"] {
|
|
26
|
-
background: var(--button-color);
|
|
27
|
-
color: #ffffff;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.button[data-hierarchy="primary"]:hover {
|
|
31
|
-
background: var(--button-color-hover);
|
|
32
|
-
color: #ffffff;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.button[data-hierarchy="primary"]:active {
|
|
36
|
-
background: var(--button-color-active);
|
|
37
|
-
box-shadow: none;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Secondary */
|
|
41
|
-
.button[data-hierarchy="secondary"] {
|
|
42
|
-
color: rgb(57, 85, 120);
|
|
43
|
-
border-color: rgb(57, 85, 120);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.button[data-hierarchy="secondary"]:hover {
|
|
47
|
-
background: color-mix(in srgb, rgb(57, 85, 120) 10%, rgb(255, 255, 255, 0.5));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.button[data-hierarchy="secondary"]:active {
|
|
51
|
-
background: color-mix(in srgb, rgb(57, 85, 120) 25%, rgb(255, 255, 255, 0.5));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/* Tertiary */
|
|
55
|
-
.button[data-hierarchy="tertiary"] {
|
|
56
|
-
border-color: transparent;
|
|
57
|
-
color: rgb(57, 85, 120);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.button[data-hierarchy="tertiary"]:hover {
|
|
61
|
-
text-decoration: underline;
|
|
62
|
-
text-decoration-color: rgb(57, 85, 120);
|
|
63
|
-
text-decoration-thickness: 1px;
|
|
64
|
-
text-underline-offset: 2px;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.button[data-hierarchy="tertiary"]:active {
|
|
68
|
-
opacity: 0.5;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* Disabled */
|
|
72
|
-
.button[data-is-disabled="true"] {
|
|
73
|
-
pointer-events: none;
|
|
74
|
-
background: var(--button-color-disabled);
|
|
75
|
-
color: #ffffff;
|
|
76
|
-
border-color: var(--button-color-disabled);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/* Disabled Secondary */
|
|
80
|
-
.button[data-hierarchy="secondary"][data-is-disabled="true"] {
|
|
81
|
-
background: transparent;
|
|
82
|
-
color: var(--button-color-disabled);
|
|
83
|
-
border-color: var(--button-color-disabled);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Disabled Tertiary */
|
|
87
|
-
.button[data-hierarchy="tertiary"][data-is-disabled="true"] {
|
|
88
|
-
background: transparent;
|
|
89
|
-
color: var(--button-color-disabled);
|
|
90
|
-
border-color: transparent;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
15
|
/* Sizes */
|
|
94
16
|
.button[data-size="sm"] {
|
|
95
17
|
--button-height: calc(22px * var(--scalar, 1));
|
|
@@ -12,13 +12,11 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function
|
|
|
12
12
|
return (
|
|
13
13
|
<BaseButton
|
|
14
14
|
ref={ref}
|
|
15
|
-
className={clsx(styles.button, '
|
|
15
|
+
className={clsx(styles.button, 'tcn-button', className)}
|
|
16
16
|
{...props}
|
|
17
17
|
>
|
|
18
18
|
{typeof children === 'string' ? (
|
|
19
|
-
<span className={clsx(styles['button-text'], '
|
|
20
|
-
{children}
|
|
21
|
-
</span>
|
|
19
|
+
<span className={clsx(styles['button-text'], 'tcn-button-text')}>{children}</span>
|
|
22
20
|
) : (
|
|
23
21
|
children
|
|
24
22
|
)}
|
|
@@ -5,29 +5,3 @@
|
|
|
5
5
|
.slim-button[data-is-disabled="true"] {
|
|
6
6
|
pointer-events: none;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
.slim-button[data-size="sm"] {
|
|
10
|
-
padding: 1px;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.slim-button[data-size="md"] {
|
|
14
|
-
padding: 2px;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.slim-button[data-size="lg"] {
|
|
18
|
-
padding: 3px;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.slim-button[data-hierarchy="tertiary"][data-is-disabled="true"] {
|
|
22
|
-
background-color: transparent;
|
|
23
|
-
border: none;
|
|
24
|
-
color: var(--button-color-disabled);
|
|
25
|
-
min-height: auto;
|
|
26
|
-
pointer-events: none;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.slim-button[data-hierarchy="secondary"][data-is-disabled="true"] {
|
|
30
|
-
background-color: transparent;
|
|
31
|
-
border: 1px solid var(--button-color-disabled);
|
|
32
|
-
color: var(--button-color-disabled);
|
|
33
|
-
}
|
|
@@ -9,7 +9,7 @@ export const SlimButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
9
9
|
return (
|
|
10
10
|
<Button
|
|
11
11
|
ref={ref}
|
|
12
|
-
className={clsx(styles['slim-button'], 'slim-button', className)}
|
|
12
|
+
className={clsx(styles['slim-button'], 'tcn-slim-button', className)}
|
|
13
13
|
{...props}
|
|
14
14
|
>
|
|
15
15
|
{children}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { InputHTMLAttributes } from 'react';
|
|
4
4
|
import styles from './input.module.css';
|
|
5
5
|
|
|
6
|
-
export interface InputProps
|
|
6
|
+
export interface InputProps
|
|
7
|
+
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
|
7
8
|
type?: React.HTMLInputTypeAttribute;
|
|
8
9
|
width?: string;
|
|
9
10
|
height?: string;
|
|
@@ -48,3 +48,15 @@
|
|
|
48
48
|
max-height: calc(26px * var(--scalar, 1)) !important;
|
|
49
49
|
max-width: calc(26px * var(--scalar, 1)) !important;
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
.phone-number-input-obfuscated {
|
|
53
|
+
user-select: none;
|
|
54
|
+
-webkit-user-select: none;
|
|
55
|
+
cursor: default;
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.phone-number-input-obfuscated::selection {
|
|
61
|
+
background: transparent;
|
|
62
|
+
}
|
|
@@ -90,6 +90,14 @@ export function PhoneNumberInput() {
|
|
|
90
90
|
>
|
|
91
91
|
Bob Johnson - +1 (435) 586-5955
|
|
92
92
|
</Option>
|
|
93
|
+
<Option
|
|
94
|
+
value="+14355865956"
|
|
95
|
+
label="Obfuscated Number"
|
|
96
|
+
keywords={['obfuscated', 'number']}
|
|
97
|
+
obfuscate={true}
|
|
98
|
+
>
|
|
99
|
+
Obfuscated Number
|
|
100
|
+
</Option>
|
|
93
101
|
</Base>
|
|
94
102
|
</td>
|
|
95
103
|
</tr>
|
|
@@ -21,6 +21,16 @@ import { NotebookIcon } from '@tcn/icons/notebook_icon.js';
|
|
|
21
21
|
import { Option, OptionProps } from '../options/option.js';
|
|
22
22
|
import { SuggestionList } from '../suggestions/suggestion_list.js';
|
|
23
23
|
import { stripNonNumericAfterCountryCode } from './utils.js';
|
|
24
|
+
import { useForkRef } from '../../utils/index.js';
|
|
25
|
+
|
|
26
|
+
const OBFUSCATED_CHARACTER = '•';
|
|
27
|
+
|
|
28
|
+
function createObfuscatedMasks(masks: { mask: string; placeholder?: string }[]) {
|
|
29
|
+
return masks.map(m => ({
|
|
30
|
+
...m,
|
|
31
|
+
placeholder: m.mask.replace(/[9a*]/g, OBFUSCATED_CHARACTER),
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
24
34
|
|
|
25
35
|
const countryList = countriesPhoneInformation.map(i => ({
|
|
26
36
|
name: i.name,
|
|
@@ -108,8 +118,15 @@ function getCountryCodeFromValue(
|
|
|
108
118
|
export interface PhoneNumberInputProps
|
|
109
119
|
extends Omit<HStackProps, 'onChange' | 'children'> {
|
|
110
120
|
value?: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
autoComplete?: string;
|
|
111
123
|
defaultCountry?: string;
|
|
112
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Callback fired when the phone number value changes.
|
|
126
|
+
* @param value - The phone number value with country prefix
|
|
127
|
+
* @param obfuscate - Whether the selected phone number is obfuscated (e.g., from a phone book entry marked as obfuscated)
|
|
128
|
+
*/
|
|
129
|
+
onChange?: (value: string, obfuscate: boolean) => void;
|
|
113
130
|
countrySelectRef?: React.Ref<HTMLButtonElement>;
|
|
114
131
|
phoneNumberInputRef?: React.Ref<HTMLInputElement>;
|
|
115
132
|
disabled?: boolean;
|
|
@@ -120,6 +137,8 @@ export interface PhoneNumberInputProps
|
|
|
120
137
|
export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
121
138
|
{
|
|
122
139
|
value = '',
|
|
140
|
+
name,
|
|
141
|
+
autoComplete,
|
|
123
142
|
defaultCountry = 'US',
|
|
124
143
|
onChange,
|
|
125
144
|
countrySelectRef: countryRef,
|
|
@@ -161,6 +180,10 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
161
180
|
const [currentMasks, setCurrentMasks] = useState([
|
|
162
181
|
...countriesPhoneInformation[0].masks,
|
|
163
182
|
]);
|
|
183
|
+
const [obfuscateValue, setObfuscateValue] = useState(false);
|
|
184
|
+
const [shouldFocusAfterClear, setShouldFocusAfterClear] = useState(false);
|
|
185
|
+
const internalInputRef = useRef<HTMLInputElement>(null);
|
|
186
|
+
const forkedInputRef = useForkRef(numberRef, internalInputRef);
|
|
164
187
|
|
|
165
188
|
const countryOptions = useMemo(() => {
|
|
166
189
|
return createCountryOptions(allowedCountryCodes);
|
|
@@ -183,7 +206,7 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
183
206
|
|
|
184
207
|
const value = `${countryInformation.prefix}${stripNonNumericAfterCountryCode(phoneNumber)}`;
|
|
185
208
|
lastOutputValueRef.current = value;
|
|
186
|
-
onChange && onChange(value);
|
|
209
|
+
onChange && onChange(value, false);
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
useLayoutEffect(() => {
|
|
@@ -202,9 +225,14 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
202
225
|
const lineNumber = stripNonNumericAfterCountryCode(newPhoneNumber);
|
|
203
226
|
const outputValue = countryPrefix + lineNumber;
|
|
204
227
|
|
|
228
|
+
// Clear obfuscated state when user types manually
|
|
229
|
+
if (obfuscateValue) {
|
|
230
|
+
setObfuscateValue(false);
|
|
231
|
+
}
|
|
232
|
+
|
|
205
233
|
lastOutputValueRef.current = outputValue;
|
|
206
234
|
phoneNumber !== newPhoneNumber && setPhoneNumber(newPhoneNumber);
|
|
207
|
-
onChange && onChange(outputValue);
|
|
235
|
+
onChange && onChange(outputValue, false);
|
|
208
236
|
}
|
|
209
237
|
|
|
210
238
|
function togglePhoneBook(e: React.MouseEvent<HTMLButtonElement>) {
|
|
@@ -219,9 +247,15 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
219
247
|
setPhoneBookElement(null);
|
|
220
248
|
}
|
|
221
249
|
|
|
222
|
-
function handlePhoneBookOptionSelect(
|
|
250
|
+
function handlePhoneBookOptionSelect(
|
|
251
|
+
value: string,
|
|
252
|
+
_label: string | undefined,
|
|
253
|
+
_isSuggestion: boolean,
|
|
254
|
+
obfuscate: boolean
|
|
255
|
+
) {
|
|
223
256
|
// Update the phone number with the selected value
|
|
224
|
-
|
|
257
|
+
setObfuscateValue(obfuscate);
|
|
258
|
+
updatePhoneNumber(value, obfuscate);
|
|
225
259
|
closePhoneBook();
|
|
226
260
|
}
|
|
227
261
|
|
|
@@ -244,8 +278,22 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
244
278
|
return value;
|
|
245
279
|
}
|
|
246
280
|
|
|
281
|
+
function handleObfuscatedInputChange(newValue: string) {
|
|
282
|
+
// When user types on a obfuscated input, clear the obfuscated state and start fresh
|
|
283
|
+
// The newValue will be the digits the user typed (mask filters to valid input)
|
|
284
|
+
setShouldFocusAfterClear(true);
|
|
285
|
+
setObfuscateValue(false);
|
|
286
|
+
setPhoneNumber(newValue);
|
|
287
|
+
|
|
288
|
+
const countryPrefix = countryCodeMap.get(countryCode)?.prefix;
|
|
289
|
+
const lineNumber = stripNonNumericAfterCountryCode(newValue);
|
|
290
|
+
const outputValue = countryPrefix + lineNumber;
|
|
291
|
+
lastOutputValueRef.current = outputValue;
|
|
292
|
+
onChange && onChange(outputValue, false);
|
|
293
|
+
}
|
|
294
|
+
|
|
247
295
|
const updatePhoneNumber = useCallback(
|
|
248
|
-
(value: string) => {
|
|
296
|
+
(value: string, obfuscate = false) => {
|
|
249
297
|
const oldValue = lastOutputValueRef.current;
|
|
250
298
|
const countryInformation = getCountryCodeFromValue(
|
|
251
299
|
value,
|
|
@@ -259,7 +307,7 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
259
307
|
|
|
260
308
|
if (oldValue !== value) {
|
|
261
309
|
setPhoneNumber(phoneNumber);
|
|
262
|
-
onChange && onChange(value);
|
|
310
|
+
onChange && onChange(value, obfuscate);
|
|
263
311
|
}
|
|
264
312
|
},
|
|
265
313
|
[defaultCountry, selectedCountry, onChange]
|
|
@@ -269,6 +317,14 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
269
317
|
updatePhoneNumber(value);
|
|
270
318
|
}, [value, updatePhoneNumber]);
|
|
271
319
|
|
|
320
|
+
// Focus the input after transitioning from obfuscated to normal mode
|
|
321
|
+
useLayoutEffect(() => {
|
|
322
|
+
if (shouldFocusAfterClear && !obfuscateValue && internalInputRef.current) {
|
|
323
|
+
internalInputRef.current.focus();
|
|
324
|
+
setShouldFocusAfterClear(false);
|
|
325
|
+
}
|
|
326
|
+
}, [shouldFocusAfterClear, obfuscateValue]);
|
|
327
|
+
|
|
272
328
|
return (
|
|
273
329
|
<HStack
|
|
274
330
|
ref={ref}
|
|
@@ -280,25 +336,55 @@ export const PhoneNumberInput = React.forwardRef(function PhoneNumberInput(
|
|
|
280
336
|
className={clsx(styles['phone-number-input-select'], 'phone-number-input-select')}
|
|
281
337
|
ref={countryRef}
|
|
282
338
|
width="auto"
|
|
283
|
-
value={countryCode}
|
|
339
|
+
value={obfuscateValue ? '' : countryCode}
|
|
284
340
|
onChange={changeCountry}
|
|
285
|
-
disabled={disabled}
|
|
286
|
-
data-is-disabled={disabled}
|
|
341
|
+
disabled={disabled || obfuscateValue}
|
|
342
|
+
data-is-disabled={disabled || obfuscateValue}
|
|
343
|
+
data-is-obfuscated={obfuscateValue}
|
|
344
|
+
placeholder={obfuscateValue ? '––' : undefined}
|
|
287
345
|
>
|
|
288
346
|
{countryOptions}
|
|
289
347
|
</Select>
|
|
290
348
|
<HStack width="flex" className={clsx(styles['phone-number-input-container'])}>
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
349
|
+
{obfuscateValue ? (
|
|
350
|
+
<MaskInput
|
|
351
|
+
key="obfuscated"
|
|
352
|
+
name={name}
|
|
353
|
+
autoComplete={autoComplete}
|
|
354
|
+
ref={forkedInputRef}
|
|
355
|
+
value=""
|
|
356
|
+
mask={createObfuscatedMasks(currentMasks)}
|
|
357
|
+
onChange={handleObfuscatedInputChange}
|
|
358
|
+
disabled={disabled}
|
|
359
|
+
data-is-disabled={disabled}
|
|
360
|
+
data-has-phone-book={showPhoneBook}
|
|
361
|
+
data-is-obfuscated={true}
|
|
362
|
+
className={clsx(
|
|
363
|
+
styles['phone-number-input'],
|
|
364
|
+
styles['phone-number-input-obfuscated'],
|
|
365
|
+
'phone-number-input'
|
|
366
|
+
)}
|
|
367
|
+
preparePasteValue={() => ''}
|
|
368
|
+
prepareCopyValue={() => ''}
|
|
369
|
+
prepareCutValue={() => ''}
|
|
370
|
+
/>
|
|
371
|
+
) : (
|
|
372
|
+
<MaskInput
|
|
373
|
+
key="normal"
|
|
374
|
+
name={name}
|
|
375
|
+
autoComplete={autoComplete}
|
|
376
|
+
ref={forkedInputRef}
|
|
377
|
+
value={phoneNumber}
|
|
378
|
+
mask={currentMasks}
|
|
379
|
+
onChange={transformValue}
|
|
380
|
+
disabled={disabled}
|
|
381
|
+
data-is-disabled={disabled}
|
|
382
|
+
data-has-phone-book={showPhoneBook}
|
|
383
|
+
data-is-obfuscated={false}
|
|
384
|
+
className={clsx(styles['phone-number-input'], 'phone-number-input')}
|
|
385
|
+
preparePasteValue={preparePasteValue}
|
|
386
|
+
/>
|
|
387
|
+
)}
|
|
302
388
|
</HStack>
|
|
303
389
|
{showPhoneBook && (
|
|
304
390
|
<>
|
|
@@ -6,7 +6,12 @@ export interface SuggestionItemProps {
|
|
|
6
6
|
isFocused: boolean;
|
|
7
7
|
isSelected: boolean;
|
|
8
8
|
option: React.ReactElement;
|
|
9
|
-
onClick?: (
|
|
9
|
+
onClick?: (
|
|
10
|
+
value: string,
|
|
11
|
+
label: string,
|
|
12
|
+
isSuggestion: boolean,
|
|
13
|
+
obfuscate: boolean
|
|
14
|
+
) => void;
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
export function SuggestionItem({
|
|
@@ -43,7 +48,12 @@ export function SuggestionItem({
|
|
|
43
48
|
disabled={isDisabled}
|
|
44
49
|
onClick={() => {
|
|
45
50
|
if (!isDisabled && onClick) {
|
|
46
|
-
onClick(
|
|
51
|
+
onClick(
|
|
52
|
+
option.props.value,
|
|
53
|
+
option.props.label,
|
|
54
|
+
true,
|
|
55
|
+
option.props.obfuscate ?? false
|
|
56
|
+
);
|
|
47
57
|
}
|
|
48
58
|
}}
|
|
49
59
|
>
|
|
@@ -25,7 +25,8 @@ export interface SuggestionListProps
|
|
|
25
25
|
onOptionSelect?: (
|
|
26
26
|
value: string,
|
|
27
27
|
label: string | undefined,
|
|
28
|
-
isSuggestion: boolean
|
|
28
|
+
isSuggestion: boolean,
|
|
29
|
+
obfuscate: boolean
|
|
29
30
|
) => void;
|
|
30
31
|
noSuggestionMessage?: React.ReactNode;
|
|
31
32
|
trimCustomInput?: boolean;
|
|
@@ -112,7 +113,13 @@ export function SuggestionList({
|
|
|
112
113
|
const optionValue = option?.props.value || value;
|
|
113
114
|
|
|
114
115
|
requestAnimationFrame(() => {
|
|
115
|
-
onOptionSelect &&
|
|
116
|
+
onOptionSelect &&
|
|
117
|
+
onOptionSelect(
|
|
118
|
+
optionValue,
|
|
119
|
+
label,
|
|
120
|
+
isSuggestion,
|
|
121
|
+
option?.props.obfuscate ?? false
|
|
122
|
+
);
|
|
116
123
|
});
|
|
117
124
|
|
|
118
125
|
break;
|
|
@@ -126,7 +133,13 @@ export function SuggestionList({
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
requestAnimationFrame(() => {
|
|
129
|
-
onOptionSelect &&
|
|
136
|
+
onOptionSelect &&
|
|
137
|
+
onOptionSelect(
|
|
138
|
+
optionProps.value,
|
|
139
|
+
optionProps.label,
|
|
140
|
+
true,
|
|
141
|
+
optionProps.obfuscate ?? false
|
|
142
|
+
);
|
|
130
143
|
});
|
|
131
144
|
break;
|
|
132
145
|
}
|
|
@@ -260,6 +273,12 @@ export function SuggestionList({
|
|
|
260
273
|
const keywords = props.keywords?.map(k => k.toLocaleLowerCase()) || [];
|
|
261
274
|
const optionValue = String(props.value).toLocaleLowerCase();
|
|
262
275
|
const searchValue = value.toLocaleLowerCase();
|
|
276
|
+
const obfuscate = props.obfuscate ?? false;
|
|
277
|
+
|
|
278
|
+
// Obfuscated options can only be searched by label or keywords, not by value
|
|
279
|
+
if (obfuscate) {
|
|
280
|
+
return label.includes(searchValue) || keywords.some(k => k.includes(searchValue));
|
|
281
|
+
}
|
|
263
282
|
|
|
264
283
|
return (
|
|
265
284
|
label.includes(searchValue) ||
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { TextareaHTMLAttributes } from 'react';
|
|
4
4
|
import styles from './textarea.module.css';
|
|
5
5
|
|
|
6
6
|
export interface TextareaProps
|
|
7
|
-
extends Omit<
|
|
7
|
+
extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
|
|
8
8
|
value?: string;
|
|
9
9
|
width?: string;
|
|
10
10
|
height?: string;
|
|
@@ -18,7 +18,7 @@ export const Footer = React.forwardRef<HTMLElement, FooterProps>(function Footer
|
|
|
18
18
|
<HStack
|
|
19
19
|
ref={ref}
|
|
20
20
|
as="footer"
|
|
21
|
-
className={clsx(styles.footer, className, '
|
|
21
|
+
className={clsx(styles.footer, className, 'tcn-footer')}
|
|
22
22
|
data-hierarchy={hierarchy}
|
|
23
23
|
data-size={size}
|
|
24
24
|
{...props}
|
|
@@ -4,7 +4,6 @@ import { HStack, type HStackProps } from '../../stacks/h_stack.js';
|
|
|
4
4
|
import type { Hierarchy, Size } from '../../utils/index.js';
|
|
5
5
|
import styles from './header.module.css';
|
|
6
6
|
|
|
7
|
-
// UtilityBar
|
|
8
7
|
export interface HeaderProps extends Omit<HStackProps, 'as'> {
|
|
9
8
|
hierarchy?: Hierarchy;
|
|
10
9
|
size?: Size;
|
package/src/layouts/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export * from './body/h_body.js';
|
|
2
|
+
export * from './body/v_body.js';
|
|
1
3
|
export * from './column/column.js';
|
|
2
4
|
export * from './divider/divider.js';
|
|
3
5
|
export * from './footer/footer.js';
|
|
@@ -8,5 +10,4 @@ export * from './list/list.js';
|
|
|
8
10
|
export * from './list/section_header.js';
|
|
9
11
|
export * from './sidebar_end/sidebar_end.js';
|
|
10
12
|
export * from './sidebar_start/sidebar_start.js';
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './body/v_body.js';
|
|
13
|
+
export * from './utility_bar/utility_bar.js';
|
|
@@ -4,14 +4,22 @@ import { clsx } from 'clsx';
|
|
|
4
4
|
|
|
5
5
|
export interface ItemProps extends Omit<HStackProps, 'as' | 'children'> {
|
|
6
6
|
children?: React.ReactNode;
|
|
7
|
+
selected?: boolean;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export const Item = React.forwardRef<HTMLDivElement, ItemProps>(function Item(
|
|
10
|
-
{ children, className, ...props }: ItemProps,
|
|
11
|
+
{ children, className, selected = false, ...props }: ItemProps,
|
|
11
12
|
ref
|
|
12
13
|
) {
|
|
13
14
|
return (
|
|
14
|
-
<HStack
|
|
15
|
+
<HStack
|
|
16
|
+
ref={ref}
|
|
17
|
+
as="li"
|
|
18
|
+
data-hierarchy={selected ? 'primary' : 'tertiary'}
|
|
19
|
+
data-is-selected={selected}
|
|
20
|
+
className={clsx('tcn-item', 'tcn-interactive', className)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
15
23
|
{children}
|
|
16
24
|
</HStack>
|
|
17
25
|
);
|
|
@@ -6,7 +6,7 @@ export interface ListProps extends Omit<VStackProps, 'as'> {
|
|
|
6
6
|
isOrdered?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export const List = React.forwardRef<HTMLDivElement, ListProps>(function
|
|
9
|
+
export const List = React.forwardRef<HTMLDivElement, ListProps>(function List(
|
|
10
10
|
{ children, className, isOrdered = false, ...props }: ListProps,
|
|
11
11
|
ref
|
|
12
12
|
) {
|
|
@@ -14,7 +14,7 @@ export const List = React.forwardRef<HTMLDivElement, ListProps>(function Item(
|
|
|
14
14
|
<VStack
|
|
15
15
|
ref={ref}
|
|
16
16
|
as={isOrdered ? 'ol' : 'ul'}
|
|
17
|
-
className={clsx('list', className)}
|
|
17
|
+
className={clsx('tcn-list', className)}
|
|
18
18
|
{...props}
|
|
19
19
|
>
|
|
20
20
|
{children}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ZStack } from '../../stacks/z_stack.js';
|
|
2
|
+
import { Frame, type FrameOwnProps } from './frame.js';
|
|
3
|
+
import { DragHandle } from '../../utils/dnd/handle.js';
|
|
4
|
+
import { Title } from '../../typography/title/title.js';
|
|
5
|
+
import { BodyText } from '../../typography/index.js';
|
|
6
|
+
import { Header } from '../../layouts/index.js';
|
|
7
|
+
import styles from './frame_stories.module.css';
|
|
8
|
+
export default {
|
|
9
|
+
title: 'Overlay/Floating/Frame',
|
|
10
|
+
component: Frame,
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
|
|
13
|
+
args: {
|
|
14
|
+
isOpen: true,
|
|
15
|
+
draggable: true,
|
|
16
|
+
veil: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const FrameStory = (args: Omit<FrameOwnProps, 'children'>) => {
|
|
21
|
+
return (
|
|
22
|
+
<ZStack height="100%" width="100%" minHeight="600px">
|
|
23
|
+
<Frame
|
|
24
|
+
width="300px"
|
|
25
|
+
height="300px"
|
|
26
|
+
className={styles['sb-frame-container']}
|
|
27
|
+
{...args}
|
|
28
|
+
>
|
|
29
|
+
<Header className={styles['sb-frame-header']}>
|
|
30
|
+
<Title> This is a frame</Title>
|
|
31
|
+
</Header>
|
|
32
|
+
<DragHandle>
|
|
33
|
+
<Header className={styles['sb-frame-header']}>
|
|
34
|
+
<BodyText> You can drag here.</BodyText>
|
|
35
|
+
</Header>
|
|
36
|
+
</DragHandle>
|
|
37
|
+
</Frame>
|
|
38
|
+
</ZStack>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ZStack, type ZStackProps } from '../../stacks/index.js';
|
|
3
|
+
import { Portal } from '../portal/portal.js';
|
|
4
|
+
import { Draggable } from '../../utils/dnd/draggable/draggable.js';
|
|
5
|
+
|
|
6
|
+
export interface FrameOwnProps {
|
|
7
|
+
isOpen?: boolean;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
draggable?: boolean;
|
|
10
|
+
veil?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type FrameProps = ZStackProps & FrameOwnProps;
|
|
14
|
+
|
|
15
|
+
export const Frame = React.forwardRef<HTMLDialogElement, FrameProps>(function Frame(
|
|
16
|
+
{ children, isOpen = false, draggable = true, veil = false, ...rest }: FrameProps,
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
if (!isOpen) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Portal>
|
|
25
|
+
<ZStack width="100%" height="100%" data-is-veil={veil} className="tcn-frame">
|
|
26
|
+
<Draggable draggable={draggable}>
|
|
27
|
+
<ZStack ref={ref} {...rest}>
|
|
28
|
+
{children}
|
|
29
|
+
</ZStack>
|
|
30
|
+
</Draggable>
|
|
31
|
+
</ZStack>
|
|
32
|
+
</Portal>
|
|
33
|
+
);
|
|
34
|
+
});
|