このコミットが含まれているのは:
@@ -1,19 +1,33 @@
|
||||
import { countChildPaths, fieldsForCounts, selectedCountChildren,
|
||||
selectedCountColumns } from '@/lib/counts'
|
||||
import { DndContext, MouseSensor, TouchSensor, closestCenter,
|
||||
useDraggable, useDroppable, useSensor, useSensors } from '@dnd-kit/core'
|
||||
|
||||
import { countChildPaths, fieldsForCounts, orderedCountFields, reorderCountFields,
|
||||
selectedCountChildren, selectedCountColumns } from '@/lib/counts'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
|
||||
import type { CountField, CountsResource } from '@/lib/counts'
|
||||
|
||||
type BranchProps = {
|
||||
field: CountField
|
||||
path: string
|
||||
enabled: boolean
|
||||
selected: Set<string>
|
||||
params: URLSearchParams
|
||||
select: (path: string, checked: boolean) => void
|
||||
update: (key: string, value: string) => void
|
||||
move: (from: string, to: string) => void
|
||||
previous?: string
|
||||
next?: string }
|
||||
|
||||
|
||||
const FieldBranch = (
|
||||
{ field, path, enabled, selected, select }: {
|
||||
field: CountField
|
||||
path: string
|
||||
enabled: boolean
|
||||
selected: Set<string>
|
||||
select: (path: string, checked: boolean) => void },
|
||||
{ field, path, enabled, selected, params, select, update, move, previous, next }: BranchProps,
|
||||
) => {
|
||||
const { label } = useUITranslation ('counts')
|
||||
const { label, message } = useUITranslation ('counts')
|
||||
const fixed = path === 'thumbnail'
|
||||
const drag = useDraggable ({ id: field.key, disabled: fixed })
|
||||
const drop = useDroppable ({ id: field.key, disabled: fixed })
|
||||
const checked = selected.has (path)
|
||||
const control = <label className="inline-flex items-center gap-1">
|
||||
<input type="checkbox" checked={checked} disabled={!enabled}
|
||||
@@ -21,15 +35,79 @@ const FieldBranch = (
|
||||
onChange={event => select (path, event.target.checked)}/>
|
||||
{label (field.label)}
|
||||
</label>
|
||||
return field.children ? (
|
||||
<details className="my-1">
|
||||
<summary className="cursor-pointer">{control}</summary>
|
||||
<div className="ml-4 border-l pl-2 dark:border-slate-700">
|
||||
{field.children.map (child => (
|
||||
<FieldBranch key={child.key} field={child} path={`${path}.${child.key}`}
|
||||
enabled={enabled && checked} selected={selected} select={select}/>))}
|
||||
return (
|
||||
<div ref={drop.setNodeRef} className={cn ('flex items-start gap-1',
|
||||
drop.isOver && 'outline outline-1 outline-blue-500 dark:outline-blue-400')}>
|
||||
<div ref={drag.setNodeRef} className="flex min-w-0 flex-1 items-start gap-1"
|
||||
style={drag.transform == null ? undefined : {
|
||||
position: 'relative', zIndex: 1,
|
||||
transform: `translate3d(${drag.transform.x}px, ${drag.transform.y}px, 0)` }}>
|
||||
{fixed ? <span className="w-6 shrink-0"/> : (
|
||||
<button type="button" ref={drag.setActivatorNodeRef}
|
||||
className="mt-1 w-6 shrink-0 cursor-grab touch-none active:cursor-grabbing"
|
||||
{...drag.attributes} {...drag.listeners}
|
||||
aria-keyshortcuts="ArrowUp ArrowDown"
|
||||
aria-label={message ('reorder', { column: message (field.label).text }).text}
|
||||
lang={message ('reorder').language}
|
||||
onClick={event => event.stopPropagation ()}
|
||||
onKeyDown={event => {
|
||||
if (!['ArrowUp', 'ArrowDown'].includes (event.key)) return
|
||||
event.preventDefault ()
|
||||
event.stopPropagation ()
|
||||
const target = event.key === 'ArrowUp' ? previous : next
|
||||
if (target != null) move (field.key, target)
|
||||
}}><span aria-hidden>⠿</span></button>)}
|
||||
{field.children ? (
|
||||
<details className="my-1 min-w-0 flex-1">
|
||||
<summary className="cursor-pointer">{control}</summary>
|
||||
<div className="ml-4 border-l pl-2 dark:border-slate-700">
|
||||
<FieldGroup fields={field.children} path={path} params={params}
|
||||
enabled={enabled && checked} selected={selected} select={select}
|
||||
update={update}/>
|
||||
</div>
|
||||
</details>) : <div className="my-1">{control}</div>}
|
||||
</div>
|
||||
</details>) : <div className="my-1">{control}</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
|
||||
const FieldGroup = (
|
||||
{ fields, path, params, enabled, selected, select, update }: {
|
||||
fields: CountField[]
|
||||
path: string
|
||||
params: URLSearchParams
|
||||
enabled: boolean
|
||||
selected: Set<string>
|
||||
select: BranchProps['select']
|
||||
update: BranchProps['update'] },
|
||||
) => {
|
||||
const { message } = useUITranslation ('counts')
|
||||
const ordered = orderedCountFields (fields, params, path)
|
||||
const movable = ordered.filter (field => path !== '' || field.key !== 'thumbnail')
|
||||
const sensors = useSensors (
|
||||
useSensor (MouseSensor, { activationConstraint: { distance: 4 } }),
|
||||
useSensor (TouchSensor, { activationConstraint: { delay: 350, tolerance: 8 } }))
|
||||
const move = (from: string, to: string) =>
|
||||
update ('order', reorderCountFields (fields, params, path, from, to))
|
||||
return (
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter}
|
||||
accessibility={{ announcements: {
|
||||
onDragStart: () => undefined, onDragOver: () => undefined,
|
||||
onDragMove: () => undefined,
|
||||
onDragEnd: () => undefined, onDragCancel: () => undefined },
|
||||
screenReaderInstructions: { draggable: message ('output').text } }}
|
||||
onDragEnd={({ active, over }) => {
|
||||
if (over != null && active.id !== over.id) move (String (active.id), String (over.id))
|
||||
}}>
|
||||
{ordered.map (field => {
|
||||
const position = movable.findIndex (item => item.key === field.key)
|
||||
return <FieldBranch key={field.key} field={field}
|
||||
path={path ? `${path}.${field.key}` : field.key}
|
||||
enabled={enabled} selected={selected} params={params}
|
||||
select={select} update={update} move={move}
|
||||
previous={movable[position - 1]?.key} next={movable[position + 1]?.key}/>
|
||||
})}
|
||||
</DndContext>)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +139,8 @@ const CountsFieldTree = (
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{fields.map (field => (
|
||||
<FieldBranch key={field.key} field={field} path={field.key}
|
||||
enabled selected={selected} select={select}/>))}
|
||||
<FieldGroup fields={fields} path="" params={params} enabled selected={selected}
|
||||
select={select} update={update}/>
|
||||
<p className="mt-2 text-xs">{label ('nested.selection')}</p>
|
||||
</>)
|
||||
}
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする