このコミットが含まれているのは:
@@ -40,7 +40,7 @@ const ConditionEditor = (
|
||||
controls.setActions ([{ label: label (initial == null ? 'add' : 'shared:actions.edit'),
|
||||
onSelect: () => save (condition) }])
|
||||
}, [controls, condition, initial, label, save])
|
||||
return <div className="space-y-5 px-1 py-3 md:px-3">
|
||||
return <div className="space-y-2">
|
||||
<select value={field} aria-label={message ('field').text}
|
||||
className={inputClass (false, 'h-11 w-full')}
|
||||
onChange={event => {
|
||||
@@ -115,7 +115,6 @@ const CountsAdditionalConditions = ({ params, update }: {
|
||||
else replace (index, condition)
|
||||
}
|
||||
void dialogue.form ({ title: label ('additionalConditions'), footerLayout: 'inline',
|
||||
size: 'large',
|
||||
body: controls => <ConditionEditor definition={definition}
|
||||
initial={initial} controls={controls} save={save}/> })
|
||||
}
|
||||
|
||||
@@ -180,9 +180,11 @@ const CountsControls = (
|
||||
</label>}
|
||||
</fieldset>
|
||||
<AnimatedCollapse id={id} open={open}>
|
||||
<div className="grid gap-3 border-b py-2 dark:border-slate-700 md:grid-cols-2">
|
||||
<div className="max-h-[20dvh] min-h-0 min-w-0 overflow-y-auto overscroll-contain
|
||||
md:max-h-[40dvh]" style={{ direction: 'rtl' }}>
|
||||
<div className="grid max-h-[40dvh] auto-rows-max gap-3 overflow-y-auto overscroll-contain
|
||||
border-b py-2 [direction:ltr] dark:border-slate-700 md:max-h-none md:auto-rows-auto
|
||||
md:grid-cols-2 md:overflow-visible">
|
||||
<div className="min-h-0 min-w-0 md:max-h-[40dvh] md:overflow-y-auto
|
||||
md:overscroll-contain md:[direction:rtl]">
|
||||
<fieldset className="min-w-0" data-counts-output style={{ direction: 'ltr' }}>
|
||||
{mode === 'summary' ? <CountsAxes params={draft} update={update}/>
|
||||
: <>
|
||||
@@ -194,8 +196,9 @@ const CountsControls = (
|
||||
</>}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div className="max-h-[20dvh] min-h-0 min-w-0 overflow-y-auto overscroll-contain
|
||||
md:max-h-[40dvh]" style={{ direction: 'ltr' }}>
|
||||
<div className="min-h-0 min-w-0 border-t pt-2 dark:border-slate-700
|
||||
md:max-h-[40dvh] md:overflow-y-auto md:overscroll-contain md:border-t-0 md:pt-0"
|
||||
style={{ direction: 'ltr' }}>
|
||||
<fieldset className="min-w-0 space-y-2" data-counts-filters>
|
||||
<legend className="font-semibold">{label ('filters')}</legend>
|
||||
{history && <div className="flex min-w-0 max-w-full items-center gap-1">
|
||||
|
||||
@@ -47,9 +47,15 @@ const CountsNestedTable = (
|
||||
{String (value)}</PrefetchLink>
|
||||
}
|
||||
const display = countsLogicalName (row, field, locale)
|
||||
const nameLocale = fields.find (item => item.key === field)?.nameLocale
|
||||
const metadata = fields.find (item => item.key === field)
|
||||
const nameLocale = metadata?.nameLocale
|
||||
if (nameLocale != null)
|
||||
return <LocalisedText language={nameLocale.languageCode}>{display.text}</LocalisedText>
|
||||
{
|
||||
if (metadata?.multipleValues)
|
||||
return <span lang={nameLocale.code} className="whitespace-pre-line">{display.text}</span>
|
||||
return <LocalisedText language={nameLocale.languageCode}
|
||||
className="whitespace-pre-line">{display.text}</LocalisedText>
|
||||
}
|
||||
if (typeof value === 'boolean')
|
||||
return <span aria-label={display.text} lang={display.language}>{value ? '✓' : '✗'}</span>
|
||||
if (display.language != null)
|
||||
@@ -59,7 +65,8 @@ const CountsNestedTable = (
|
||||
return display.text
|
||||
}
|
||||
if (countsNestedIsScalar (fields, path, params))
|
||||
return <span>{countsNestedScalarText (rows, fields, path, params, locale)}</span>
|
||||
return <span className="whitespace-pre-line">
|
||||
{countsNestedScalarText (rows, fields, path, params, locale)}</span>
|
||||
return (
|
||||
<div className="counts-nested w-full min-w-0 max-w-full">
|
||||
{params.get ('nested_json') === '1' ? (
|
||||
|
||||
@@ -48,7 +48,12 @@ const CountsResults = (
|
||||
title={message (field.label, field.labelValues)} params={params} update={update}/>
|
||||
}
|
||||
if (field.nameLocale != null)
|
||||
return <LocalisedText language={field.nameLocale.languageCode}>{value}</LocalisedText>
|
||||
{
|
||||
if (field.multipleValues)
|
||||
return <span lang={field.nameLocale.code} className="whitespace-pre-line">{value}</span>
|
||||
return <LocalisedText language={field.nameLocale.languageCode}
|
||||
className="whitespace-pre-line">{value}</LocalisedText>
|
||||
}
|
||||
switch (column)
|
||||
{
|
||||
case 'resource_id':
|
||||
|
||||
@@ -44,6 +44,44 @@ const cellAt = (table: HTMLTableElement, point: Point) =>
|
||||
cellGrid (table)[point.row]?.[point.column]
|
||||
|
||||
|
||||
const scrollToCell = (table: HTMLTableElement, point: Point, previous: Point) => {
|
||||
const cell = cellAt (table, point)
|
||||
const container = table.parentElement?.closest<HTMLElement> (
|
||||
'[data-counts-scroll],[data-counts-results]')
|
||||
if (cell == null || container == null) return
|
||||
const viewport = container.getBoundingClientRect ()
|
||||
const top = viewport.top + container.clientTop
|
||||
const left = viewport.left + container.clientLeft
|
||||
const bottom = top + container.clientHeight
|
||||
const right = left + container.clientWidth
|
||||
const header = table.tHead?.getBoundingClientRect ()
|
||||
const visibleTop = header == null ? top : Math.max (top, Math.min (bottom, header.bottom))
|
||||
const rect = cell.getBoundingClientRect ()
|
||||
const lastRow = (table.tBodies[0]?.rows.length ?? 1) - 1
|
||||
const verticalEdge = (point.row === 0 || point.row < previous.row) ? 'start'
|
||||
: ((point.row === lastRow || point.row > previous.row) ? 'end'
|
||||
: (Math.abs (rect.top - visibleTop) <= Math.abs (rect.bottom - bottom) ? 'start' : 'end'))
|
||||
const offset = (
|
||||
start: number, end: number, minimum: number, maximum: number,
|
||||
edge?: 'start' | 'end',
|
||||
) => {
|
||||
if (maximum <= minimum) return 0
|
||||
const oversized = end - start > maximum - minimum
|
||||
// Vertical merged cells follow the destination and movement direction.
|
||||
// Horizontal scrolling retains its existing nearest-edge behaviour.
|
||||
if (oversized && edge != null) return edge === 'start' ? start - minimum : end - maximum
|
||||
if (start < minimum && end > maximum) return 0
|
||||
if (start < minimum) return oversized ? end - maximum : start - minimum
|
||||
if (end > maximum) return oversized ? start - minimum : end - maximum
|
||||
return 0
|
||||
}
|
||||
const vertical = offset (rect.top, rect.bottom, visibleTop, bottom, verticalEdge)
|
||||
const horizontal = offset (rect.left, rect.right, left, right)
|
||||
if (vertical !== 0) container.scrollTop += vertical
|
||||
if (horizontal !== 0) container.scrollLeft += horizontal
|
||||
}
|
||||
|
||||
|
||||
const CountsSelectableTable = (
|
||||
{ family, columns, identity, children, className, name, copyTable, stretched = false }: {
|
||||
family: string
|
||||
@@ -74,6 +112,7 @@ const CountsSelectableTable = (
|
||||
const { label, message } = useUITranslation ('counts')
|
||||
const clearTextSelection = useCallback (() => {
|
||||
setTextMode (null)
|
||||
setCaret (null)
|
||||
}, [])
|
||||
useLayoutEffect (() => {
|
||||
if (textMode == null) return
|
||||
@@ -488,12 +527,18 @@ const CountsSelectableTable = (
|
||||
}
|
||||
return
|
||||
}
|
||||
if (event.key === 'Escape')
|
||||
if (event.key === 'Escape' || (event.key === 'Enter' && textCell != null))
|
||||
{
|
||||
const textModeFlg = textCell != null
|
||||
clearTextSelection ()
|
||||
if (!textModeFlg) setStored (null)
|
||||
if (textModeFlg)
|
||||
{
|
||||
window.getSelection ()?.removeAllRanges ()
|
||||
event.currentTarget.focus ({ preventScroll: true })
|
||||
}
|
||||
else setStored (null)
|
||||
event.preventDefault ()
|
||||
event.stopPropagation ()
|
||||
return
|
||||
}
|
||||
if (event.key === 'Enter' && textCell == null && selection != null)
|
||||
@@ -567,8 +612,7 @@ const CountsSelectableTable = (
|
||||
+ (key === 'ArrowRight' ? 1 : (key === 'ArrowLeft' ? -1 : 0)))) }
|
||||
setStored ({ identity, anchor: event.shiftKey && selection != null
|
||||
? selection.anchor : point, end: point })
|
||||
cellAt (event.currentTarget, point)?.scrollIntoView ({
|
||||
block: 'nearest', inline: 'nearest' })
|
||||
scrollToCell (event.currentTarget, point, previous)
|
||||
}}>
|
||||
<caption className="sr-only">
|
||||
<span id={`${id}-hint`} className="sr-only">{label ('selection.hint')}</span>
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする