Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React, { useEffect, useState } from 'react'
import { Select } from '@Pimcore/components/select/select'
import { useDynamicFilter } from '@Pimcore/components/dynamic-filter/provider/use-dynamic-filter'
import { type DefaultOptionType } from 'antd/es/select'
import { useFocusRestore } from '@Pimcore/modules/element/listing/decorators/general-filters/view-layer/components/sidebar/tabs/filters/focus-context'

interface IObjectSelectConfig {
fieldDefinition: {
Expand Down Expand Up @@ -41,6 +42,7 @@ const boolToNum = (value: boolean | null): number => {

export const DynamicTypeFieldFilterBooleanSelectComponent = (): React.JSX.Element => {
const { setData, data, config: rawConfig } = useDynamicFilter()
const { restoreFocus } = useFocusRestore()
const config: IAssetSelectConfig | IObjectSelectConfig = rawConfig

const [_value, setValue] = useState<number[]>([])
Expand Down Expand Up @@ -75,6 +77,7 @@ export const DynamicTypeFieldFilterBooleanSelectComponent = (): React.JSX.Elemen
<Select
mode="multiple"
onChange={ handleChange }
onDropdownVisibleChange={ (open: boolean) => { if (!open) restoreFocus() } }
options={ formattedOptions }
style={ { width: '100%' } }
value={ _value }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { DateRangePicker } from '@Pimcore/components/date-picker/date-range-pick
import { useDynamicFilter } from '@Pimcore/components/dynamic-filter/provider/use-dynamic-filter'
import { t } from 'i18next'
import { type AbstractFieldFilterDefinition } from '../dynamic-type-field-filter-abstract'
import { useFocusRestore } from '@Pimcore/modules/element/listing/decorators/general-filters/view-layer/components/sidebar/tabs/filters/focus-context'

enum DatePickerSettingValue {
ON = 'on',
Expand All @@ -38,6 +39,7 @@ export const DynamicTypeFieldFilterDateComponent = (props: DynamicTypeFieldFilte
}

const { data: rawData, setData } = useDynamicFilter()
const { restoreFocus } = useFocusRestore()

const data: DateValue = rawData ?? {
setting: DatePickerSettingValue.ON,
Expand Down Expand Up @@ -105,10 +107,9 @@ export const DynamicTypeFieldFilterDateComponent = (props: DynamicTypeFieldFilte

const handleDateChange = (field: 'on' | 'from' | 'to', value: string | null): void => {
setData({
...data,
setting: currentSetting,
from: (field === 'from') ? value : null,
to: (field === 'to') ? value : null,
on: (field === 'on') ? value : null
[field]: value
})
}

Expand Down Expand Up @@ -152,6 +153,7 @@ export const DynamicTypeFieldFilterDateComponent = (props: DynamicTypeFieldFilte

handleDateRangeChange(convertValueToISOFormat(newFrom), convertValueToISOFormat(newTo))
} }
onOpenChange={ (open: boolean) => { if (!open) restoreFocus() } }
outputType="timestamp"
value={ [convertISOToTimestamp(data?.from ?? null), convertISOToTimestamp(data?.to ?? null)] }
/>
Expand All @@ -172,6 +174,7 @@ export const DynamicTypeFieldFilterDateComponent = (props: DynamicTypeFieldFilte
handleDateChange('from', convertedValue)
}
} }
onOpenChange={ (open: boolean) => { if (!open) restoreFocus() } }
outputType="timestamp"
value={ convertISOToTimestamp(getDatePickerValue()) }
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React, { useEffect, useState } from 'react'
import { Select } from '@Pimcore/components/select/select'
import { useDynamicFilter } from '@Pimcore/components/dynamic-filter/provider/use-dynamic-filter'
import { type DefaultOptionType } from 'antd/es/select'
import { useFocusRestore } from '@Pimcore/modules/element/listing/decorators/general-filters/view-layer/components/sidebar/tabs/filters/focus-context'

interface IObjectSelectConfig {
fieldDefinition: {
Expand All @@ -25,6 +26,7 @@ interface IAssetSelectConfig {

export const DynamicTypeFieldFilterMultiselectComponent = (): React.JSX.Element => {
const { setData, data, config: rawConfig } = useDynamicFilter()
const { restoreFocus } = useFocusRestore()
const [_value, setValue] = useState<string[]>(data as string[])

const config: IAssetSelectConfig | IObjectSelectConfig = rawConfig
Expand Down Expand Up @@ -55,6 +57,7 @@ export const DynamicTypeFieldFilterMultiselectComponent = (): React.JSX.Element
<Select
mode="multiple"
onChange={ handleChange }
onDropdownVisibleChange={ (open: boolean) => { if (!open) restoreFocus() } }
options={ formattedOptions }
showSearch={ rawConfig?.showSearch ?? false }
style={ { width: '100%' } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ export const DynamicTypeFieldFilterTextAreaComponent = (): React.JSX.Element =>

return (
<TextArea
onBlur={ onBlur }
onChange={ (event) => { setValue(event.target.value) } }
onChange={ (event) => {
const newValue = event.target.value
setData(newValue)
} }
value={ _value }
/>
)

function onBlur (): void {
setData(_value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@ export const DynamicTypeFieldFilterTextComponent = (): React.JSX.Element => {

return (
<Input
onBlur={ onBlur }
onChange={ (event) => { setValue(event.target.value) } }
onChange={ (event) => {
const newValue = event.target.value
setData(newValue)
} }
type='text'
value={ _value }
/>
)

function onBlur (): void {
setData(_value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @license Pimcore Open Core License (POCL)
*/

import React, { useEffect, useMemo, useState } from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useDynamicTypeResolver } from '@Pimcore/modules/element/dynamic-types/resolver/hooks/use-dynamic-type-resolver'
import { Space } from 'antd'
import { useTranslation } from 'react-i18next'
Expand All @@ -20,6 +20,7 @@ import { type AvailableColumn } from '@Pimcore/modules/element/listing/decorator
import { FieldFilters, type FieldFiltersProps } from '@Pimcore/components/field-filters/field-filters'
import { useFilter } from '../provider/filter-provider/use-filter'
import { type DynamicTypeFieldFilterAbstract } from '@sdk/modules/element'
import { useFocusRestore } from '../focus-context'

const FILTER_FIELD_KEY_IGNORE_LIST = ['size']

Expand All @@ -28,6 +29,7 @@ export const FieldFiltersContainer = (): React.JSX.Element => {
const { availableColumns } = useAvailableColumns()
const { getType } = useDynamicTypeResolver()
const { fieldFilters, setFieldFilters } = useFilter()
const { restoreFocus } = useFocusRestore()

const initialFilters: FieldFiltersProps['data'] = useMemo(() => fieldFilters.map((filter) => {
const currentColumn = availableColumns.find((column) => column.key === filter.key)
Expand All @@ -46,6 +48,7 @@ export const FieldFiltersContainer = (): React.JSX.Element => {
}), [fieldFilters, availableColumns])

const [filters, setFilters] = useState<FieldFiltersProps['data']>(initialFilters)
const previousFilterCount = useRef<number>(filters.length)

const onFilterChange: FieldFiltersProps['onChange'] = (data) => {
setFilters(data)
Expand All @@ -62,6 +65,13 @@ export const FieldFiltersContainer = (): React.JSX.Element => {
setFilters(initialFilters)
}, [initialFilters])

useEffect(() => {
if (filters.length > previousFilterCount.current) {
restoreFocus()
}
previousFilterCount.current = filters.length
}, [filters.length, restoreFocus])

const handleColumnClick = (column: AvailableColumn): void => {
const objectDataByFrontendType = getType({ target: 'FIELD_FILTER', dynamicTypeIds: [column.frontendType!] })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @license Pimcore Open Core License (POCL)
*/

import React, { useState } from 'react'
import React, { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { IconTextButton } from '@Pimcore/components/icon-text-button/icon-text-button'
import { Title } from '@Pimcore/components/title/title'
Expand Down Expand Up @@ -36,9 +36,11 @@ import { useSearchTermFilter } from '../../../../../context-layer/provider/searc
import { useGeneralFiltersConfig } from '../../../../../context-layer/provider/general-filters-config/use-general-filters-config'
import { SearchTermFilter } from '../../../search/search-term-filter'
import { useData } from '@Pimcore/modules/element/listing/abstract/data-layer/provider/data/use-data'
import { FocusProvider } from './focus-context'

export const FilterContainerInner = (): React.JSX.Element => {
const [isAdvancedMode, setIsAdvancedMode] = useState<boolean>(false)
const contentRef = useRef<HTMLDivElement>(null)
const { setPage } = usePaging()
const { setFieldFilters: setListingFieldFilters } = useFieldFilters()
const { setOnlyDirectChildren: setListingOnlyDirectChildren } = useDirectChildrenFilter()
Expand Down Expand Up @@ -83,6 +85,17 @@ export const FilterContainerInner = (): React.JSX.Element => {
}
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLOrSVGElement>): void => {
if (event.key === 'Enter') {
event.preventDefault()
handleApplyClick()
}
}

const restoreFocus = (): void => {
contentRef.current?.focus()
}

return (
<ContentLayout
renderToolbar={
Expand All @@ -104,67 +117,77 @@ export const FilterContainerInner = (): React.JSX.Element => {
</Toolbar>
}
>
<Content padded>
<Flex
align='center'
justify='space-between'
<div
ref={ contentRef }
style={ { outline: 'none' } }
>
<Content
onKeyDown={ handleKeyDown }
padded
>
<Title>{t('sidebar.search_filter')}</Title>
<Flex gap='extra-small'>
<Text>{t('toggle.advanced-mode')}</Text>
<Switch
checked={ isAdvancedMode }
onChange={ () => {
setIsAdvancedMode(!isAdvancedMode)
} }
/>
</Flex>
</Flex>

{isAdvancedMode
? (
<PQLQueryInput
handleBlur={ (e) => { setPqlQuery(e.target.value) } }
handleChange={ (e) => { setPqlQuery(e.target.value) } }
isShowError={ false }
value={ pqlQuery }
/>
)
: (
<>
<Form>
<Space
direction='vertical'
style={ { width: '100%' } }
>
{handleSearchTermInSidebar && (
<SearchTermFilter />
)}

<Checkbox
checked={ onlyDirectChildren }
onChange={ (e) => { setOnlyDirectChildren(e.target.checked) } }
>
{t('element.sidebar.filter.only-direct-children')}
</Checkbox>

{/* <Checkbox */}
{/* checked={ false } */}
{/* value={ 'referenced' } */}
{/* > */}
{/* only unreferenced */}
{/* </Checkbox> */}
</Space>
</Form>

<Title>
{t('element.sidebar.field-filters')}
</Title>

<FieldFiltersContainer />
</>
)}
</Content>
<FocusProvider restoreFocus={ restoreFocus }>
<Flex
align='center'
justify='space-between'
>
<Title>{t('sidebar.search_filter')}</Title>
<Flex gap='extra-small'>
<Text>{t('toggle.advanced-mode')}</Text>
<Switch
checked={ isAdvancedMode }
onChange={ () => {
setIsAdvancedMode(!isAdvancedMode)
} }
/>
</Flex>
</Flex>

{isAdvancedMode
? (
<PQLQueryInput
handleBlur={ (e) => { setPqlQuery(e.target.value) } }
handleChange={ (e) => { setPqlQuery(e.target.value) } }
isShowError={ false }
value={ pqlQuery }
/>
)
: (
<>
<Form>
<Space
direction='vertical'
style={ { width: '100%' } }
>
{handleSearchTermInSidebar && (
<SearchTermFilter />
)}

<Checkbox
checked={ onlyDirectChildren }
onChange={ (e) => { setOnlyDirectChildren(e.target.checked) } }
>
{t('element.sidebar.filter.only-direct-children')}
</Checkbox>

{/* <Checkbox */}
{/* checked={ false } */}
{/* value={ 'referenced' } */}
{/* > */}
{/* only unreferenced */}
{/* </Checkbox> */}
</Space>
</Form>

<Title>
{t('element.sidebar.field-filters')}
</Title>

<FieldFiltersContainer />
</>
)}
</FocusProvider>
</Content>
</div>
</ContentLayout>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import React, { createContext, useContext, useMemo } from 'react'

interface FocusContextType {
restoreFocus: () => void
}

const FocusContext = createContext<FocusContextType | undefined>(undefined)

export const useFocusRestore = (): FocusContextType => {
const context = useContext(FocusContext)
if (context === undefined) {
throw new Error('useFocusRestore must be used within a FocusProvider')
}
return context
}

export interface FocusProviderProps {
children: React.ReactNode
restoreFocus: () => void
}

export const FocusProvider = ({ children, restoreFocus }: FocusProviderProps): React.JSX.Element => {
const contextValue = useMemo(() => ({ restoreFocus }), [restoreFocus])

return (
<FocusContext.Provider value={ contextValue }>
{children}
</FocusContext.Provider>
)
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading