このコミットが含まれているのは:
2026-10-27 18:49:58 +09:00
コミット f9f31812a9
22個のファイルの変更、201行の追加、59行の削除
+2 -2
ファイルの表示
@@ -47,7 +47,7 @@ const CountsControls = (
const mode = draft.get ('mode') ?? 'summary'
const columns = selectedCountColumns (resource, draft, definition)
const details = mode === 'details'
const hasNested = details && fieldsForCounts (resource, definition).some (field =>
const nestedFlg = details && fieldsForCounts (resource, definition).some (field =>
columns.includes (field.key) && field.children != null
&& !countsNestedIsScalar (field.children, field.key, draft)
&& field.children.some (child => selectedCountChildren (draft)
@@ -163,7 +163,7 @@ const CountsControls = (
onChange={event => displayThumbnail (event.target.checked)}/>
{label ('columns.thumbnailDisplay')}
</label>}
{hasNested && <label className="inline-flex items-center gap-1 text-sm">
{nestedFlg && <label className="inline-flex items-center gap-1 text-sm">
<input type="checkbox" checked={draft.get ('nested_json') === '1'}
onChange={event => {
update ('nested_json', event.target.checked ? '1' : '0')
+1 -1
ファイルの表示
@@ -87,7 +87,7 @@ const CountsResults = (
case 'thumbnail_base':
return <a href={countsImageURL (value) ?? undefined} target="_blank" rel="noreferrer">
{value}</a>
case 'is_latest':
case 'latest':
return <LocalisedText language={message ('latestValue').language}>
{value}</LocalisedText>
case 'user':
+51
ファイルの表示
@@ -53,6 +53,7 @@ const CountsSelectableTable = (
) => {
const id = useId ()
const root = useRef<HTMLTableElement> (null)
const textCell = useRef<HTMLTableCellElement | null> (null)
const dragging = useRef (false)
const touchArmed = useRef (false)
const touchPress = useRef<{ x: number; y: number
@@ -64,6 +65,15 @@ const CountsSelectableTable = (
const selection = stored?.identity === identity ? stored : null
const layout = useCountsTableLayout ()
const { label, message } = useUITranslation ('counts')
const clearTextSelection = useCallback (() => {
const cell = textCell.current
if (cell == null) return
delete cell.dataset.countsText
const text = window.getSelection ()
if (text != null && cell.contains (text.anchorNode)) text.removeAllRanges ()
textCell.current = null
}, [])
useEffect (() => () => clearTextSelection (), [identity, clearTextSelection])
const bounds = useMemo (() => selection == null ? null : {
top: Math.min (selection.anchor.row, selection.end.row),
bottom: Math.max (selection.anchor.row, selection.end.row),
@@ -198,6 +208,14 @@ const CountsSelectableTable = (
closeMenu ()
}
const copy = (event: ClipboardEvent<HTMLTableElement>) => {
const text = window.getSelection ()
if (textCell.current != null && text != null && !text.isCollapsed
&& textCell.current.contains (text.anchorNode)
&& textCell.current.contains (text.focusNode))
{
event.stopPropagation ()
return
}
if (document.activeElement !== root.current) return
const payload = clipboard ()
if (payload == null) return
@@ -232,6 +250,26 @@ const CountsSelectableTable = (
'[&>tbody>tr>td[data-counts-selected]]:outline-blue-500',
'[&>tbody>tr>td[data-counts-selected]]:outline-offset-[-1px]', className)}
onCopy={copy}
onDoubleClick={event => {
const element = event.target as Element
if (element.closest ('table') !== event.currentTarget
|| element.closest (interactive) != null) return
const cell = element.closest ('td')
if (cell == null) return
event.preventDefault ()
event.stopPropagation ()
finish ()
clearTextSelection ()
setStored (null)
setMenu (null)
textCell.current = cell
cell.dataset.countsText = ''
const range = document.createRange ()
range.selectNodeContents (cell)
const text = window.getSelection ()
text?.removeAllRanges ()
text?.addRange (range)
}}
onClickCapture={event => {
if ((event.target as Element).closest ('table') !== event.currentTarget
|| !suppressClick.current) return
@@ -243,6 +281,12 @@ const CountsSelectableTable = (
const element = event.target as Element
if (element.closest ('table') !== event.currentTarget) return
suppressClick.current = false
if (textCell.current != null && textCell.current.contains (element))
{
event.stopPropagation ()
return
}
clearTextSelection ()
if (element.closest (interactive) != null || event.button !== 0) return
if (element instanceof HTMLElement && element.hasAttribute ('data-counts-scroll'))
{
@@ -300,6 +344,11 @@ const CountsSelectableTable = (
onContextMenu={event => {
const element = event.target as Element
if (element.closest ('table') !== event.currentTarget) return
if (textCell.current != null && textCell.current.contains (element))
{
event.stopPropagation ()
return
}
const point = pointFor (element, event.clientY)
if (point == null || element.closest ('[data-counts-resize]') != null) return
event.preventDefault ()
@@ -321,9 +370,11 @@ const CountsSelectableTable = (
}
if (event.key === 'Escape')
{
clearTextSelection ()
setStored (null)
return
}
if (textCell.current != null) return
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes (event.key)) return
const rows = event.currentTarget.tBodies[0]?.rows
if (rows == null || rows.length === 0) return
+6
ファイルの表示
@@ -2,6 +2,12 @@
@apply h-6;
}
.counts-grid > tbody > tr > td[data-counts-text] {
user-select: text;
-webkit-user-select: text;
cursor: text;
}
.counts-grid > tbody > tr:nth-child(odd) > td:not([data-counts-selected]) {
@apply bg-white text-slate-900;
}