@beydesign/storybook 0.0.57 → 0.0.58

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.
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { AutoCompleteProps } from './types';
3
+ export declare const AutoComplete: React.FC<AutoCompleteProps>;
@@ -0,0 +1,164 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import { Box } from '@mui/material';
4
+ import { Typography, TypographySize, TypographyWeight } from '../Typography';
5
+ import { getIconComponent } from '../../utils';
6
+ import { getTypographySizeToken, getTypographyWeightToken, getTextStyles, getTextTransform, } from '../../utils';
7
+ export const AutoComplete = ({ options, value, onChange, leadingIcon = 'MagnifyingGlass', placeholder = 'Search', disabled = false, onBlur, onFocus, }) => {
8
+ const [searchText, setSearchText] = useState('');
9
+ const [focused, setFocused] = useState(false);
10
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
11
+ const inputRef = useRef(null);
12
+ const optionsContainerRef = useRef(null);
13
+ useEffect(() => {
14
+ setSearchText(value || '');
15
+ }, [value]);
16
+ const handleInputChange = (e) => {
17
+ const newValue = e.target.value;
18
+ setSearchText(newValue);
19
+ setHighlightedIndex(-1);
20
+ if (value !== newValue) {
21
+ onChange(newValue);
22
+ }
23
+ };
24
+ const handleOptionSelect = (option) => {
25
+ if (option !== value) {
26
+ onChange(option);
27
+ }
28
+ setSearchText(option);
29
+ setFocused(false);
30
+ inputRef.current?.blur();
31
+ };
32
+ const handleKeyDown = (e) => {
33
+ if (!focused)
34
+ return;
35
+ const hasOptions = filteredOptions.length > 0;
36
+ switch (e.key) {
37
+ case 'ArrowDown':
38
+ e.preventDefault();
39
+ if (hasOptions) {
40
+ setHighlightedIndex((prev) => prev < filteredOptions.length - 1 ? prev + 1 : 0);
41
+ }
42
+ break;
43
+ case 'ArrowUp':
44
+ e.preventDefault();
45
+ if (hasOptions) {
46
+ setHighlightedIndex((prev) => prev > 0 ? prev - 1 : filteredOptions.length - 1);
47
+ }
48
+ break;
49
+ case 'Enter':
50
+ e.preventDefault();
51
+ if (hasOptions && highlightedIndex >= 0) {
52
+ handleOptionSelect(filteredOptions[highlightedIndex]);
53
+ }
54
+ break;
55
+ case 'Escape':
56
+ e.preventDefault();
57
+ setFocused(false);
58
+ inputRef.current?.blur();
59
+ break;
60
+ case 'Tab':
61
+ setFocused(false);
62
+ break;
63
+ default:
64
+ break;
65
+ }
66
+ };
67
+ useEffect(() => {
68
+ const handleClickOutside = (event) => {
69
+ if (optionsContainerRef.current &&
70
+ !optionsContainerRef.current.contains(event.target) &&
71
+ inputRef.current &&
72
+ !inputRef.current.contains(event.target)) {
73
+ setFocused(false);
74
+ }
75
+ };
76
+ if (focused) {
77
+ document.addEventListener('mousedown', handleClickOutside);
78
+ }
79
+ return () => {
80
+ document.removeEventListener('mousedown', handleClickOutside);
81
+ };
82
+ }, [focused]);
83
+ useEffect(() => {
84
+ if (highlightedIndex >= 0 && optionsContainerRef.current) {
85
+ const highlightedElement = optionsContainerRef.current.children[highlightedIndex];
86
+ if (highlightedElement) {
87
+ highlightedElement.scrollIntoView({ block: 'nearest' });
88
+ }
89
+ }
90
+ }, [highlightedIndex]);
91
+ const handleClear = (event) => {
92
+ event.stopPropagation();
93
+ if (!disabled) {
94
+ onChange('');
95
+ setSearchText('');
96
+ inputRef.current?.focus();
97
+ }
98
+ };
99
+ const handleFocus = () => {
100
+ if (!disabled) {
101
+ setFocused(true);
102
+ if (onFocus)
103
+ onFocus();
104
+ }
105
+ };
106
+ const handleBlur = () => {
107
+ setTimeout(() => {
108
+ setFocused(false);
109
+ if (onBlur)
110
+ onBlur();
111
+ if (searchText !== value) {
112
+ setSearchText(value || '');
113
+ }
114
+ }, 150);
115
+ };
116
+ const filteredOptions = React.useMemo(() => {
117
+ return options.filter((option) => option.toLowerCase().includes((searchText || '').toLowerCase()));
118
+ }, [options, searchText]);
119
+ const sizeToken = getTypographySizeToken(TypographySize.TextS);
120
+ const weightToken = getTypographyWeightToken(TypographyWeight.Regular);
121
+ const { fontSize, fontWeight, fontFamily, lineHeight, letterSpacing, paragraphIndent, paragraphSpacing, textDecoration, } = getTextStyles(sizeToken, weightToken);
122
+ const textTransform = getTextTransform(sizeToken, weightToken);
123
+ return (_jsxs(Box, { display: 'flex', flexDirection: 'column', position: 'relative', width: "100%", children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', alignItems: 'center', justifyContent: 'center', border: `1px solid ${focused ? 'var(--colors-light-border-border-component-active)' : 'var(--colors-light-border-border-component)'}`, borderRadius: '4px', bgcolor: disabled
124
+ ? 'var(--primitives-desktop-primary-gray-25)'
125
+ : 'var(--primitives-desktop-primary-white-100)', children: [getIconComponent(leadingIcon, {
126
+ width: '24px',
127
+ height: '24px',
128
+ color: 'var(--colors-light-foreground-fg-quaternary)',
129
+ 'aria-hidden': 'true',
130
+ }), _jsx("input", { ref: inputRef, type: "text", value: searchText, onChange: handleInputChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, style: {
131
+ flexGrow: 1,
132
+ border: 'none',
133
+ outline: 'none',
134
+ fontSize: fontSize,
135
+ fontWeight: fontWeight,
136
+ fontFamily: fontFamily + ', sans-serif',
137
+ lineHeight: lineHeight + 'px',
138
+ letterSpacing: letterSpacing,
139
+ textIndent: paragraphIndent,
140
+ marginBottom: paragraphSpacing,
141
+ textTransform: textTransform,
142
+ textDecoration: textDecoration,
143
+ color: disabled
144
+ ? 'var(--colors-light-text-text-disabled)'
145
+ : 'var(--colors-light-text-text-title)',
146
+ backgroundColor: 'transparent',
147
+ }, onFocus: handleFocus, onBlur: handleBlur, "aria-autocomplete": "list", "aria-controls": focused ? 'autocomplete-options' : undefined, "aria-expanded": focused, role: "combobox" }), searchText &&
148
+ getIconComponent('X', {
149
+ width: '24px',
150
+ height: '24px',
151
+ color: 'var(--colors-light-foreground-fg-quaternary)',
152
+ onClick: handleClear,
153
+ style: { cursor: disabled ? 'default' : 'pointer' },
154
+ 'aria-label': 'Clear input',
155
+ })] }), focused && (_jsx(Box, { id: "autocomplete-options", ref: optionsContainerRef, position: 'absolute', top: '100%', left: 0, zIndex: 1000, bgcolor: 'var(--primitives-desktop-primary-white-100)', borderRadius: '4px', width: '100%', maxHeight: '200px', overflow: 'auto', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.1)', mt: '4px', role: "listbox", children: filteredOptions.length === 0 ? (_jsx(Box, { display: 'flex', flexDirection: 'row', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', children: _jsx(Typography, { text: "No results found", size: TypographySize.TextS, weight: TypographyWeight.Regular }) })) : (filteredOptions.map((option, index) => (_jsx(Box, { display: 'flex', flexDirection: 'row', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', onClick: () => handleOptionSelect(option), sx: {
156
+ cursor: 'pointer',
157
+ backgroundColor: highlightedIndex === index
158
+ ? 'var(--primitives-desktop-primary-gray-25)'
159
+ : 'transparent',
160
+ '&:hover': {
161
+ backgroundColor: 'var(--primitives-desktop-primary-gray-25)',
162
+ },
163
+ }, role: "option", "aria-selected": highlightedIndex === index, tabIndex: -1, children: _jsx(Typography, { text: option, size: TypographySize.TextS, weight: TypographyWeight.Regular }) }, option)))) }))] }));
164
+ };
@@ -0,0 +1,6 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { AutoComplete } from './AutoComplete';
3
+ declare const meta: Meta<typeof AutoComplete>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof AutoComplete>;
6
+ export declare const Default: Story;
@@ -0,0 +1,88 @@
1
+ import { AutoComplete } from './AutoComplete';
2
+ const meta = {
3
+ title: 'Design System/Components/Form/AutoComplete',
4
+ component: AutoComplete,
5
+ parameters: {
6
+ layout: 'centered',
7
+ design: {
8
+ type: 'figspec',
9
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=4501-25356&t=XsmO3ZzXQ9rb1lu5-1',
10
+ },
11
+ },
12
+ tags: ['autodocs'],
13
+ argTypes: {
14
+ options: {
15
+ control: 'select',
16
+ description: 'The options to display in the autocomplete',
17
+ table: {
18
+ type: { summary: 'string[]' },
19
+ },
20
+ },
21
+ value: {
22
+ control: 'text',
23
+ description: 'The value of the selected option',
24
+ table: {
25
+ type: { summary: 'string' },
26
+ },
27
+ },
28
+ onChange: {
29
+ description: 'The onChange handler for the selected option',
30
+ table: {
31
+ type: { summary: 'function' },
32
+ },
33
+ },
34
+ disabled: {
35
+ control: 'boolean',
36
+ description: 'Whether the autocomplete is disabled',
37
+ table: {
38
+ type: { summary: 'boolean' },
39
+ },
40
+ },
41
+ leadingIcon: {
42
+ control: 'select',
43
+ description: 'The leading icon of the autocomplete',
44
+ options: ['MagnifyingGlass', 'User', 'Calendar', 'Bell'],
45
+ table: {
46
+ type: { summary: 'string' },
47
+ },
48
+ },
49
+ placeholder: {
50
+ control: 'text',
51
+ description: 'The placeholder of the autocomplete',
52
+ table: {
53
+ type: { summary: 'string' },
54
+ },
55
+ },
56
+ onBlur: {
57
+ description: 'The onBlur handler for the autocomplete',
58
+ table: {
59
+ type: { summary: 'function' },
60
+ },
61
+ },
62
+ onFocus: {
63
+ description: 'The onFocus handler for the autocomplete',
64
+ table: {
65
+ type: { summary: 'function' },
66
+ },
67
+ },
68
+ },
69
+ };
70
+ export default meta;
71
+ export const Default = {
72
+ args: {
73
+ options: [
74
+ 'Option 1',
75
+ 'Option 2',
76
+ 'Option 3',
77
+ 'Option 4',
78
+ 'Option 5',
79
+ 'Option 6',
80
+ 'Option 7',
81
+ 'Option 8',
82
+ 'Option 9',
83
+ 'Option 10',
84
+ ],
85
+ value: '',
86
+ onChange: () => { },
87
+ },
88
+ };
@@ -1,5 +1,6 @@
1
1
  import { SxProps, Theme } from '@mui/material';
2
2
  import { HintBubbleProps } from '../UI';
3
+ import { IconName } from '../../utils';
3
4
  /**
4
5
  * Text area component props
5
6
  */
@@ -140,7 +141,7 @@ export declare enum FileType {
140
141
  }
141
142
  export interface UploadedFileProps {
142
143
  /** Type of the uploaded file */
143
- type: FileType;
144
+ type?: FileType;
144
145
  /** File name */
145
146
  fileName: string;
146
147
  /** Whether to show the delete button */
@@ -183,3 +184,21 @@ export interface FileUploadingProps {
183
184
  /** File name */
184
185
  fileName: string;
185
186
  }
187
+ export interface AutoCompleteProps {
188
+ /** The options to display in the autocomplete */
189
+ options: string[];
190
+ /** The value of the selected option */
191
+ value: string;
192
+ /** The onChange handler for the selected option */
193
+ onChange: (value: string) => void;
194
+ /** The leading icon of the autocomplete */
195
+ leadingIcon?: IconName;
196
+ /** The placeholder of the autocomplete */
197
+ placeholder?: string;
198
+ /** Whether the autocomplete is disabled */
199
+ disabled?: boolean;
200
+ /** The onBlur handler for the autocomplete */
201
+ onBlur?: () => void;
202
+ /** The onFocus handler for the autocomplete */
203
+ onFocus?: () => void;
204
+ }
@@ -1,42 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { TypographySize, TypographyWeight } from './types';
3
3
  import { Box } from '@mui/material';
4
+ import { getTextStyles, getTextTransform, getTypographySizeToken, getTypographyWeightToken, } from '../../utils';
4
5
  export const Typography = ({ text, color, size = TypographySize.HeadingL, weight = TypographyWeight.Bold, className, required = false, style, trailingElement = null, textStyle, }) => {
5
- const getTypographySizeToken = () => {
6
- let sizeLc = size?.toLowerCase() || '';
7
- if (sizeLc.includes('heading')) {
8
- sizeLc = sizeLc.replace('heading', 'heading-');
9
- }
10
- else if (sizeLc.includes('text')) {
11
- sizeLc = sizeLc.replace('text', 'text-');
12
- }
13
- sizeLc = sizeLc.replace(/(\d)(\D)/g, '$1-$2');
14
- return sizeLc;
15
- };
16
- const getTypographyWeightToken = () => {
17
- let weightLc = weight?.toLowerCase() || '';
18
- if (weightLc.includes('semibold')) {
19
- weightLc = weightLc.replace('semibold', 'semi-bold');
20
- }
21
- return weightLc;
22
- };
23
- const sizeToken = getTypographySizeToken();
24
- const weightToken = getTypographyWeightToken();
25
- const fontSize = `var(--global-${sizeToken}-${weightToken}-font-size)`;
26
- const fontWeight = `var(--global-${sizeToken}-${weightToken}-font-weight)`;
27
- const fontFamily = `var(--global-${sizeToken}-${weightToken}-font-family)`;
28
- const lineHeight = `var(--global-${sizeToken}-${weightToken}-line-height)`;
29
- const letterSpacing = `var(--global-${sizeToken}-${weightToken}-letter-spacing)`;
30
- const paragraphIndent = `var(--global-${sizeToken}-${weightToken}-paragraph-indent)`;
31
- const paragraphSpacing = `var(--global-${sizeToken}-${weightToken}-paragraph-spacing)`;
32
- const textDecoration = `var(--global-${sizeToken}-${weightToken}-text-decoration)`;
6
+ const sizeToken = getTypographySizeToken(size);
7
+ const weightToken = getTypographyWeightToken(weight);
8
+ const { fontSize, fontWeight, fontFamily, lineHeight, letterSpacing, paragraphIndent, paragraphSpacing, textDecoration, } = getTextStyles(sizeToken, weightToken);
9
+ const textTransform = getTextTransform(sizeToken, weightToken);
33
10
  const requiredColor = 'var(--colors-light-text-text-error)';
34
- const getTextTransform = () => {
35
- const computedValue = getComputedStyle(document.documentElement)
36
- .getPropertyValue(`--global-${sizeToken}-${weightToken}-text-case`)
37
- .trim();
38
- return (computedValue === 'uppercase' ? 'uppercase' : 'none');
39
- };
40
11
  const Label = () => {
41
12
  return (_jsxs("span", { className: className, style: {
42
13
  display: 'flex',
@@ -49,7 +20,7 @@ export const Typography = ({ text, color, size = TypographySize.HeadingL, weight
49
20
  letterSpacing: letterSpacing,
50
21
  textIndent: paragraphIndent,
51
22
  marginBottom: paragraphSpacing,
52
- textTransform: getTextTransform(),
23
+ textTransform: textTransform,
53
24
  textDecoration: textDecoration,
54
25
  color: color || 'var(--global-text-text-title)',
55
26
  ...textStyle,
@@ -1,2 +1,3 @@
1
1
  export * from './icon';
2
2
  export * from './common';
3
+ export * from './typography';
@@ -1,2 +1,3 @@
1
1
  export * from './icon';
2
2
  export * from './common';
3
+ export * from './typography';
@@ -0,0 +1,14 @@
1
+ import { TypographySize, TypographyWeight } from '../stories/Typography';
2
+ export declare const getTypographySizeToken: (size: TypographySize) => string;
3
+ export declare const getTypographyWeightToken: (weight: TypographyWeight) => string;
4
+ export declare const getTextStyles: (sizeToken: string, weightToken: string) => {
5
+ fontSize: string;
6
+ fontWeight: string;
7
+ fontFamily: string;
8
+ lineHeight: string;
9
+ letterSpacing: string;
10
+ paragraphIndent: string;
11
+ paragraphSpacing: string;
12
+ textDecoration: string;
13
+ };
14
+ export declare const getTextTransform: (sizeToken: string, weightToken: string) => "uppercase" | "none";
@@ -0,0 +1,44 @@
1
+ export const getTypographySizeToken = (size) => {
2
+ let sizeLc = size?.toLowerCase() || '';
3
+ if (sizeLc.includes('heading')) {
4
+ sizeLc = sizeLc.replace('heading', 'heading-');
5
+ }
6
+ else if (sizeLc.includes('text')) {
7
+ sizeLc = sizeLc.replace('text', 'text-');
8
+ }
9
+ sizeLc = sizeLc.replace(/(\d)(\D)/g, '$1-$2');
10
+ return sizeLc;
11
+ };
12
+ export const getTypographyWeightToken = (weight) => {
13
+ let weightLc = weight?.toLowerCase() || '';
14
+ if (weightLc.includes('semibold')) {
15
+ weightLc = weightLc.replace('semibold', 'semi-bold');
16
+ }
17
+ return weightLc;
18
+ };
19
+ export const getTextStyles = (sizeToken, weightToken) => {
20
+ const fontSize = `var(--global-${sizeToken}-${weightToken}-font-size)`;
21
+ const fontWeight = `var(--global-${sizeToken}-${weightToken}-font-weight)`;
22
+ const fontFamily = `var(--global-${sizeToken}-${weightToken}-font-family)`;
23
+ const lineHeight = `var(--global-${sizeToken}-${weightToken}-line-height)`;
24
+ const letterSpacing = `var(--global-${sizeToken}-${weightToken}-letter-spacing)`;
25
+ const paragraphIndent = `var(--global-${sizeToken}-${weightToken}-paragraph-indent)`;
26
+ const paragraphSpacing = `var(--global-${sizeToken}-${weightToken}-paragraph-spacing)`;
27
+ const textDecoration = `var(--global-${sizeToken}-${weightToken}-text-decoration)`;
28
+ return {
29
+ fontSize,
30
+ fontWeight,
31
+ fontFamily,
32
+ lineHeight,
33
+ letterSpacing,
34
+ paragraphIndent,
35
+ paragraphSpacing,
36
+ textDecoration,
37
+ };
38
+ };
39
+ export const getTextTransform = (sizeToken, weightToken) => {
40
+ const computedValue = getComputedStyle(document.documentElement)
41
+ .getPropertyValue(`--global-${sizeToken}-${weightToken}-text-case`)
42
+ .trim();
43
+ return computedValue === 'uppercase' ? 'uppercase' : 'none';
44
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beydesign/storybook",
3
3
  "private": false,
4
- "version": "0.0.57",
4
+ "version": "0.0.58",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",