@basic-ui/material 1.0.0-alpha.4 → 1.0.0-alpha.7

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,109 @@
1
+ import { rem } from 'polished';
2
+ import React, { useState } from 'react';
3
+
4
+ import { Box } from '../Box';
5
+ import { NavRailItem, NavRailIndicator, NavRailLabel } from './';
6
+ // import './styles.css';
7
+
8
+ export default {
9
+ title: 'components/NavRail',
10
+ };
11
+
12
+ const ListIcon = (props) => (
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ height={16}
16
+ width={16}
17
+ viewBox="0 0 24 24"
18
+ {...props}
19
+ >
20
+ <path
21
+ fill="currentColor"
22
+ d="M8 17q.425 0 .713-.288Q9 16.425 9 16t-.287-.713Q8.425 15 8 15t-.713.287Q7 15.575 7 16t.287.712Q7.575 17 8 17Zm0-4q.425 0 .713-.288Q9 12.425 9 12t-.287-.713Q8.425 11 8 11t-.713.287Q7 11.575 7 12t.287.712Q7.575 13 8 13Zm0-4q.425 0 .713-.288Q9 8.425 9 8t-.287-.713Q8.425 7 8 7t-.713.287Q7 7.575 7 8t.287.712Q7.575 9 8 9Zm3 8h6v-2h-6Zm0-4h6v-2h-6Zm0-4h6V7h-6ZM5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14ZM5 5v14V5Z"
23
+ />
24
+ </svg>
25
+ );
26
+
27
+ const KitchenIcon = (props) => (
28
+ <svg
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ height={16}
31
+ width={16}
32
+ viewBox="0 0 24 24"
33
+ {...props}
34
+ >
35
+ <path
36
+ fill="currentColor"
37
+ d="M8 8V5h2v3Zm0 9v-5h2v5Zm-2 5q-.825 0-1.412-.587Q4 20.825 4 20V4q0-.825.588-1.413Q5.175 2 6 2h12q.825 0 1.413.587Q20 3.175 20 4v16q0 .825-.587 1.413Q18.825 22 18 22Zm0-2h12v-9H6v9ZM6 9h12V4H6Z"
38
+ />
39
+ </svg>
40
+ );
41
+
42
+ const MenuIcon = (props) => (
43
+ <svg
44
+ xmlns="http://www.w3.org/2000/svg"
45
+ height={16}
46
+ width={16}
47
+ viewBox="0 0 24 24"
48
+ {...props}
49
+ >
50
+ <path
51
+ fill="currentColor"
52
+ d="M17 22v-8h-3V7q0-2.075 1.463-3.537Q16.925 2 19 2v20ZM7 22v-9.15q-1.275-.35-2.137-1.4Q4 10.4 4 9V2h2v7h1V2h2v7h1V2h2v7q0 1.4-.863 2.45-.862 1.05-2.137 1.4V22Z"
53
+ />
54
+ </svg>
55
+ );
56
+
57
+ const Example = ({ selectable = true }) => {
58
+ const [selectedItem, setSelectedItem] = useState(selectable ? '0' : '-1');
59
+
60
+ return (
61
+ <Box
62
+ bg="surface"
63
+ width="80px"
64
+ position="fixed"
65
+ top="0"
66
+ bottom="0"
67
+ left="0"
68
+ py={rem(80)}
69
+ display="flex"
70
+ flexDirection="column"
71
+ >
72
+ <NavRailItem
73
+ selected={selectedItem === '0'}
74
+ onClick={() => {
75
+ selectable && setSelectedItem('0');
76
+ }}
77
+ >
78
+ <NavRailIndicator>
79
+ <MenuIcon />
80
+ </NavRailIndicator>
81
+ <NavRailLabel>Menu</NavRailLabel>
82
+ </NavRailItem>
83
+ <NavRailItem
84
+ selected={selectedItem === '1'}
85
+ onClick={() => {
86
+ selectable && setSelectedItem('1');
87
+ }}
88
+ >
89
+ <NavRailIndicator>
90
+ <KitchenIcon />
91
+ </NavRailIndicator>
92
+ <NavRailLabel>Kitchen</NavRailLabel>
93
+ </NavRailItem>
94
+ <NavRailItem
95
+ selected={selectedItem === '2'}
96
+ onClick={() => {
97
+ selectable && setSelectedItem('2');
98
+ }}
99
+ >
100
+ <NavRailIndicator>
101
+ <ListIcon />
102
+ </NavRailIndicator>
103
+ <NavRailLabel>Orders</NavRailLabel>
104
+ </NavRailItem>
105
+ </Box>
106
+ );
107
+ };
108
+
109
+ export const Normal = () => <Example />;
@@ -0,0 +1,164 @@
1
+ import React, { forwardRef, useMemo } from 'react';
2
+ import { rem } from 'polished';
3
+ import { get } from 'styled-system';
4
+
5
+ import type { BoxProps, SxStyleProp } from '../Box';
6
+ import { Box } from '../Box';
7
+ import { useTheme } from '../theme';
8
+ import { Text } from '../Text';
9
+ import type { TextProps } from '../Text';
10
+ import type { RippleBoxProps } from '../Ripple';
11
+ import { useRippleSurface } from '../Ripple';
12
+
13
+ export interface NavRailItemProps extends Omit<BoxProps, 'color'> {
14
+ as?: React.ElementType<any>;
15
+ children?: React.ReactNode;
16
+ disabled?: boolean;
17
+ selected?: boolean;
18
+ color?: string;
19
+ }
20
+
21
+ export const NavRailIndicator = forwardRef<HTMLDivElement, RippleBoxProps>(
22
+ function NavRailIndicator(props, forwardedRef) {
23
+ const { as = 'div', children, __css, ...otherProps } = props;
24
+
25
+ return (
26
+ <Box
27
+ color="currentColor"
28
+ ref={forwardedRef}
29
+ as={as}
30
+ data-nav-rail-item-indicator=""
31
+ {...otherProps}
32
+ __css={{
33
+ width: '100%',
34
+ height: '100%',
35
+ maxWidth: rem(56),
36
+ maxHeight: rem(56),
37
+ borderRadius: 'full',
38
+ bg: 'var(--indicator-background-color)',
39
+ display: 'flex',
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ minHeight: rem(32),
43
+ ...__css,
44
+ }}
45
+ >
46
+ {children}
47
+ </Box>
48
+ );
49
+ }
50
+ );
51
+
52
+ export const NavRailLabel = forwardRef<HTMLDivElement, TextProps>(
53
+ function NavRailLabel(props, forwardedRef) {
54
+ const { as = 'div', children, __css, ...otherProps } = props;
55
+
56
+ return (
57
+ <Text
58
+ ref={forwardedRef}
59
+ as={as}
60
+ variant="caption"
61
+ {...otherProps}
62
+ lineHeight={1}
63
+ __css={{
64
+ pt: rem(4),
65
+ pb: rem(12),
66
+ color: 'inherit',
67
+ ...__css,
68
+ }}
69
+ >
70
+ {children}
71
+ </Text>
72
+ );
73
+ }
74
+ );
75
+
76
+ export const NavRailItem = forwardRef<HTMLDivElement, NavRailItemProps>(
77
+ function NavRailItem(props, forwardedRef) {
78
+ const {
79
+ as = 'button',
80
+ children,
81
+ color = 'primary-container',
82
+ selected = false,
83
+ disabled = false,
84
+ style,
85
+ onKeyDown,
86
+ onPointerDown,
87
+ __css,
88
+ ...otherProps
89
+ } = props;
90
+ const theme = useTheme();
91
+ const baseOpacity = 0,
92
+ hoverOpacity = 0.04,
93
+ focusOpacity = 0.12,
94
+ pressedOpacity = 0.12;
95
+ const ripple = useRippleSurface({
96
+ rippleColor: `on.${color}`,
97
+ onKeyDown,
98
+ onPointerDown,
99
+ baseOpacity,
100
+ hoverOpacity,
101
+ focusOpacity,
102
+ pressedOpacity,
103
+ });
104
+
105
+ // Apply ripple from nav item to nav indicator
106
+ const rippleCss = useMemo(() => {
107
+ const ret: SxStyleProp = {};
108
+ const keys = Object.keys(ripple.__css);
109
+ for (const key of keys) {
110
+ if (!key.startsWith('&')) {
111
+ ret['& [data-nav-rail-item-indicator]'] =
112
+ ret['& [data-nav-rail-item-indicator]'] || {};
113
+ ret['& [data-nav-rail-item-indicator]'][key] = ripple.__css[key];
114
+ continue;
115
+ }
116
+
117
+ const newKey = key.replace(
118
+ /(.+?)::(before|after)/g,
119
+ '$1 [data-nav-rail-item-indicator]::$2'
120
+ );
121
+ ret[newKey] = ripple.__css[key];
122
+ }
123
+ return ret;
124
+ }, [ripple.__css]);
125
+ console.log(Object.keys(rippleCss));
126
+
127
+ return (
128
+ <Box
129
+ ref={forwardedRef}
130
+ as={as}
131
+ {...otherProps}
132
+ onPointerDown={ripple.onPointerDown}
133
+ onKeyDown={ripple.onKeyDown}
134
+ aria-pressed={selected}
135
+ type="button"
136
+ style={{ ...ripple.style, ...style }}
137
+ disabled={disabled}
138
+ __css={{
139
+ display: 'flex',
140
+ flexDirection: 'column',
141
+ alignItems: 'center',
142
+ color: `on.${color}`,
143
+ '--indicator-background-color': selected
144
+ ? get(theme, `colors.${color}`)
145
+ : 'transparent',
146
+ border: 'none',
147
+ ':focus': {
148
+ outline: 'none',
149
+ },
150
+ backgroundColor: 'transparent',
151
+ margin: 0,
152
+ padding: 0,
153
+ px: rem(12),
154
+ cursor: 'pointer',
155
+ textDecoration: 'none',
156
+ ...rippleCss,
157
+ ...__css,
158
+ }}
159
+ >
160
+ {children}
161
+ </Box>
162
+ );
163
+ }
164
+ );
@@ -0,0 +1 @@
1
+ export * from './NavRailItem';
@@ -1,4 +1,5 @@
1
1
  import type { CSSProperties } from 'react';
2
+ import { useMemo } from 'react';
2
3
  import { get } from '@styled-system/css';
3
4
  import { wrapEvent } from '@basic-ui/core';
4
5
 
@@ -22,7 +23,7 @@ export interface UseRippleSurfaceOptions<T extends HTMLElement> {
22
23
  }
23
24
 
24
25
  export function useRippleSurface<T extends HTMLElement>(
25
- opts: UseRippleSurfaceOptions<T>
26
+ opts: UseRippleSurfaceOptions<T> = {}
26
27
  ) {
27
28
  let {
28
29
  // eslint-disable-next-line prefer-const
@@ -62,67 +63,78 @@ export function useRippleSurface<T extends HTMLElement>(
62
63
  ...rippleProps,
63
64
  });
64
65
 
65
- const css: SxStyleProp = {
66
- overflow: 'hidden',
67
- position: 'relative',
68
- cursor: 'pointer',
69
- // fix overflow: hidden + borderRadius in Safari
70
- // https://gist.github.com/ayamflow/b602ab436ac9f05660d9c15190f4fd7b#gistcomment-2359479
71
- willChange: 'transform,opacity',
72
- WebkitTapHighlightColor: 'transparent',
73
- // ripple overlay
74
- '&::before': {
75
- backgroundColor: rippleColor,
76
- boxSizing: 'content-box',
77
- position: 'absolute',
78
- content: '""',
79
- opacity: baseOpacity,
80
- pointerEvents: 'none',
81
- top: '0',
82
- left: '0',
83
- width: '100%',
84
- height: '100%',
85
- transition: 'opacity 75ms linear',
86
- },
87
- '&:hover::before': {
88
- opacity: hoverOpacity,
89
- },
90
- '&:focus-visible::before': {
91
- opacity: focusOpacity,
92
- },
93
- ...(!rippleEnabled && {
94
- '&:active::before': {
95
- opacity: pressedOpacity,
66
+ const css: SxStyleProp = useMemo(
67
+ () => ({
68
+ overflow: 'hidden',
69
+ position: 'relative',
70
+ cursor: 'pointer',
71
+ // fix overflow: hidden + borderRadius in Safari
72
+ // https://gist.github.com/ayamflow/b602ab436ac9f05660d9c15190f4fd7b#gistcomment-2359479
73
+ willChange: 'transform,opacity',
74
+ WebkitTapHighlightColor: 'transparent',
75
+ // ripple overlay
76
+ '&::before': {
77
+ backgroundColor: rippleColor,
78
+ boxSizing: 'content-box',
79
+ position: 'absolute',
80
+ content: '""',
81
+ opacity: baseOpacity,
82
+ pointerEvents: 'none',
83
+ top: '0',
84
+ left: '0',
85
+ width: '100%',
86
+ height: '100%',
87
+ transition: 'opacity 75ms linear',
96
88
  },
97
- }),
98
- '&[aria-pressed="true"]::before': {
99
- opacity: baseOpacity + pressedOpacity,
100
- },
101
- '&[aria-pressed="true"]:hover::before': {
102
- opacity: pressedOpacity + hoverOpacity,
103
- },
104
- '&[aria-pressed="true"]:focus-visible::before': {
105
- opacity: pressedOpacity + focusOpacity,
106
- },
107
- ...(!rippleEnabled && {
108
- '&[aria-pressed="true"]:active::before': {
109
- opacity: pressedOpacity + pressedOpacity,
89
+ '&:hover::before': {
90
+ opacity: hoverOpacity,
110
91
  },
111
- }),
112
- ['&:disabled::before,&:disabled:hover::before,&:disabled:focus::before,&:disabled:active::before,' +
113
- '&[data-disabled]::before,&[data-disabled]:hover::before,&[data-disabled]:focus::before,' +
114
- '&[data-disabled]:active::before,&:disabled[aria-pressed="true"]::before']:
115
- {
116
- opacity: 0,
92
+ '&:focus-visible::before': {
93
+ opacity: focusOpacity,
94
+ },
95
+ ...(!rippleEnabled && {
96
+ '&:active::before': {
97
+ opacity: pressedOpacity,
98
+ },
99
+ }),
100
+ '&[aria-pressed="true"]::before': {
101
+ opacity: baseOpacity + pressedOpacity,
102
+ },
103
+ '&[aria-pressed="true"]:hover::before': {
104
+ opacity: pressedOpacity + hoverOpacity,
117
105
  },
118
- '&:disabled,&[data-disabled]': {
119
- cursor: 'default',
120
- },
121
- // ripple
122
- ...(rippleEnabled && {
123
- '&::after': { ...rippleStyle({ animation }) },
106
+ '&[aria-pressed="true"]:focus-visible::before': {
107
+ opacity: pressedOpacity + focusOpacity,
108
+ },
109
+ ...(!rippleEnabled && {
110
+ '&[aria-pressed="true"]:active::before': {
111
+ opacity: pressedOpacity + pressedOpacity,
112
+ },
113
+ }),
114
+ ['&:disabled::before,&:disabled:hover::before,&:disabled:focus::before,&:disabled:active::before,' +
115
+ '&[data-disabled]::before,&[data-disabled]:hover::before,&[data-disabled]:focus::before,' +
116
+ '&[data-disabled]:active::before,&:disabled[aria-pressed="true"]::before']:
117
+ {
118
+ opacity: 0,
119
+ },
120
+ '&:disabled,&[data-disabled]': {
121
+ cursor: 'default',
122
+ },
123
+ // ripple
124
+ ...(rippleEnabled && {
125
+ '&::after': { ...rippleStyle({ animation }) },
126
+ }),
124
127
  }),
125
- };
128
+ [
129
+ animation,
130
+ baseOpacity,
131
+ focusOpacity,
132
+ hoverOpacity,
133
+ pressedOpacity,
134
+ rippleColor,
135
+ rippleEnabled,
136
+ ]
137
+ );
126
138
 
127
139
  return {
128
140
  style: { ...animationStyle, ...style },
@@ -20,7 +20,7 @@ const CheckBoxCore: React.FC<
20
20
  }
21
21
  > = _CheckBoxCore as any;
22
22
 
23
- interface SwitchProps
23
+ export interface SwitchProps
24
24
  extends BoxProps<
25
25
  HTMLInputElement,
26
26
  React.InputHTMLAttributes<HTMLInputElement>