@basic-ui/material 1.0.0-alpha.43 → 1.0.0-alpha.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basic-ui/material",
3
- "version": "1.0.0-alpha.43",
3
+ "version": "1.0.0-alpha.44",
4
4
  "description": "Accessible React Components used as building blocks for UI patterns",
5
5
  "author": "Lucas Terra <lucasterra7@gmail.com>",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "test:watch": "yarn test --watch"
31
31
  },
32
32
  "dependencies": {
33
- "@basic-ui/core": "^0.0.51",
33
+ "@basic-ui/core": "^0.0.52",
34
34
  "@basic-ui/dynamic-theme": "^0.0.11",
35
35
  "@styled-system/should-forward-prop": "5.1.5",
36
36
  "@types/styled-system": "^5.1.10",
@@ -62,5 +62,5 @@
62
62
  "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0",
63
63
  "tailwindcss": "^3.0.0"
64
64
  },
65
- "gitHead": "3fed4eb9238027ed52b7ac89538023810aac260d"
65
+ "gitHead": "fc731d3f336d3dce8bcf3b6589d724883bc66c7a"
66
66
  }
@@ -1,82 +1,82 @@
1
- import { assignMultipleRefs } from '@basic-ui/core';
2
- import { rem } from 'polished';
3
- import type { ReactElement } from 'react';
4
- import { cloneElement, forwardRef, useRef } from 'react';
5
- import {
6
- DARK_SCHEME_CLASS,
7
- LIGHT_SCHEME_CLASS,
8
- useColorScheme,
9
- } from '@basic-ui/dynamic-theme';
10
-
11
- import { Box } from '../Box';
12
- import type { PaperProps } from '../Paper';
13
- import { Paper } from '../Paper';
14
- import { Text } from '../Text';
15
- import { useSnackbarAnimation } from './useSnackbarAnimation';
16
-
17
- export interface SnackbarProps extends PaperProps {
18
- action?: ReactElement;
19
- timeout?: number;
20
- dismissible?: boolean;
21
- }
22
-
23
- export const Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(
24
- function Snackbar(props, forwardedRef) {
25
- const { colorScheme } = useColorScheme();
26
- const ref = useRef<HTMLDivElement | null>(null);
27
- const {
28
- __css,
29
- action,
30
- children,
31
- style: styleProp,
32
- timeout = 5000,
33
- dismissible,
34
- ...otherProps
35
- } = props;
36
-
37
- const { onClose, placementStyle, style } = useSnackbarAnimation(
38
- ref,
39
- timeout
40
- );
41
-
42
- return (
43
- <Paper
44
- elevation={4}
45
- darkThemeBackgroundOverlay={4}
46
- className={
47
- colorScheme === 'light' ? DARK_SCHEME_CLASS : LIGHT_SCHEME_CLASS
48
- }
49
- ref={assignMultipleRefs(forwardedRef, ref)}
50
- __css={{
51
- boxShadow: 4,
52
- py: rem(6),
53
- pl: 3,
54
- pr: 3,
55
- display: 'inline-flex',
56
- minWidth: `min(100%, ${rem(344)})`,
57
- maxWidth: ['100%', 'unset'],
58
- borderRadius: 'extra-small',
59
- color: 'on.surface',
60
- flexDirection: 'row',
61
- willChange: 'transform,opacity',
62
- ...placementStyle,
63
- ...__css,
64
- }}
65
- style={{
66
- ...style,
67
- ...styleProp,
68
- }}
69
- {...otherProps}
70
- >
71
- <Text variant="body-medium" as="span" py={2}>
72
- {children}
73
- </Text>
74
- {action && (
75
- <Box as="span" pl={2} mr={-2} ml="auto" minWidth="auto">
76
- {dismissible ? cloneElement(action, { onClick: onClose }) : action}
77
- </Box>
78
- )}
79
- </Paper>
80
- );
81
- }
82
- );
1
+ import { assignMultipleRefs } from '@basic-ui/core';
2
+ import { rem } from 'polished';
3
+ import type { ReactElement } from 'react';
4
+ import { cloneElement, forwardRef, useRef } from 'react';
5
+ import {
6
+ DARK_SCHEME_CLASS,
7
+ LIGHT_SCHEME_CLASS,
8
+ useColorScheme,
9
+ } from '@basic-ui/dynamic-theme';
10
+
11
+ import { Box } from '../Box';
12
+ import type { PaperProps } from '../Paper';
13
+ import { Paper } from '../Paper';
14
+ import { Text } from '../Text';
15
+ import { useSnackbarAnimation } from './useSnackbarAnimation';
16
+
17
+ export interface SnackbarProps extends PaperProps {
18
+ action?: ReactElement;
19
+ timeout?: number;
20
+ dismissible?: boolean;
21
+ }
22
+
23
+ export const Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(
24
+ function Snackbar(props, forwardedRef) {
25
+ const { colorScheme } = useColorScheme();
26
+ const ref = useRef<HTMLDivElement | null>(null);
27
+ const {
28
+ __css,
29
+ action,
30
+ children,
31
+ style: styleProp,
32
+ timeout = 5000,
33
+ dismissible,
34
+ ...otherProps
35
+ } = props;
36
+
37
+ const { onClose, placementStyle, style } = useSnackbarAnimation(
38
+ ref,
39
+ timeout
40
+ );
41
+
42
+ return (
43
+ <Paper
44
+ elevation={4}
45
+ darkThemeBackgroundOverlay={4}
46
+ className={
47
+ colorScheme === 'light' ? DARK_SCHEME_CLASS : LIGHT_SCHEME_CLASS
48
+ }
49
+ ref={assignMultipleRefs(forwardedRef, ref)}
50
+ __css={{
51
+ boxShadow: 4,
52
+ py: rem(6),
53
+ pl: 3,
54
+ pr: 3,
55
+ display: 'inline-flex',
56
+ minWidth: `min(100%, ${rem(344)})`,
57
+ maxWidth: ['100%', 'unset'],
58
+ borderRadius: 'extra-small',
59
+ color: 'on.surface',
60
+ flexDirection: 'row',
61
+ willChange: 'transform,opacity',
62
+ ...placementStyle,
63
+ ...__css,
64
+ }}
65
+ style={{
66
+ ...style,
67
+ ...styleProp,
68
+ }}
69
+ {...otherProps}
70
+ >
71
+ <Text variant="body-medium" as="span" py={2}>
72
+ {children}
73
+ </Text>
74
+ {action && (
75
+ <Box as="span" pl={2} mr={-2} ml="auto" minWidth="auto">
76
+ {dismissible ? cloneElement(action, { onClick: onClose }) : action}
77
+ </Box>
78
+ )}
79
+ </Paper>
80
+ );
81
+ }
82
+ );
@@ -1,239 +1,239 @@
1
- import {
2
- useMemo,
3
- useLayoutEffect,
4
- createContext,
5
- useContext,
6
- useEffect,
7
- useRef,
8
- useState,
9
- } from 'react';
10
- import type { MutableRefObject, ReactElement, FC, ReactNode } from 'react';
11
-
12
- export type StackPlacement =
13
- | 'top-right'
14
- | 'top-center'
15
- | 'top-left'
16
- | 'bottom-right'
17
- | 'bottom-center'
18
- | 'bottom-left';
19
-
20
- // StackItemContext Component
21
- export interface StackItemContextProps {
22
- posY: number | undefined;
23
- isExiting: boolean;
24
- onSetHeight(height: number): void;
25
- onRemoveItem(): void;
26
- placement: StackPlacement;
27
- }
28
-
29
- const StackItemContext = createContext<StackItemContextProps | null>(null);
30
- StackItemContext.displayName = 'StackItemContext';
31
-
32
- interface StackItemProps {
33
- ref: MutableRefObject<HTMLElement | null>;
34
- openTimeoutInMilliseconds?: number;
35
- closeTimeoutInMilliseconds?: number;
36
- }
37
-
38
- export function useStackItem({
39
- ref,
40
- closeTimeoutInMilliseconds = 110,
41
- }: StackItemProps) {
42
- const context = useContext(StackItemContext);
43
- if (!context) {
44
- return null;
45
- }
46
- const { onSetHeight, onRemoveItem, posY, placement, isExiting } = context;
47
- /* eslint-disable react-hooks/rules-of-hooks */
48
- const [state, setState] = useState<'entering' | 'entered' | 'exiting'>(
49
- 'entering'
50
- );
51
-
52
- function onClose() {
53
- setState('exiting');
54
- }
55
-
56
- useLayoutEffect(() => {
57
- const height = ref.current?.getBoundingClientRect().height ?? 0;
58
- onSetHeight(height);
59
-
60
- if (isExiting) {
61
- onClose();
62
- }
63
- });
64
-
65
- useEffect(() => {
66
- if (posY !== undefined && state === 'entering') {
67
- setState('entered');
68
- }
69
- }, [state, posY]);
70
-
71
- useLayoutEffect(() => {
72
- if (state === 'exiting') {
73
- const handle = setTimeout(() => {
74
- onRemoveItem();
75
- }, closeTimeoutInMilliseconds);
76
-
77
- return () => {
78
- clearTimeout(handle);
79
- };
80
- }
81
- }, [state]); // eslint-disable-line react-hooks/exhaustive-deps
82
-
83
- return {
84
- translateY: posY,
85
- state,
86
- onClose,
87
- placement,
88
- };
89
- /* eslint-enable react-hooks/rules-of-hooks */
90
- }
91
-
92
- function StackContent({
93
- gap = 0,
94
- placement,
95
- maxSize,
96
- onAddItemRef,
97
- }: {
98
- gap: number;
99
- placement: StackPlacement;
100
- maxSize: number;
101
- onAddItemRef: MutableRefObject<((element: ReactElement) => void) | null>;
102
- }) {
103
- const id = useRef(0);
104
- const items = useRef<StackItem[]>([]);
105
- const [, setCounter] = useState(false);
106
- function rerender() {
107
- setCounter((v) => !v);
108
- }
109
-
110
- useEffect(() => {
111
- onAddItemRef.current = (element: ReactElement) => {
112
- const itemId = String(++id.current);
113
-
114
- items.current.push({
115
- id: itemId,
116
- height: undefined,
117
- element,
118
- isExiting: false,
119
- });
120
- // Stack > maxSize? Close items
121
- if (items.current.length > maxSize) {
122
- for (let i = 0; i < items.current.length; i++) {
123
- if (!items.current[i].isExiting) {
124
- items.current[i].isExiting = true;
125
- break;
126
- }
127
- }
128
- }
129
- rerender();
130
- };
131
- return () => {
132
- onAddItemRef.current = null;
133
- };
134
- }, [maxSize]); // eslint-disable-line react-hooks/exhaustive-deps
135
-
136
- function setHeightById(id: string, height: number) {
137
- const item = items.current.find(({ id: itemId }) => itemId === id);
138
- if (item && item.height !== height) {
139
- item.height = height;
140
- rerender();
141
- }
142
- }
143
-
144
- function removeItemById(id: string) {
145
- const previousLength = items.current.length;
146
- items.current = items.current.filter(({ id: itemId }) => itemId !== id);
147
- if (items.current.length !== previousLength) {
148
- rerender();
149
- }
150
- }
151
-
152
- let y = 0;
153
- return (
154
- <div data-stack-root="">
155
- {Array.from(items.current).map((item) => {
156
- let posY: number | undefined = undefined;
157
- const height = item.height;
158
- if (height) {
159
- posY = y;
160
- y += height + gap;
161
- }
162
-
163
- return (
164
- <StackItemContext.Provider
165
- key={item.id}
166
- value={{
167
- isExiting: item.isExiting,
168
- posY,
169
- onSetHeight: (height: number) => {
170
- if (height === item.height) {
171
- return;
172
- }
173
- setHeightById(item.id, height);
174
- },
175
- onRemoveItem: () => removeItemById(item.id),
176
- placement,
177
- }}
178
- >
179
- {item.element}
180
- </StackItemContext.Provider>
181
- );
182
- })}
183
- </div>
184
- );
185
- }
186
-
187
- export interface StackContextProps {
188
- addItem: (element: ReactElement) => void;
189
- }
190
-
191
- const StackContext = createContext<StackContextProps | null>(null);
192
-
193
- interface StackItem {
194
- id: string;
195
- element: ReactElement;
196
- height: number | undefined;
197
- isExiting: boolean;
198
- }
199
-
200
- export const StackProvider: FC<{
201
- children?: ReactNode | ReactNode[];
202
- gap?: number;
203
- placement?:
204
- | 'top-right'
205
- | 'top-center'
206
- | 'top-left'
207
- | 'bottom-right'
208
- | 'bottom-center'
209
- | 'bottom-left';
210
- maxSize?: number;
211
- }> = ({ children, gap = 8, placement = 'top-right', maxSize = 3 }) => {
212
- const onAddItemRef = useRef<(element: ReactElement) => void>(null);
213
- const value = useMemo(
214
- () => ({
215
- addItem: (element: ReactElement) => onAddItemRef.current?.(element),
216
- }),
217
- []
218
- );
219
-
220
- return (
221
- <StackContext.Provider value={value}>
222
- {children}
223
- <StackContent
224
- gap={gap}
225
- placement={placement}
226
- maxSize={maxSize}
227
- onAddItemRef={onAddItemRef}
228
- />
229
- </StackContext.Provider>
230
- );
231
- };
232
-
233
- export function useStack(): StackContextProps {
234
- const ctx = useContext(StackContext);
235
- if (!ctx) {
236
- throw new Error('useStack must be used within a StackContext.Provider');
237
- }
238
- return ctx;
239
- }
1
+ import {
2
+ useMemo,
3
+ useLayoutEffect,
4
+ createContext,
5
+ useContext,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import type { MutableRefObject, ReactElement, FC, ReactNode } from 'react';
11
+
12
+ export type StackPlacement =
13
+ | 'top-right'
14
+ | 'top-center'
15
+ | 'top-left'
16
+ | 'bottom-right'
17
+ | 'bottom-center'
18
+ | 'bottom-left';
19
+
20
+ // StackItemContext Component
21
+ export interface StackItemContextProps {
22
+ posY: number | undefined;
23
+ isExiting: boolean;
24
+ onSetHeight(height: number): void;
25
+ onRemoveItem(): void;
26
+ placement: StackPlacement;
27
+ }
28
+
29
+ const StackItemContext = createContext<StackItemContextProps | null>(null);
30
+ StackItemContext.displayName = 'StackItemContext';
31
+
32
+ interface StackItemProps {
33
+ ref: MutableRefObject<HTMLElement | null>;
34
+ openTimeoutInMilliseconds?: number;
35
+ closeTimeoutInMilliseconds?: number;
36
+ }
37
+
38
+ export function useStackItem({
39
+ ref,
40
+ closeTimeoutInMilliseconds = 110,
41
+ }: StackItemProps) {
42
+ const context = useContext(StackItemContext);
43
+ if (!context) {
44
+ return null;
45
+ }
46
+ const { onSetHeight, onRemoveItem, posY, placement, isExiting } = context;
47
+ /* eslint-disable react-hooks/rules-of-hooks */
48
+ const [state, setState] = useState<'entering' | 'entered' | 'exiting'>(
49
+ 'entering'
50
+ );
51
+
52
+ function onClose() {
53
+ setState('exiting');
54
+ }
55
+
56
+ useLayoutEffect(() => {
57
+ const height = ref.current?.getBoundingClientRect().height ?? 0;
58
+ onSetHeight(height);
59
+
60
+ if (isExiting) {
61
+ onClose();
62
+ }
63
+ });
64
+
65
+ useEffect(() => {
66
+ if (posY !== undefined && state === 'entering') {
67
+ setState('entered');
68
+ }
69
+ }, [state, posY]);
70
+
71
+ useLayoutEffect(() => {
72
+ if (state === 'exiting') {
73
+ const handle = setTimeout(() => {
74
+ onRemoveItem();
75
+ }, closeTimeoutInMilliseconds);
76
+
77
+ return () => {
78
+ clearTimeout(handle);
79
+ };
80
+ }
81
+ }, [state]); // eslint-disable-line react-hooks/exhaustive-deps
82
+
83
+ return {
84
+ translateY: posY,
85
+ state,
86
+ onClose,
87
+ placement,
88
+ };
89
+ /* eslint-enable react-hooks/rules-of-hooks */
90
+ }
91
+
92
+ function StackContent({
93
+ gap = 0,
94
+ placement,
95
+ maxSize,
96
+ onAddItemRef,
97
+ }: {
98
+ gap: number;
99
+ placement: StackPlacement;
100
+ maxSize: number;
101
+ onAddItemRef: MutableRefObject<((element: ReactElement) => void) | null>;
102
+ }) {
103
+ const id = useRef(0);
104
+ const items = useRef<StackItem[]>([]);
105
+ const [, setCounter] = useState(false);
106
+ function rerender() {
107
+ setCounter((v) => !v);
108
+ }
109
+
110
+ useEffect(() => {
111
+ onAddItemRef.current = (element: ReactElement) => {
112
+ const itemId = String(++id.current);
113
+
114
+ items.current.push({
115
+ id: itemId,
116
+ height: undefined,
117
+ element,
118
+ isExiting: false,
119
+ });
120
+ // Stack > maxSize? Close items
121
+ if (items.current.length > maxSize) {
122
+ for (let i = 0; i < items.current.length; i++) {
123
+ if (!items.current[i].isExiting) {
124
+ items.current[i].isExiting = true;
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ rerender();
130
+ };
131
+ return () => {
132
+ onAddItemRef.current = null;
133
+ };
134
+ }, [maxSize]); // eslint-disable-line react-hooks/exhaustive-deps
135
+
136
+ function setHeightById(id: string, height: number) {
137
+ const item = items.current.find(({ id: itemId }) => itemId === id);
138
+ if (item && item.height !== height) {
139
+ item.height = height;
140
+ rerender();
141
+ }
142
+ }
143
+
144
+ function removeItemById(id: string) {
145
+ const previousLength = items.current.length;
146
+ items.current = items.current.filter(({ id: itemId }) => itemId !== id);
147
+ if (items.current.length !== previousLength) {
148
+ rerender();
149
+ }
150
+ }
151
+
152
+ let y = 0;
153
+ return (
154
+ <div data-stack-root="">
155
+ {Array.from(items.current).map((item) => {
156
+ let posY: number | undefined = undefined;
157
+ const height = item.height;
158
+ if (height) {
159
+ posY = y;
160
+ y += height + gap;
161
+ }
162
+
163
+ return (
164
+ <StackItemContext.Provider
165
+ key={item.id}
166
+ value={{
167
+ isExiting: item.isExiting,
168
+ posY,
169
+ onSetHeight: (height: number) => {
170
+ if (height === item.height) {
171
+ return;
172
+ }
173
+ setHeightById(item.id, height);
174
+ },
175
+ onRemoveItem: () => removeItemById(item.id),
176
+ placement,
177
+ }}
178
+ >
179
+ {item.element}
180
+ </StackItemContext.Provider>
181
+ );
182
+ })}
183
+ </div>
184
+ );
185
+ }
186
+
187
+ export interface StackContextProps {
188
+ addItem: (element: ReactElement) => void;
189
+ }
190
+
191
+ const StackContext = createContext<StackContextProps | null>(null);
192
+
193
+ interface StackItem {
194
+ id: string;
195
+ element: ReactElement;
196
+ height: number | undefined;
197
+ isExiting: boolean;
198
+ }
199
+
200
+ export const StackProvider: FC<{
201
+ children?: ReactNode | ReactNode[];
202
+ gap?: number;
203
+ placement?:
204
+ | 'top-right'
205
+ | 'top-center'
206
+ | 'top-left'
207
+ | 'bottom-right'
208
+ | 'bottom-center'
209
+ | 'bottom-left';
210
+ maxSize?: number;
211
+ }> = ({ children, gap = 8, placement = 'top-right', maxSize = 3 }) => {
212
+ const onAddItemRef = useRef<(element: ReactElement) => void>(null);
213
+ const value = useMemo(
214
+ () => ({
215
+ addItem: (element: ReactElement) => onAddItemRef.current?.(element),
216
+ }),
217
+ []
218
+ );
219
+
220
+ return (
221
+ <StackContext.Provider value={value}>
222
+ {children}
223
+ <StackContent
224
+ gap={gap}
225
+ placement={placement}
226
+ maxSize={maxSize}
227
+ onAddItemRef={onAddItemRef}
228
+ />
229
+ </StackContext.Provider>
230
+ );
231
+ };
232
+
233
+ export function useStack(): StackContextProps {
234
+ const ctx = useContext(StackContext);
235
+ if (!ctx) {
236
+ throw new Error('useStack must be used within a StackContext.Provider');
237
+ }
238
+ return ctx;
239
+ }
@@ -1,3 +1,3 @@
1
- export * from './Snackbar';
2
- export * from './Stack';
3
- export * from './useSnackbarAnimation';
1
+ export * from './Snackbar';
2
+ export * from './Stack';
3
+ export * from './useSnackbarAnimation';