@charcoal-ui/react 6.1.0-beta.1 → 6.1.0-beta.2

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.
@@ -1,8 +1,8 @@
1
1
  import '../layout.css'
2
2
  import './index.css'
3
3
 
4
- import { forwardRef, useImperativeHandle, useRef } from 'react'
5
- import type { ReactNode } from 'react'
4
+ import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'
5
+ import { useClassNames } from '../../../_lib/useClassNames'
6
6
  import { NotificationItem } from '../NotificationItem'
7
7
  import { NotificationRegion } from '../NotificationRegion'
8
8
  import { useNotificationQueue } from '../useNotificationQueue'
@@ -12,30 +12,23 @@ export type SnackbarCloseReason =
12
12
  'timeout' | 'replaced' | 'action' | 'close' | 'unmounted'
13
13
 
14
14
  export type ShowSnackbarOptions = {
15
- /**
16
- * Snackbar の右側に表示するアクション
17
- */
18
15
  action?: ReactNode
19
- /**
20
- * Snackbar が閉じるときに呼び出される
21
- */
22
16
  onClose?: (reason: SnackbarCloseReason) => void
23
17
  }
24
18
 
25
- export type SnackbarHandler = {
26
- show: (message: ReactNode, options?: ShowSnackbarOptions) => void
19
+ type SnackbarBaseProps = {
20
+ message: ReactNode
21
+ action?: ReactNode
22
+ dim?: boolean
23
+ } & Omit<HTMLAttributes<HTMLDivElement>, 'children'>
24
+
25
+ export type SnackbarProps = Omit<SnackbarBaseProps, 'action'> & {
26
+ /** Snackbar の右側に表示するアクション */
27
+ action: NonNullable<ReactNode>
27
28
  }
28
29
 
29
- export type SnackbarProps = Omit<NotificationProps, 'position'> & {
30
- /**
31
- * Snackbar の表示位置。ボタン付きの場合は `bottom` に固定される
32
- * @default 'bottom'
33
- */
30
+ export type UseSnackbarProps = Omit<NotificationProps, 'position'> & {
34
31
  position?: Position
35
- /**
36
- * 暗い背景色
37
- * @default false
38
- */
39
32
  dim?: boolean
40
33
  }
41
34
 
@@ -44,12 +37,24 @@ type SnackbarContent = {
44
37
  action?: ReactNode
45
38
  }
46
39
 
47
- const Snackbar = forwardRef<SnackbarHandler, SnackbarProps>(function Snackbar(
48
- { position = 'bottom', dim = false, duration, order, ...regionProps },
49
- ref,
50
- ) {
40
+ const Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(
41
+ function Snackbar(props, ref) {
42
+ return <SnackbarBase {...props} ref={ref} />
43
+ },
44
+ )
45
+
46
+ export default Snackbar
47
+
48
+ export function useSnackbar(props: UseSnackbarProps = {}) {
51
49
  'use memo'
52
50
 
51
+ const {
52
+ position = 'bottom',
53
+ dim = false,
54
+ duration,
55
+ order,
56
+ ...regionProps
57
+ } = props
53
58
  const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } =
54
59
  useNotificationQueue<SnackbarContent, SnackbarCloseReason>('snackbar', {
55
60
  duration,
@@ -59,24 +64,14 @@ const Snackbar = forwardRef<SnackbarHandler, SnackbarProps>(function Snackbar(
59
64
  })
60
65
 
61
66
  function show(message: ReactNode, options: ShowSnackbarOptions = {}) {
62
- enqueue(
63
- {
64
- message,
65
- action: options.action,
66
- },
67
- options.onClose,
68
- )
67
+ enqueue({ message, action: options.action }, options.onClose)
69
68
  }
70
69
 
71
- useImperativeHandle(ref, () => ({ show }))
72
-
73
- // アクション付きの Snackbar は常に下部に表示される
74
70
  const hasAction = state.visibleToasts.some(
75
71
  (toast) => toast.content.action !== undefined,
76
72
  )
77
73
  const effectivePosition = hasAction ? 'bottom' : position
78
-
79
- return (
74
+ const element = (
80
75
  <NotificationRegion
81
76
  name="snackbar"
82
77
  state={state}
@@ -96,36 +91,46 @@ const Snackbar = forwardRef<SnackbarHandler, SnackbarProps>(function Snackbar(
96
91
  data-with-action={toast.content.action !== undefined}
97
92
  >
98
93
  {toast.content.action !== undefined && (
99
- <SnackbarAction
100
- action={toast.content.action}
101
- onClick={() => close(toast.key, 'action')}
102
- />
94
+ <div onClick={() => close(toast.key, 'action')}>
95
+ <SnackbarAction action={toast.content.action} />
96
+ </div>
103
97
  )}
104
98
  </NotificationItem>
105
99
  ))}
106
100
  </NotificationRegion>
107
101
  )
108
- })
109
102
 
110
- export default Snackbar
111
-
112
- export function useSnackbar(props: SnackbarProps = {}) {
113
- 'use memo'
114
-
115
- const snackbarHandlerRef = useRef<SnackbarHandler>(null)
116
- const element = <Snackbar ref={snackbarHandlerRef} {...props} />
117
- function show(message: ReactNode, options?: ShowSnackbarOptions) {
118
- snackbarHandlerRef.current?.show(message, options)
119
- }
120
103
  return [element, show] as const
121
104
  }
122
105
 
123
- function SnackbarAction({
124
- action,
125
- onClick,
126
- }: {
127
- action: ReactNode
128
- onClick: () => void
129
- }) {
130
- return <div onClick={onClick}>{action}</div>
106
+ const SnackbarBase = forwardRef<HTMLDivElement, SnackbarBaseProps>(
107
+ function SnackbarBase(
108
+ { message, action, dim = false, className, ...rootProps },
109
+ ref,
110
+ ) {
111
+ const classNames = useClassNames(
112
+ 'charcoal-notification',
113
+ 'charcoal-snackbar',
114
+ className,
115
+ )
116
+
117
+ return (
118
+ <div
119
+ {...rootProps}
120
+ ref={ref}
121
+ className={classNames}
122
+ data-dim={dim}
123
+ data-with-action={action !== undefined}
124
+ >
125
+ <div role="status" className="charcoal-notification-content">
126
+ <div className="charcoal-notification-label">{message}</div>
127
+ </div>
128
+ {action !== undefined && <SnackbarAction action={action} />}
129
+ </div>
130
+ )
131
+ },
132
+ )
133
+
134
+ function SnackbarAction({ action }: { action: ReactNode }) {
135
+ return <div>{action}</div>
131
136
  }
@@ -15,7 +15,7 @@ type ToastStoryArgs = unstable_ToastProps & {
15
15
  }
16
16
 
17
17
  export default {
18
- title: 'react/unstable_Toast',
18
+ title: 'react/Toast',
19
19
  component: unstable_Toast,
20
20
  parameters: {
21
21
  layout: 'centered',
@@ -33,33 +33,36 @@ export default {
33
33
  },
34
34
  args: {
35
35
  message: '保存しました',
36
+ position: 'top',
37
+ offset: 16,
36
38
  duration: 5000,
37
39
  order: 'queue',
40
+ zIndex: 20,
38
41
  type: 'success',
39
42
  },
40
43
  argTypes: {
41
44
  position: {
42
45
  options: ['top', 'bottom'],
43
46
  control: { type: 'inline-radio' },
44
- table: { category: 'Hook' },
47
+ table: { category: 'Hook', defaultValue: { summary: "'top'" } },
45
48
  },
46
49
  offset: {
47
50
  control: { type: 'number', min: 0, step: 1 },
48
- table: { category: 'Hook' },
51
+ table: { category: 'Hook', defaultValue: { summary: '16' } },
49
52
  },
50
53
  duration: {
51
54
  control: { type: 'number', min: 0, step: 500 },
52
55
  description: '表示時間(ミリ秒)',
53
- table: { category: 'Hook' },
56
+ table: { category: 'Hook', defaultValue: { summary: '5000' } },
54
57
  },
55
58
  order: {
56
59
  options: ['queue', 'replace'],
57
60
  control: { type: 'inline-radio' },
58
- table: { category: 'Hook' },
61
+ table: { category: 'Hook', defaultValue: { summary: "'queue'" } },
59
62
  },
60
63
  zIndex: {
61
64
  control: { type: 'number', min: 0, step: 1 },
62
- table: { category: 'Hook' },
65
+ table: { category: 'Hook', defaultValue: { summary: '20' } },
63
66
  },
64
67
  className: {
65
68
  control: 'text',
package/src/index.ts CHANGED
@@ -107,10 +107,10 @@ export {
107
107
  type ScrollStepContext,
108
108
  } from './components/Carousel'
109
109
  export {
110
- default as unstable_Snackbar,
110
+ default as UnstableSnackbar,
111
111
  useSnackbar as unstable_useSnackbar,
112
- type SnackbarProps as unstable_SnackbarProps,
113
- type SnackbarHandler as unstable_SnackbarHandler,
112
+ type SnackbarProps as UnstableSnackbarProps,
113
+ type UseSnackbarProps as unstable_UseSnackbarProps,
114
114
  type SnackbarCloseReason as unstable_SnackbarCloseReason,
115
115
  type ShowSnackbarOptions as unstable_ShowSnackbarOptions,
116
116
  } from './components/Notification/Snackbar'