@charcoal-ui/react 5.4.0 → 5.4.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.
- package/dist/components/DropdownSelector/DropdownMenuItem/index.d.ts +4 -1
- package/dist/components/DropdownSelector/DropdownMenuItem/index.d.ts.map +1 -1
- package/dist/components/TextEllipsis/helper.d.ts +1 -0
- package/dist/components/TextEllipsis/helper.d.ts.map +1 -1
- package/dist/components/TextEllipsis/index.d.ts +19 -2
- package/dist/components/TextEllipsis/index.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +28 -12
- package/dist/index.css.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/layered.css +28 -12
- package/dist/layered.css.map +1 -1
- package/package.json +5 -5
- package/src/components/Button/index.story.tsx +1 -1
- package/src/components/Checkbox/CheckboxInput/index.story.tsx +1 -1
- package/src/components/Checkbox/index.story.tsx +1 -1
- package/src/components/Clickable/index.story.tsx +1 -1
- package/src/components/DropdownSelector/DropdownMenuItem/index.css +16 -0
- package/src/components/DropdownSelector/DropdownMenuItem/index.tsx +26 -15
- package/src/components/DropdownSelector/ListItem/index.story.tsx +1 -1
- package/src/components/DropdownSelector/MenuList/index.story.tsx +1 -1
- package/src/components/DropdownSelector/Popover/index.story.tsx +1 -1
- package/src/components/DropdownSelector/index.story.tsx +30 -1
- package/src/components/HintText/index.story.tsx +1 -1
- package/src/components/Icon/index.story.tsx +1 -1
- package/src/components/IconButton/index.story.tsx +1 -1
- package/src/components/LoadingSpinner/index.story.tsx +1 -1
- package/src/components/Modal/index.story.tsx +1 -1
- package/src/components/MultiSelect/index.story.tsx +1 -1
- package/src/components/Radio/index.story.tsx +1 -1
- package/src/components/SegmentedControl/index.story.tsx +1 -1
- package/src/components/Switch/index.story.tsx +1 -1
- package/src/components/TagItem/index.story.tsx +1 -1
- package/src/components/TextArea/TextArea.story.tsx +1 -1
- package/src/components/TextEllipsis/TextEllipsis.story.tsx +87 -0
- package/src/components/TextEllipsis/helper.ts +22 -0
- package/src/components/TextEllipsis/index.css +12 -12
- package/src/components/TextEllipsis/index.tsx +31 -5
- package/src/components/TextField/TextField.story.tsx +1 -1
|
@@ -2,10 +2,12 @@ import './index.css'
|
|
|
2
2
|
|
|
3
3
|
import MenuItem, { MenuItemProps } from '../MenuItem'
|
|
4
4
|
import { MenuListContext } from '../MenuList/MenuListContext'
|
|
5
|
-
import { useContext } from 'react'
|
|
5
|
+
import { ReactNode, useContext } from 'react'
|
|
6
6
|
import Icon from '../../Icon'
|
|
7
7
|
|
|
8
|
-
export type DropdownMenuItemProps = Omit<MenuItemProps, 'as'>
|
|
8
|
+
export type DropdownMenuItemProps = Omit<MenuItemProps, 'as'> & {
|
|
9
|
+
secondary?: ReactNode
|
|
10
|
+
}
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* DropdownSelectorの選択肢として使うMenuItem
|
|
@@ -13,22 +15,31 @@ export type DropdownMenuItemProps = Omit<MenuItemProps, 'as'>
|
|
|
13
15
|
export default function DropdownMenuItem(props: DropdownMenuItemProps) {
|
|
14
16
|
const { value: ctxValue } = useContext(MenuListContext)
|
|
15
17
|
const isSelected = props.value === ctxValue
|
|
16
|
-
const { children, ...rest } = props
|
|
18
|
+
const { children, secondary, ...rest } = props
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
21
|
<MenuItem {...rest} aria-selected={isSelected}>
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
<div>
|
|
23
|
+
<div className="charcoal-dropdown-selector-menu-item-container">
|
|
24
|
+
{isSelected && (
|
|
25
|
+
<Icon
|
|
26
|
+
className="charcoal-dropdown-selector-menu-item-icon"
|
|
27
|
+
name="16/Check"
|
|
28
|
+
/>
|
|
29
|
+
)}
|
|
30
|
+
<span
|
|
31
|
+
className="charcoal-dropdown-selector-menu-item"
|
|
32
|
+
data-selected={isSelected}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
{secondary && (
|
|
38
|
+
<span className="charcoal-dropdown-selector-menu-secondary">
|
|
39
|
+
{secondary}
|
|
40
|
+
</span>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
32
43
|
</MenuItem>
|
|
33
44
|
)
|
|
34
45
|
}
|
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import Icon from '../../Icon'
|
|
3
3
|
import Switch from '../../Switch'
|
|
4
4
|
import ListItem from '.'
|
|
5
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
5
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
title: 'react/internals/ListItem',
|
|
@@ -2,7 +2,7 @@ import { action } from 'storybook/actions'
|
|
|
2
2
|
import MenuList from '.'
|
|
3
3
|
import MenuItem from '../MenuItem'
|
|
4
4
|
import MenuItemGroup from '../MenuItemGroup'
|
|
5
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
5
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
title: 'react/internals/MenuList',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef, CSSProperties, useState } from 'react'
|
|
2
2
|
import Popover from '.'
|
|
3
3
|
import Button from '../../Button'
|
|
4
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
4
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: 'react/internals/Popover',
|
|
@@ -5,7 +5,7 @@ import MenuItemGroup from './MenuItemGroup'
|
|
|
5
5
|
import DropdownMenuItem from './DropdownMenuItem'
|
|
6
6
|
import Modal from '../Modal'
|
|
7
7
|
import { ModalBody, ModalHeader } from '../Modal/ModalPlumbing'
|
|
8
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
8
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
9
9
|
import TextField from '../TextField'
|
|
10
10
|
import TextArea from '../TextArea'
|
|
11
11
|
import Button from '../Button'
|
|
@@ -424,3 +424,32 @@ export const Section: StoryObj<typeof DropdownSelector> = {
|
|
|
424
424
|
)
|
|
425
425
|
},
|
|
426
426
|
}
|
|
427
|
+
|
|
428
|
+
export const WithSeconday: StoryObj<typeof DropdownSelector> = {
|
|
429
|
+
render: function Render(props) {
|
|
430
|
+
const [selected, setSelected] = useState('option-3')
|
|
431
|
+
return (
|
|
432
|
+
<div style={{ width: 288 }}>
|
|
433
|
+
<DropdownSelector
|
|
434
|
+
{...props}
|
|
435
|
+
onChange={(value) => {
|
|
436
|
+
setSelected(value)
|
|
437
|
+
}}
|
|
438
|
+
value={selected}
|
|
439
|
+
label="label"
|
|
440
|
+
>
|
|
441
|
+
<MenuItemGroup text="fruits">
|
|
442
|
+
<DropdownMenuItem value={'option-1'}>Option 1</DropdownMenuItem>
|
|
443
|
+
<DropdownMenuItem value={'option-2'}>Option 2</DropdownMenuItem>
|
|
444
|
+
<DropdownMenuItem
|
|
445
|
+
value={'option-3'}
|
|
446
|
+
secondary={<span>Option 3 Secondary</span>}
|
|
447
|
+
>
|
|
448
|
+
Option 3
|
|
449
|
+
</DropdownMenuItem>
|
|
450
|
+
</MenuItemGroup>
|
|
451
|
+
</DropdownSelector>
|
|
452
|
+
</div>
|
|
453
|
+
)
|
|
454
|
+
},
|
|
455
|
+
}
|
|
@@ -12,7 +12,7 @@ import TextField from '../TextField'
|
|
|
12
12
|
import DropdownSelector from '../DropdownSelector'
|
|
13
13
|
import Checkbox from '../Checkbox'
|
|
14
14
|
import DropdownMenuItem from '../DropdownSelector/DropdownMenuItem'
|
|
15
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
15
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
16
16
|
|
|
17
17
|
export default {
|
|
18
18
|
title: 'react/Modal',
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
default as MultiSelect,
|
|
5
5
|
MultiSelectGroupProps,
|
|
6
6
|
} from '.'
|
|
7
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
7
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
8
8
|
import { action } from 'storybook/actions'
|
|
9
9
|
|
|
10
10
|
const StyledMultiSelectGroup = (props: MultiSelectGroupProps) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { action } from 'storybook/actions'
|
|
2
2
|
import Clickable from '../Clickable'
|
|
3
3
|
import TextArea from '.'
|
|
4
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
4
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: 'react/TextArea',
|
|
@@ -61,3 +61,90 @@ export const EmptyTitle: StoryObj<typeof TextEllipsis> = {
|
|
|
61
61
|
'title="" を渡すとツールチップは表示されません。ホバーしても何も出ません。',
|
|
62
62
|
},
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
const narrowRender = (args: React.ComponentProps<typeof TextEllipsis>) => (
|
|
66
|
+
<div style={{ width: 80 }} lang="en">
|
|
67
|
+
<TextEllipsis {...args} />
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
export const HyphensAuto: StoryObj<typeof TextEllipsis> = {
|
|
72
|
+
args: {
|
|
73
|
+
lineHeight: 24,
|
|
74
|
+
lineLimit: 2,
|
|
75
|
+
hyphens: 'auto',
|
|
76
|
+
children: 'internationalization',
|
|
77
|
+
},
|
|
78
|
+
render: narrowRender,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const HyphensManual: StoryObj<typeof TextEllipsis> = {
|
|
82
|
+
args: {
|
|
83
|
+
lineHeight: 24,
|
|
84
|
+
lineLimit: 2,
|
|
85
|
+
hyphens: 'manual',
|
|
86
|
+
children: 'inter\u00ADnational\u00ADization',
|
|
87
|
+
},
|
|
88
|
+
render: narrowRender,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const HyphensNone: StoryObj<typeof TextEllipsis> = {
|
|
92
|
+
args: {
|
|
93
|
+
lineHeight: 24,
|
|
94
|
+
lineLimit: 2,
|
|
95
|
+
hyphens: 'none',
|
|
96
|
+
children: 'internationalization',
|
|
97
|
+
},
|
|
98
|
+
render: narrowRender,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const LineHeightInherit: StoryObj<typeof TextEllipsis> = {
|
|
102
|
+
args: {
|
|
103
|
+
lineLimit: 2,
|
|
104
|
+
children: 'lineHeight を指定しない場合、親の line-height を継承します。',
|
|
105
|
+
},
|
|
106
|
+
render: (args) => (
|
|
107
|
+
<div style={{ lineHeight: 1.8, width: 200 }}>
|
|
108
|
+
<TextEllipsis {...args} />
|
|
109
|
+
</div>
|
|
110
|
+
),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const ShowTooltipFalse: StoryObj<typeof TextEllipsis> = {
|
|
114
|
+
args: {
|
|
115
|
+
lineHeight: 24,
|
|
116
|
+
lineLimit: 1,
|
|
117
|
+
showTooltip: false,
|
|
118
|
+
children:
|
|
119
|
+
'showTooltip=false の場合、ホバーしてもツールチップは表示されません。',
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const useLineClamp: StoryObj<typeof TextEllipsis> = {
|
|
124
|
+
render() {
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
style={{
|
|
128
|
+
width: 240,
|
|
129
|
+
borderInline: '2px solid var(--charcoal-border)',
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<div style={{ width: '100%', display: 'flex', gap: 8 }}>
|
|
133
|
+
<div>
|
|
134
|
+
<TextEllipsis>これは非常に長いテキストです。</TextEllipsis>
|
|
135
|
+
</div>
|
|
136
|
+
<div>{'useNowrap={false}'}</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div style={{ width: '100%', display: 'flex', gap: 8 }}>
|
|
140
|
+
<div>
|
|
141
|
+
<TextEllipsis useNowrap>
|
|
142
|
+
これは非常に長いテキストです。
|
|
143
|
+
</TextEllipsis>
|
|
144
|
+
</div>
|
|
145
|
+
<div>{'useNowrap={true}'}</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
},
|
|
150
|
+
}
|
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
import { Children, isValidElement, ReactElement, ReactNode } from 'react'
|
|
4
4
|
|
|
5
|
+
export const getFinalTitle = (
|
|
6
|
+
showTooltip: boolean,
|
|
7
|
+
title?: string,
|
|
8
|
+
children?: ReactNode,
|
|
9
|
+
): string | undefined => {
|
|
10
|
+
// 1. 表示フラグが false なら即終了
|
|
11
|
+
if (!showTooltip) {
|
|
12
|
+
return undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 2. タイトルの候補を取得
|
|
16
|
+
const resolvedTitle = title ?? onlyText(children)
|
|
17
|
+
|
|
18
|
+
// 3. resolvedTitle が falsy (undefined, null, "") なら終了
|
|
19
|
+
if (!resolvedTitle) {
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 4. 有効な文字列のみを返す
|
|
24
|
+
return resolvedTitle
|
|
25
|
+
}
|
|
26
|
+
|
|
5
27
|
const hasChildren = (
|
|
6
28
|
element: ReactNode,
|
|
7
29
|
): element is ReactElement<{ children: ReactNode[] }> =>
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
.charcoal-text-ellipsis {
|
|
2
2
|
overflow: hidden;
|
|
3
|
-
line-height: var(--charcoal-text-ellipsis-line-height);
|
|
4
3
|
overflow-wrap: break-word;
|
|
4
|
+
display: -webkit-box;
|
|
5
|
+
-webkit-box-orient: vertical;
|
|
6
|
+
-webkit-line-clamp: var(--charcoal-text-ellipsis-line-limit);
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
.charcoal-text-ellipsis[data-line-
|
|
8
|
-
|
|
9
|
-
white-space: nowrap;
|
|
9
|
+
.charcoal-text-ellipsis[data-has-line-height='true'] {
|
|
10
|
+
line-height: var(--charcoal-text-ellipsis-line-height);
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
.charcoal-text-ellipsis[data-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
13
|
+
.charcoal-text-ellipsis[data-has-line-height='false'] {
|
|
14
|
+
line-height: inherit;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.charcoal-text-ellipsis[data-line-limit='1'][data-use-nowrap='true'] {
|
|
18
|
+
text-overflow: ellipsis;
|
|
19
|
+
white-space: nowrap;
|
|
20
20
|
}
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import './index.css'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getFinalTitle } from './helper'
|
|
4
4
|
import { useClassNames } from '../../_lib/useClassNames'
|
|
5
|
+
import { CSSProperties } from 'react'
|
|
5
6
|
|
|
6
7
|
export type TextEllipsisProps = {
|
|
7
|
-
lineHeight
|
|
8
|
+
lineHeight?: number
|
|
8
9
|
lineLimit?: number
|
|
10
|
+
/**
|
|
11
|
+
* @default 'auto'
|
|
12
|
+
*/
|
|
13
|
+
hyphens?: CSSProperties['hyphens']
|
|
14
|
+
/**
|
|
15
|
+
* html title属性を付与。false のときは付与せず、空文字のときは表示しない
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
showTooltip?: boolean
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @default false
|
|
22
|
+
* true: use white-space: nowrap if lineLimit is 1
|
|
23
|
+
* false: use -webkit-line-clamp if lineLimit is 1
|
|
24
|
+
*/
|
|
25
|
+
useNowrap?: boolean
|
|
9
26
|
} & React.ComponentPropsWithoutRef<'div'>
|
|
10
27
|
|
|
11
28
|
/**
|
|
@@ -16,22 +33,31 @@ export default function TextEllipsis({
|
|
|
16
33
|
lineLimit = 1,
|
|
17
34
|
children,
|
|
18
35
|
title,
|
|
36
|
+
hyphens = 'auto',
|
|
37
|
+
showTooltip = true,
|
|
38
|
+
useNowrap = false,
|
|
19
39
|
...props
|
|
20
40
|
}: TextEllipsisProps) {
|
|
21
|
-
|
|
22
|
-
const finalTitle =
|
|
41
|
+
'use memo'
|
|
42
|
+
const finalTitle = getFinalTitle(showTooltip, title, children)
|
|
23
43
|
|
|
24
44
|
const classNames = useClassNames('charcoal-text-ellipsis', props.className)
|
|
45
|
+
const hasLineHeight = lineHeight !== undefined
|
|
25
46
|
|
|
26
47
|
return (
|
|
27
48
|
<div
|
|
28
49
|
{...props}
|
|
29
50
|
className={classNames}
|
|
30
51
|
data-line-limit={lineLimit}
|
|
52
|
+
data-has-line-height={hasLineHeight}
|
|
53
|
+
{...(useNowrap ? { 'data-use-nowrap': useNowrap } : {})}
|
|
31
54
|
style={
|
|
32
55
|
{
|
|
33
|
-
|
|
56
|
+
...(hasLineHeight && {
|
|
57
|
+
'--charcoal-text-ellipsis-line-height': `${lineHeight}px`,
|
|
58
|
+
}),
|
|
34
59
|
'--charcoal-text-ellipsis-line-limit': lineLimit,
|
|
60
|
+
hyphens,
|
|
35
61
|
...props.style,
|
|
36
62
|
} satisfies React.CSSProperties
|
|
37
63
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Clickable from '../Clickable'
|
|
2
2
|
import TextField from '.'
|
|
3
3
|
import { useState } from 'react'
|
|
4
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
4
|
+
import { Meta, StoryObj } from '@storybook/react-vite'
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: 'react/TextField',
|