@charcoal-ui/react 6.0.0-rc.3 → 6.0.0-rc.5
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/_lib/useIsomorphicLayoutEffect.d.ts +6 -0
- package/dist/_lib/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/components/Carousel/index.d.ts +2 -6
- package/dist/components/Carousel/index.d.ts.map +1 -1
- package/dist/components/Carousel/useCarouselScroller.d.ts.map +1 -1
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/Modal/useTransitionPresence.d.ts +19 -0
- package/dist/components/Modal/useTransitionPresence.d.ts.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +100 -47
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/layered.css +100 -47
- package/dist/layered.css.map +1 -1
- package/package.json +5 -6
- package/src/__tests__/css-variables.test.ts +1 -1
- package/src/__tests__/token-v1-compat.test.ts +1 -2
- package/src/__tests__/token-v1-remap.test.ts +6 -6
- package/src/_lib/useIsomorphicLayoutEffect.ts +7 -0
- package/src/components/Carousel/MIGRATION.md +39 -39
- package/src/components/Carousel/__snapshots__/index.css.snap +39 -37
- package/src/components/Carousel/index.css +45 -44
- package/src/components/Carousel/index.story.tsx +87 -68
- package/src/components/Carousel/index.test.tsx +68 -47
- package/src/components/Carousel/index.tsx +23 -14
- package/src/components/Carousel/useCarouselScroller.ts +8 -12
- package/src/components/FieldLabel/__snapshots__/index.css.snap +1 -1
- package/src/components/FieldLabel/index.css +1 -1
- package/src/components/IconButton/__snapshots__/index.css.snap +1 -1
- package/src/components/IconButton/index.css +1 -1
- package/src/components/Modal/__snapshots__/index.css.snap +32 -1
- package/src/components/Modal/index.css +34 -0
- package/src/components/Modal/index.test.tsx +200 -0
- package/src/components/Modal/index.tsx +48 -78
- package/src/components/Modal/useTransitionPresence.ts +95 -0
- package/src/index.ts +0 -1
|
@@ -6,7 +6,7 @@ import postcss, { type Rule } from 'postcss'
|
|
|
6
6
|
// Design Token 1.0互換モードの「見た目の非破壊」を検証する。
|
|
7
7
|
//
|
|
8
8
|
// 移行: 旧 `@charcoal-ui/react/dist/index.css` (v1, --charcoal-* を直接使用)
|
|
9
|
-
// → 新 コンポーネントCSS (v2, --charcoal-color-* を使用) + `
|
|
9
|
+
// → 新 コンポーネントCSS (v2, --charcoal-color-* を使用) + `v1/remap.css` remap
|
|
10
10
|
//
|
|
11
11
|
// 非破壊であるためには、各 (セレクタ, プロパティ) で新CSSが使う 1つの --charcoal-color-*
|
|
12
12
|
// が、旧CSSで使われていた「単一の」--charcoal-* に対応していなければならない。同じ新トークンが
|
|
@@ -99,7 +99,6 @@ describe('Design Token 1.0互換: 旧 index.css の見た目を壊さない', ()
|
|
|
99
99
|
- .charcoal-button | color → --charcoal-text2
|
|
100
100
|
- .charcoal-checkbox__label_div | color → --charcoal-text2
|
|
101
101
|
- .charcoal-ui-dropdown-selector-text | color → --charcoal-text2
|
|
102
|
-
- .charcoal-field-label | color → --charcoal-text1
|
|
103
102
|
- .charcoal-modal-header-title | color → --charcoal-text1
|
|
104
103
|
- .charcoal-multi-select-label | color → --charcoal-text2
|
|
105
104
|
- .charcoal-radio__label_div | color → --charcoal-text2
|
|
@@ -3,9 +3,9 @@ import fs from 'node:fs/promises'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import postcss from 'postcss'
|
|
5
5
|
|
|
6
|
-
// Design Token 1.0互換モード (`@charcoal-ui/theme/css/
|
|
6
|
+
// Design Token 1.0互換モード (`@charcoal-ui/theme/css/v1/remap.css`) の remap を検証する。
|
|
7
7
|
//
|
|
8
|
-
// アプリは 1.0互換 (`
|
|
8
|
+
// アプリは 1.0互換 (`v1/remap.css`) か 2.0 (`v2/{light,dark}.css`) のいずれかで --charcoal-color-*
|
|
9
9
|
// を供給し、コンポーネント実装CSS (`@charcoal-ui/react`) がそれを消費する。remap が
|
|
10
10
|
// 「壊さない・冗長でない・正しい」ことを次で担保する:
|
|
11
11
|
// 1. 非破壊: コンポーネントが使う --charcoal-color-* は全て remap 経由で解決できる。
|
|
@@ -13,8 +13,8 @@ import postcss from 'postcss'
|
|
|
13
13
|
// 3. 冗長でない: 2.0 API にも無く・内部参照も無く・コンポーネントも使わないトークンを持たない。
|
|
14
14
|
|
|
15
15
|
const cssDir = path.resolve(import.meta.dirname, '../../../theme/src/css')
|
|
16
|
-
const tokenV1Path = path.join(cssDir, '
|
|
17
|
-
const variables2Path = path.join(cssDir, '
|
|
16
|
+
const tokenV1Path = path.join(cssDir, 'v1/remap.css')
|
|
17
|
+
const variables2Path = path.join(cssDir, 'v2/light.css')
|
|
18
18
|
const componentsDir = path.resolve(import.meta.dirname, '../components')
|
|
19
19
|
|
|
20
20
|
const COLOR_PREFIX = '--charcoal-color-'
|
|
@@ -89,7 +89,7 @@ for (const file of componentCssFiles) {
|
|
|
89
89
|
componentUsed.add(v)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
describe('Design Token 1.0互換 (
|
|
92
|
+
describe('Design Token 1.0互換 (v1/remap.css) remap', () => {
|
|
93
93
|
it('重複定義がない', () => {
|
|
94
94
|
expect(duplicates).toEqual([])
|
|
95
95
|
})
|
|
@@ -119,7 +119,7 @@ describe('Design Token 1.0互換 (_token_v1.css) remap', () => {
|
|
|
119
119
|
})
|
|
120
120
|
|
|
121
121
|
it.each(componentCssFiles)(
|
|
122
|
-
'%s: 使用する --charcoal-color-* は全て
|
|
122
|
+
'%s: 使用する --charcoal-color-* は全て v1/remap.css 経由で解決する (非破壊)',
|
|
123
123
|
async (file) => {
|
|
124
124
|
const css = await fs.readFile(file, 'utf-8')
|
|
125
125
|
const unresolved = [...usedColorVars(css)]
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
## 移行の要点
|
|
7
7
|
|
|
8
|
-
- **import
|
|
8
|
+
- **import 差し替えがほぼ唯一の必須変更**。`children` はそのまま渡せる(ただし自前の
|
|
9
|
+
`flex` ラッパーは外して **1 スライド 1 直接子要素**にし、ラッパーの `gap` は
|
|
10
|
+
各スライドの `margin` に置き換える)。
|
|
9
11
|
- `onScroll` / `onResize` / `onScrollStateChange` / `ref`(`resetScroll()`)/ `defaultScroll` /
|
|
10
12
|
`hasGradient` は **sandbox と同じシグネチャ**で利用できる(drop-in 互換)。
|
|
11
13
|
- `scrollAmountCoef` は `scrollStep`(関数も可)に名称変更。`fadeInGradient` / `buttonOffset` 系 /
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
| | sandbox (`@charcoal-ui/react-sandbox`) | react (`@charcoal-ui/react`) |
|
|
18
20
|
| -------------- | -------------------------------------- | ----------------------------------------------- |
|
|
19
21
|
| スクロール | react-spring による JS アニメーション | ネイティブ overflow + CSS `scroll-snap` |
|
|
20
|
-
| 子要素 | `children`(任意のノード) | `
|
|
22
|
+
| 子要素 | `children`(任意のノード) | `children`(1 直接子要素 = 1 スライド) |
|
|
21
23
|
| インジケーター | なし | CSS `::scroll-marker` / JS フォールバックの dot |
|
|
22
24
|
| スタイル | styled-components | プレーン CSS(`index.css`) |
|
|
23
25
|
|
|
@@ -28,14 +30,15 @@
|
|
|
28
30
|
+ import { Carousel } from '@charcoal-ui/react'
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
公開型: `CarouselProps` / `
|
|
33
|
+
公開型: `CarouselProps` / `CarouselHandlerRef` / `ScrollAlign` / `ScrollStep` /
|
|
32
34
|
`ScrollStepContext` / `ScrollSnap` / `ScrollSnapType` / `ScrollSnapAlign`。
|
|
33
35
|
|
|
34
|
-
## 子要素: `children
|
|
36
|
+
## 子要素: `children`(そのまま渡せる)
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
sandbox と同じく子ノードを直接渡し、スライドの寸法・間隔も sandbox 同様に利用者が持つ
|
|
39
|
+
(コンポーネントの責務はスクロールと indicator / arrow によるページ送りのみ)。
|
|
40
|
+
ただし**直接子要素 1 つを 1 スライド**として数えるので、`flex` ラッパーは外し、
|
|
41
|
+
`gap` の代わりに各スライドの `margin` などで間隔を注入する。
|
|
39
42
|
|
|
40
43
|
```diff
|
|
41
44
|
- <Carousel hasGradient defaultScroll={{ align: 'center' }} scrollAmountCoef={0.75}>
|
|
@@ -45,38 +48,36 @@ sandbox では自前で `flex` ラッパーや `gap` を組んでいたが、新
|
|
|
45
48
|
- ))}
|
|
46
49
|
- </div>
|
|
47
50
|
- </Carousel>
|
|
48
|
-
+ <Carousel
|
|
49
|
-
+
|
|
50
|
-
+
|
|
51
|
-
+
|
|
52
|
-
+
|
|
53
|
-
+ items={items.map((i) => ({ id: String(i), children: <Slide /> }))}
|
|
54
|
-
+ />
|
|
51
|
+
+ <Carousel hasGradient defaultScroll={{ align: 'center' }} scrollStep={0.75}>
|
|
52
|
+
+ {items.map((i) => (
|
|
53
|
+
+ <Slide key={i} style={{ marginInlineEnd: 8 }} />
|
|
54
|
+
+ ))}
|
|
55
|
+
+ </Carousel>
|
|
55
56
|
```
|
|
56
57
|
|
|
57
|
-
`
|
|
58
|
+
`key` は再レンダー時のスライド識別に使うので、一意かつ安定した値にする。
|
|
58
59
|
|
|
59
60
|
## props 対応表
|
|
60
61
|
|
|
61
|
-
| sandbox | react
|
|
62
|
-
| ------------------------------------------------- |
|
|
63
|
-
| `children` | `
|
|
64
|
-
| `scrollAmountCoef`(既定 `0.75`) | `scrollStep`(既定 `0.75`)
|
|
65
|
-
| `defaultScroll: { align, offset }` | `defaultScroll: { align, offset }`
|
|
66
|
-
| `hasGradient` | `hasGradient`(既定 `false`)
|
|
67
|
-
| `fadeInGradient` | (廃止)
|
|
68
|
-
| `buttonOffset` / `buttonPadding` / `bottomOffset` | (廃止)
|
|
69
|
-
| `centerItems` | (廃止)
|
|
70
|
-
| `onScroll(left)` | `onScroll(left)`
|
|
71
|
-
| `onResize(width)` | `onResize(width)`
|
|
72
|
-
| `onScrollStateChange(canScroll)` | `onScrollStateChange(canScroll)`
|
|
73
|
-
| `ref`(`CarouselHandlerRef.resetScroll()`) | `ref`(`CarouselHandlerRef.resetScroll()`)
|
|
74
|
-
| — | `size: 'S' \| 'M'`(既定 `'M'`)
|
|
75
|
-
| — | `navigationButtons?: boolean`
|
|
76
|
-
| — | `indicator?: boolean`
|
|
77
|
-
| — | `fullWidth?: boolean`(既定 `false`)
|
|
78
|
-
| — | `className?: string`
|
|
79
|
-
| — | `scrollSnap?: { type?; align? }`
|
|
62
|
+
| sandbox | react | 備考 |
|
|
63
|
+
| ------------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| `children` | `children` | ✅ そのまま対応(1 直接子要素 = 1 スライド。ラッパーは外す。上記参照) |
|
|
65
|
+
| `scrollAmountCoef`(既定 `0.75`) | `scrollStep`(既定 `0.75`) | `number`(表示幅比)に加え `(ctx) => px` の関数も渡せる |
|
|
66
|
+
| `defaultScroll: { align, offset }` | `defaultScroll: { align, offset }` | `align` は `'left' \| 'center' \| 'right'`。ほぼ同等 |
|
|
67
|
+
| `hasGradient` | `hasGradient`(既定 `false`) | ✅ そのまま対応(mask による透過フェード) |
|
|
68
|
+
| `fadeInGradient` | (廃止) | スクロール可能な側のみ常にフェード |
|
|
69
|
+
| `buttonOffset` / `buttonPadding` / `bottomOffset` | (廃止) | ボタン配置は CSS グリッド(左右 72px ゾーン)に固定 |
|
|
70
|
+
| `centerItems` | (廃止) | スライドの寸法・間隔は children 側で注入する(sandbox 同様) |
|
|
71
|
+
| `onScroll(left)` | `onScroll(left)` | ✅ そのまま対応(scroll で発火) |
|
|
72
|
+
| `onResize(width)` | `onResize(width)` | ✅ scroller 幅の変化で発火 |
|
|
73
|
+
| `onScrollStateChange(canScroll)` | `onScrollStateChange(canScroll)` | ✅ `canPrev \|\| canNext` の変化で発火 |
|
|
74
|
+
| `ref`(`CarouselHandlerRef.resetScroll()`) | `ref`(`CarouselHandlerRef.resetScroll()`) | ✅ `forwardRef` で対応。`defaultScroll` の初期位置へ戻す |
|
|
75
|
+
| — | `size: 'S' \| 'M'`(既定 `'M'`) | 新規。`S` は 1 枚全幅 + `mandatory` スナップ |
|
|
76
|
+
| — | `navigationButtons?: boolean` | 既定は `size === 'M'` |
|
|
77
|
+
| — | `indicator?: boolean` | 既定は `size === 'S'` |
|
|
78
|
+
| — | `fullWidth?: boolean`(既定 `false`) | `100vw` 表示 |
|
|
79
|
+
| — | `className?: string` | ルートに付与 |
|
|
80
|
+
| — | `scrollSnap?: { type?; align? }` | `type`: `none`/`proximity`/`mandatory`、`align`: `center`/`start`。未指定で M=none / S=mandatory / center |
|
|
80
81
|
|
|
81
82
|
## 挙動の変更(移行時に確認すること)
|
|
82
83
|
|
|
@@ -92,8 +93,6 @@ sandbox では自前で `flex` ラッパーや `gap` を組んでいたが、新
|
|
|
92
93
|
非対応環境では JS フォールバック)。
|
|
93
94
|
- **キーボード操作**: スクローラーが `tabIndex={0}` でフォーカス可能になり、`←` / `→` で 1 ステップスクロール。
|
|
94
95
|
フォーカスリングは charcoal 標準(`box-shadow: 0 0 0 4px rgba(0, 150, 250, 0.32)`)。
|
|
95
|
-
- **グラデーション**: `mask` による透過から、背景色(`#fff`)オーバーレイ方式に変更。
|
|
96
|
-
ダークモードは現状未対応(背景色固定)。
|
|
97
96
|
|
|
98
97
|
## スクロール量を細かく制御したい場合
|
|
99
98
|
|
|
@@ -101,17 +100,18 @@ sandbox では自前で `flex` ラッパーや `gap` を組んでいたが、新
|
|
|
101
100
|
|
|
102
101
|
```tsx
|
|
103
102
|
// 表示幅の比率(sandbox の scrollAmountCoef 相当)
|
|
104
|
-
<Carousel
|
|
103
|
+
<Carousel scrollStep={0.5}>{slides}</Carousel>
|
|
105
104
|
|
|
106
105
|
// 進む px を直接返す(端は残り全部、など)
|
|
107
106
|
<Carousel
|
|
108
|
-
items={items}
|
|
109
107
|
scrollStep={({ clientWidth, scrollWidth, scrollLeft, direction }) =>
|
|
110
108
|
direction === 'next'
|
|
111
109
|
? Math.min(clientWidth * 0.8, scrollWidth - clientWidth - scrollLeft)
|
|
112
110
|
: Math.min(clientWidth * 0.8, scrollLeft)
|
|
113
111
|
}
|
|
114
|
-
|
|
112
|
+
>
|
|
113
|
+
{slides}
|
|
114
|
+
</Carousel>
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
戻り値は「進む量の絶対値(px)」。符号(prev / next)はコンポーネント側で付与する。
|
|
@@ -12,10 +12,19 @@
|
|
|
12
12
|
position: relative;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/* フォーカスリングは scroller ではなく viewport に描く。scroller に置くと
|
|
16
|
+
hasGradient の mask(mask-clip: border-box)で外側の box-shadow ごと消えるため。 */
|
|
17
|
+
|
|
18
|
+
.charcoal-carousel__viewport[data-focus-visible] {
|
|
19
|
+
box-shadow: 0 0 0 4px rgba(0, 150, 250, 0.32);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* スライド間隔は所有しない(sandbox 同様、利用者が children 側で注入する)。
|
|
23
|
+
このコンポーネントの責務はスクロールと indicator / arrow によるページ送りのみ。 */
|
|
24
|
+
|
|
15
25
|
.charcoal-carousel__scroller {
|
|
16
26
|
position: relative;
|
|
17
27
|
display: flex;
|
|
18
|
-
gap: 24px;
|
|
19
28
|
overflow-x: auto;
|
|
20
29
|
overflow-y: hidden;
|
|
21
30
|
overscroll-behavior-x: contain;
|
|
@@ -28,14 +37,6 @@
|
|
|
28
37
|
display: none;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
.charcoal-carousel__scroller[data-focus-visible] {
|
|
32
|
-
box-shadow: 0 0 0 4px rgba(0, 150, 250, 0.32);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.charcoal-carousel[data-size='S'] .charcoal-carousel__scroller {
|
|
36
|
-
gap: 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
40
|
/* スクロールスナップ type は prop(scrollSnap.type)で scroller に出し分ける。
|
|
40
41
|
align は基底 .charcoal-carousel__item の後(後述)に置く(no-descending-specificity 回避)。
|
|
41
42
|
未指定時の既定(M=none / S=mandatory / align=center)は JS 側で data 属性に解決する。 */
|
|
@@ -52,38 +53,39 @@
|
|
|
52
53
|
scroll-snap-type: x mandatory;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
/* グラデーション:
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
/* グラデーション: mask で端のアイテム自体を透明へフェードさせる(下の背景色を
|
|
57
|
+
問わない)。mask の色はアルファとしてだけ効く(#000=表示 / transparent=透過)。
|
|
58
|
+
左フェード・右フェードの 2 レイヤーを intersect で合成し、はみ出して
|
|
59
|
+
スクロール可能な側のみ data-can-prev/next で 72px のフェード帯を枠内に置く。
|
|
60
|
+
無効な側はフェード帯ごと枠外へスライドして隠す(mask-image と違い
|
|
61
|
+
mask-position は補間可能なので、切替が 0.2s のフェードになる)。 */
|
|
58
62
|
|
|
59
|
-
.charcoal-carousel[data-has-gradient='true'] .charcoal-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
.charcoal-carousel[data-has-gradient='true'] .charcoal-carousel__scroller {
|
|
64
|
+
mask-image: linear-gradient(to right, transparent, #000 72px),
|
|
65
|
+
linear-gradient(to left, transparent, #000 72px);
|
|
66
|
+
/* 枠外へ 72px スライドしても反対側の端が欠けないよう、レイヤー幅に 72px 足す */
|
|
67
|
+
mask-size: calc(100% + 72px) 100%;
|
|
68
|
+
mask-repeat: no-repeat;
|
|
69
|
+
mask-composite: intersect;
|
|
70
|
+
mask-position: -72px 0,
|
|
71
|
+
0 0;
|
|
72
|
+
/* 0.2s ease-in は react-sandbox の LeftGradient と同じ値 */
|
|
73
|
+
transition: 0.2s ease-in mask-position;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
.charcoal-carousel[data-has-gradient='true'] .charcoal-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
.charcoal-carousel[data-has-gradient='true'][data-can-prev='true'] .charcoal-carousel__scroller {
|
|
77
|
+
mask-position: 0 0,
|
|
78
|
+
0 0;
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
.charcoal-carousel[data-has-gradient='true'] .charcoal-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
.charcoal-carousel[data-has-gradient='true'][data-can-next='true'] .charcoal-carousel__scroller {
|
|
82
|
+
mask-position: -72px 0,
|
|
83
|
+
-72px 0;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
.charcoal-carousel[data-has-gradient='true'][data-can-prev='true'] .charcoal-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.charcoal-carousel[data-has-gradient='true'][data-can-next='true'] .charcoal-carousel__viewport::after {
|
|
86
|
-
opacity: 1;
|
|
86
|
+
.charcoal-carousel[data-has-gradient='true'][data-can-prev='true'][data-can-next='true'] .charcoal-carousel__scroller {
|
|
87
|
+
mask-position: 0 0,
|
|
88
|
+
-72px 0;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
.charcoal-carousel__item {
|
|
@@ -108,7 +110,7 @@
|
|
|
108
110
|
|
|
109
111
|
/* ── Navigation Buttons (charcoal IconButton, variant=Overlay size=S) ── */
|
|
110
112
|
|
|
111
|
-
/*
|
|
113
|
+
/* マスクの透過域と同じ 72px 1fr 72px グリッド。各ボタンは 72px ゾーンの中央に置く。 */
|
|
112
114
|
|
|
113
115
|
.charcoal-carousel__navigation {
|
|
114
116
|
position: absolute;
|
|
@@ -117,8 +119,8 @@
|
|
|
117
119
|
display: grid;
|
|
118
120
|
grid-template-columns: 72px 1fr 72px;
|
|
119
121
|
align-items: center;
|
|
120
|
-
/*
|
|
121
|
-
z-index:
|
|
122
|
+
/* アイテム内のコンテンツより上にボタンを表示する */
|
|
123
|
+
z-index: 1;
|
|
122
124
|
/* sandbox 同様、通常は隠してカルーセル hover 時にフェードイン表示する。 */
|
|
123
125
|
opacity: 0;
|
|
124
126
|
transition: 0.4s opacity;
|
|
@@ -10,12 +10,19 @@
|
|
|
10
10
|
|
|
11
11
|
.charcoal-carousel__viewport {
|
|
12
12
|
position: relative;
|
|
13
|
+
|
|
14
|
+
/* フォーカスリングは scroller ではなく viewport に描く。scroller に置くと
|
|
15
|
+
hasGradient の mask(mask-clip: border-box)で外側の box-shadow ごと消えるため。 */
|
|
16
|
+
&[data-focus-visible] {
|
|
17
|
+
box-shadow: 0 0 0 4px rgba(0, 150, 250, 0.32);
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
/* スライド間隔は所有しない(sandbox 同様、利用者が children 側で注入する)。
|
|
22
|
+
このコンポーネントの責務はスクロールと indicator / arrow によるページ送りのみ。 */
|
|
15
23
|
.charcoal-carousel__scroller {
|
|
16
24
|
position: relative;
|
|
17
25
|
display: flex;
|
|
18
|
-
gap: 24px;
|
|
19
26
|
overflow-x: auto;
|
|
20
27
|
overflow-y: hidden;
|
|
21
28
|
overscroll-behavior-x: contain;
|
|
@@ -26,14 +33,6 @@
|
|
|
26
33
|
&::-webkit-scrollbar {
|
|
27
34
|
display: none;
|
|
28
35
|
}
|
|
29
|
-
|
|
30
|
-
&[data-focus-visible] {
|
|
31
|
-
box-shadow: 0 0 0 4px rgba(0, 150, 250, 0.32);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.charcoal-carousel[data-size='S'] .charcoal-carousel__scroller {
|
|
36
|
-
gap: 0;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/* スクロールスナップ type は prop(scrollSnap.type)で scroller に出し分ける。
|
|
@@ -53,44 +52,46 @@
|
|
|
53
52
|
scroll-snap-type: x mandatory;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
/* グラデーション:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
.charcoal-carousel__viewport::before {
|
|
76
|
-
left: 0;
|
|
77
|
-
background: linear-gradient(to right, #fff, transparent);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.charcoal-carousel[data-has-gradient='true']
|
|
81
|
-
.charcoal-carousel__viewport::after {
|
|
82
|
-
right: 0;
|
|
83
|
-
background: linear-gradient(to left, #fff, transparent);
|
|
55
|
+
/* グラデーション: mask で端のアイテム自体を透明へフェードさせる(下の背景色を
|
|
56
|
+
問わない)。mask の色はアルファとしてだけ効く(#000=表示 / transparent=透過)。
|
|
57
|
+
左フェード・右フェードの 2 レイヤーを intersect で合成し、はみ出して
|
|
58
|
+
スクロール可能な側のみ data-can-prev/next で 72px のフェード帯を枠内に置く。
|
|
59
|
+
無効な側はフェード帯ごと枠外へスライドして隠す(mask-image と違い
|
|
60
|
+
mask-position は補間可能なので、切替が 0.2s のフェードになる)。 */
|
|
61
|
+
.charcoal-carousel[data-has-gradient='true'] .charcoal-carousel__scroller {
|
|
62
|
+
mask-image:
|
|
63
|
+
linear-gradient(to right, transparent, #000 72px),
|
|
64
|
+
linear-gradient(to left, transparent, #000 72px);
|
|
65
|
+
/* 枠外へ 72px スライドしても反対側の端が欠けないよう、レイヤー幅に 72px 足す */
|
|
66
|
+
mask-size: calc(100% + 72px) 100%;
|
|
67
|
+
mask-repeat: no-repeat;
|
|
68
|
+
mask-composite: intersect;
|
|
69
|
+
mask-position:
|
|
70
|
+
-72px 0,
|
|
71
|
+
0 0;
|
|
72
|
+
/* 0.2s ease-in は react-sandbox の LeftGradient と同じ値 */
|
|
73
|
+
transition: 0.2s ease-in mask-position;
|
|
84
74
|
}
|
|
85
75
|
|
|
86
76
|
.charcoal-carousel[data-has-gradient='true'][data-can-prev='true']
|
|
87
|
-
.charcoal-
|
|
88
|
-
|
|
77
|
+
.charcoal-carousel__scroller {
|
|
78
|
+
mask-position:
|
|
79
|
+
0 0,
|
|
80
|
+
0 0;
|
|
89
81
|
}
|
|
90
82
|
|
|
91
83
|
.charcoal-carousel[data-has-gradient='true'][data-can-next='true']
|
|
92
|
-
.charcoal-
|
|
93
|
-
|
|
84
|
+
.charcoal-carousel__scroller {
|
|
85
|
+
mask-position:
|
|
86
|
+
-72px 0,
|
|
87
|
+
-72px 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.charcoal-carousel[data-has-gradient='true'][data-can-prev='true'][data-can-next='true']
|
|
91
|
+
.charcoal-carousel__scroller {
|
|
92
|
+
mask-position:
|
|
93
|
+
0 0,
|
|
94
|
+
-72px 0;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
.charcoal-carousel__item {
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
|
|
115
116
|
/* ── Navigation Buttons (charcoal IconButton, variant=Overlay size=S) ── */
|
|
116
117
|
|
|
117
|
-
/*
|
|
118
|
+
/* マスクの透過域と同じ 72px 1fr 72px グリッド。各ボタンは 72px ゾーンの中央に置く。 */
|
|
118
119
|
.charcoal-carousel__navigation {
|
|
119
120
|
position: absolute;
|
|
120
121
|
inset: 0;
|
|
@@ -122,8 +123,8 @@
|
|
|
122
123
|
display: grid;
|
|
123
124
|
grid-template-columns: 72px 1fr 72px;
|
|
124
125
|
align-items: center;
|
|
125
|
-
/*
|
|
126
|
-
z-index:
|
|
126
|
+
/* アイテム内のコンテンツより上にボタンを表示する */
|
|
127
|
+
z-index: 1;
|
|
127
128
|
/* sandbox 同様、通常は隠してカルーセル hover 時にフェードイン表示する。 */
|
|
128
129
|
opacity: 0;
|
|
129
130
|
transition: 0.4s opacity;
|