@@ -5,7 +5,7 @@ import DualListBox, {
5
5
DualListBoxItem ,
6
6
DualListBoxProps ,
7
7
} from "./DualListBox" ;
8
- import { ActionButton , Flex , Icon , Typography } from ".." ;
8
+ import { ActionButton , Flex , Icon , Spacer , Typography } from ".." ;
9
9
import { useTheme } from "../../themes" ;
10
10
11
11
export default {
@@ -174,9 +174,8 @@ export const Nested: StoryObj<DualListBoxProps> = {
174
174
} ;
175
175
176
176
/**
177
- * props に onAdd / onRemove を渡さない
178
177
* 規定の Checkbox による選択・選択解除ではなく
179
- * DualListBoxItem に Button を配置して選択 ・選択解除を行う
178
+ * DualListBoxItem に配置した Button による選択 ・選択解除を行う
180
179
*/
181
180
export const WithoutCheckbox : StoryObj < DualListBoxProps > = {
182
181
render : ( ) => {
@@ -200,71 +199,115 @@ export const WithoutCheckbox: StoryObj<DualListBoxProps> = {
200
199
[ ] ,
201
200
) ;
202
201
203
- const [ selectedIds , setSelectedIds ] = React . useState < string [ ] > ( [
204
- items [ 2 ] . id ,
205
- ] ) ;
202
+ const [ allowedIds , setAllowedIds ] = React . useState < string [ ] > ( [ items [ 2 ] . id ] ) ;
206
203
207
- const handleAdd = ( id : string ) => {
208
- setSelectedIds ( ( prevState ) => {
204
+ const [ disallowedIds , setDisallowedIds ] = React . useState < string [ ] > ( [ ] ) ;
205
+
206
+ const handleAllow = ( id : string ) => {
207
+ setAllowedIds ( ( prevState ) => {
209
208
if ( prevState . includes ( id ) ) {
210
209
return prevState ;
211
210
}
212
211
return [ ...prevState , id ] ;
213
212
} ) ;
214
213
} ;
215
214
216
- const handleRemove = ( id : string ) => {
217
- setSelectedIds ( ( prevState ) =>
218
- prevState . filter ( ( selectedId ) => selectedId !== id ) ,
215
+ const handleDisallow = ( id : string ) => {
216
+ setDisallowedIds ( ( prevState ) => {
217
+ if ( prevState . includes ( id ) ) {
218
+ return prevState ;
219
+ }
220
+ return [ ...prevState , id ] ;
221
+ } ) ;
222
+ } ;
223
+
224
+ const handleRemove = ( item : DualListBoxItem ) => {
225
+ setAllowedIds ( ( prevState ) =>
226
+ prevState . filter ( ( selectedId ) => selectedId !== item . id ) ,
227
+ ) ;
228
+ setDisallowedIds ( ( prevState ) =>
229
+ prevState . filter ( ( selectedId ) => selectedId !== item . id ) ,
219
230
) ;
220
231
} ;
221
232
222
233
const candidateItems = React . useMemo (
223
234
( ) =>
224
235
items
225
- . filter ( ( item ) => ! selectedIds . includes ( item . id ) )
236
+ . filter (
237
+ ( item ) =>
238
+ ! allowedIds . includes ( item . id ) && ! disallowedIds . includes ( item . id ) ,
239
+ )
226
240
. map ( ( item ) => ( {
227
241
id : item . id ,
228
242
content : (
229
- < Flex alignItems = "center" display = "flex" gap = { 1 } >
243
+ < Flex alignItems = "center" display = "flex" flex = { 1 } gap = { 1 } >
230
244
< Typography > { item . content } </ Typography >
231
- < ActionButton
232
- color = "primary"
233
- onClick = { ( ) => handleAdd ( item . id ) }
245
+ < Flex
246
+ alignItems = "center"
247
+ display = "flex"
248
+ flex = { 1 }
249
+ justifyContent = "flex-end"
250
+ gap = { 1 }
234
251
>
235
- < Icon color = "active" name = "check_thin" />
236
- </ ActionButton >
252
+ < ActionButton
253
+ color = "primary"
254
+ onClick = { ( ) => handleAllow ( item . id ) }
255
+ >
256
+ < Icon color = "active" name = "check_thin" />
257
+ </ ActionButton >
258
+ < ActionButton
259
+ color = "warning"
260
+ onClick = { ( ) => handleDisallow ( item . id ) }
261
+ >
262
+ < Icon color = { theme . palette . danger . main } name = "forbid" />
263
+ </ ActionButton >
264
+ </ Flex >
237
265
</ Flex >
238
266
) ,
239
267
} ) ) ,
240
- [ items , selectedIds ] ,
268
+ [ allowedIds , disallowedIds , items , theme . palette . danger . main ] ,
241
269
) ;
242
270
243
271
const selectedItems = React . useMemo (
244
272
( ) =>
245
273
items
246
- . filter ( ( item ) => selectedIds . includes ( item . id ) )
274
+ . filter (
275
+ ( item ) =>
276
+ allowedIds . includes ( item . id ) || disallowedIds . includes ( item . id ) ,
277
+ )
247
278
. map ( ( item ) => ( {
248
279
id : item . id ,
249
280
content : (
250
- < Flex alignItems = "center" display = "flex" gap = { 1 } >
281
+ < Flex alignItems = "center" display = "flex" flex = { 1 } gap = { 1 } >
251
282
< Typography > { item . content } </ Typography >
252
- < ActionButton
253
- color = "warning"
254
- onClick = { ( ) => handleRemove ( item . id ) }
283
+ < Flex
284
+ alignItems = "center"
285
+ display = "flex"
286
+ flex = { 1 }
287
+ justifyContent = "flex-end"
288
+ gap = { 1 }
255
289
>
256
- < Icon color = { theme . palette . danger . main } name = "delete_bin" />
257
- </ ActionButton >
290
+ < Spacer pr = { 2 } >
291
+ { allowedIds . includes ( item . id ) && (
292
+ < Icon color = "active" name = "check_thin" />
293
+ ) }
294
+ { disallowedIds . includes ( item . id ) && (
295
+ < Icon color = { theme . palette . danger . main } name = "forbid" />
296
+ ) }
297
+ </ Spacer >
298
+ </ Flex >
258
299
</ Flex >
259
300
) ,
260
301
} ) ) ,
261
- [ items , selectedIds , theme . palette . danger . main ] ,
302
+ [ allowedIds , disallowedIds , items , theme . palette . danger . main ] ,
262
303
) ;
263
304
264
305
return (
265
306
< DualListBox
266
307
candidateItems = { candidateItems }
308
+ disableCheckbox = { true }
267
309
selectedItems = { selectedItems }
310
+ onRemove = { handleRemove }
268
311
/>
269
312
) ;
270
313
} ,
0 commit comments