@brilo/base-blocks 0.1.36 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/.tsbuildinfo +1 -1
- package/build/blocks/event/event-calendar-group/constants.d.ts +2 -0
- package/build/blocks/event/event-calendar-group/constants.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/constants.js +2 -0
- package/build/blocks/event/event-calendar-group/edit.d.ts +6 -0
- package/build/blocks/event/event-calendar-group/edit.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/edit.js +18 -0
- package/build/blocks/event/event-calendar-group/factory.d.ts +5 -0
- package/build/blocks/event/event-calendar-group/factory.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/factory.js +25 -0
- package/build/blocks/event/event-calendar-group/icon.d.ts +2 -0
- package/build/blocks/event/event-calendar-group/icon.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/icon.js +2 -0
- package/build/blocks/event/event-calendar-group/index.d.ts +6 -0
- package/build/blocks/event/event-calendar-group/index.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/index.js +6 -0
- package/build/blocks/event/event-calendar-group/types.d.ts +12 -0
- package/build/blocks/event/event-calendar-group/types.d.ts.map +1 -0
- package/build/blocks/event/event-calendar-group/types.js +1 -0
- package/build/blocks/event/event-detail-content/constants.d.ts +2 -0
- package/build/blocks/event/event-detail-content/constants.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/constants.js +1 -0
- package/build/blocks/event/event-detail-content/edit.d.ts +6 -0
- package/build/blocks/event/event-detail-content/edit.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/edit.js +9 -0
- package/build/blocks/event/event-detail-content/factory.d.ts +5 -0
- package/build/blocks/event/event-detail-content/factory.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/factory.js +18 -0
- package/build/blocks/event/event-detail-content/icon.d.ts +2 -0
- package/build/blocks/event/event-detail-content/icon.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/icon.js +2 -0
- package/build/blocks/event/event-detail-content/index.d.ts +5 -0
- package/build/blocks/event/event-detail-content/index.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/index.js +5 -0
- package/build/blocks/event/event-detail-content/types.d.ts +6 -0
- package/build/blocks/event/event-detail-content/types.d.ts.map +1 -0
- package/build/blocks/event/event-detail-content/types.js +1 -0
- package/build/blocks/event/event-detail-header/constants.d.ts +2 -0
- package/build/blocks/event/event-detail-header/constants.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/constants.js +1 -0
- package/build/blocks/event/event-detail-header/edit.d.ts +6 -0
- package/build/blocks/event/event-detail-header/edit.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/edit.js +11 -0
- package/build/blocks/event/event-detail-header/factory.d.ts +5 -0
- package/build/blocks/event/event-detail-header/factory.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/factory.js +22 -0
- package/build/blocks/event/event-detail-header/icon.d.ts +2 -0
- package/build/blocks/event/event-detail-header/icon.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/icon.js +2 -0
- package/build/blocks/event/event-detail-header/index.d.ts +5 -0
- package/build/blocks/event/event-detail-header/index.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/index.js +5 -0
- package/build/blocks/event/event-detail-header/types.d.ts +9 -0
- package/build/blocks/event/event-detail-header/types.d.ts.map +1 -0
- package/build/blocks/event/event-detail-header/types.js +1 -0
- package/build/blocks/event/event-list-group/constants.d.ts +5 -0
- package/build/blocks/event/event-list-group/constants.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/constants.js +5 -0
- package/build/blocks/event/event-list-group/edit.d.ts +6 -0
- package/build/blocks/event/event-list-group/edit.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/edit.js +23 -0
- package/build/blocks/event/event-list-group/factory.d.ts +5 -0
- package/build/blocks/event/event-list-group/factory.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/factory.js +28 -0
- package/build/blocks/event/event-list-group/icon.d.ts +2 -0
- package/build/blocks/event/event-list-group/icon.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/icon.js +2 -0
- package/build/blocks/event/event-list-group/index.d.ts +8 -0
- package/build/blocks/event/event-list-group/index.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/index.js +8 -0
- package/build/blocks/event/event-list-group/types.d.ts +30 -0
- package/build/blocks/event/event-list-group/types.d.ts.map +1 -0
- package/build/blocks/event/event-list-group/types.js +7 -0
- package/build/components/Event/EntitySelectModal.d.ts +27 -0
- package/build/components/Event/EntitySelectModal.d.ts.map +1 -0
- package/build/components/Event/EntitySelectModal.js +100 -0
- package/build/components/Event/EventSelectModal.d.ts +7 -0
- package/build/components/Event/EventSelectModal.d.ts.map +1 -0
- package/build/components/Event/EventSelectModal.js +42 -0
- package/build/components/Event/EventTermSelectModal.d.ts +7 -0
- package/build/components/Event/EventTermSelectModal.d.ts.map +1 -0
- package/build/components/Event/EventTermSelectModal.js +48 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -0
- package/build/types.d.ts +4 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/blocks/event/event-calendar-group/constants.ts +3 -0
- package/src/blocks/event/event-calendar-group/edit.tsx +60 -0
- package/src/blocks/event/event-calendar-group/editor.scss +13 -0
- package/src/blocks/event/event-calendar-group/factory.tsx +30 -0
- package/src/blocks/event/event-calendar-group/icon.tsx +13 -0
- package/src/blocks/event/event-calendar-group/index.tsx +10 -0
- package/src/blocks/event/event-calendar-group/types.ts +13 -0
- package/src/blocks/event/event-detail-content/constants.ts +1 -0
- package/src/blocks/event/event-detail-content/edit.tsx +24 -0
- package/src/blocks/event/event-detail-content/factory.tsx +22 -0
- package/src/blocks/event/event-detail-content/icon.tsx +9 -0
- package/src/blocks/event/event-detail-content/index.ts +9 -0
- package/src/blocks/event/event-detail-content/types.ts +5 -0
- package/src/blocks/event/event-detail-header/constants.ts +2 -0
- package/src/blocks/event/event-detail-header/edit.tsx +49 -0
- package/src/blocks/event/event-detail-header/factory.tsx +26 -0
- package/src/blocks/event/event-detail-header/icon.tsx +8 -0
- package/src/blocks/event/event-detail-header/index.ts +9 -0
- package/src/blocks/event/event-detail-header/types.ts +9 -0
- package/src/blocks/event/event-list-group/constants.ts +6 -0
- package/src/blocks/event/event-list-group/edit.tsx +84 -0
- package/src/blocks/event/event-list-group/editor.scss +9 -0
- package/src/blocks/event/event-list-group/factory.tsx +33 -0
- package/src/blocks/event/event-list-group/icon.tsx +27 -0
- package/src/blocks/event/event-list-group/index.tsx +12 -0
- package/src/blocks/event/event-list-group/types.ts +27 -0
- package/src/components/Event/EntitySelectModal.tsx +249 -0
- package/src/components/Event/EventSelectModal.tsx +51 -0
- package/src/components/Event/EventTermSelectModal.tsx +58 -0
- package/src/editor.scss +2 -0
- package/src/index.ts +36 -0
- package/src/types.ts +5 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const icon = (
|
|
2
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<rect x="2" y="3" width="16" height="4" rx="1" stroke="black" fill="none" />
|
|
4
|
+
<line x1="2" y1="10" x2="14" y2="10" stroke="black" />
|
|
5
|
+
<line x1="2" y1="13" x2="18" y2="13" stroke="black" />
|
|
6
|
+
<line x1="2" y1="16" x2="10" y2="16" stroke="black" />
|
|
7
|
+
</svg>
|
|
8
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type {EventDetailHeaderAttributes, EventDetailHeaderOptions} from './types'
|
|
2
|
+
export {createEventDetailHeaderBlock} from './factory'
|
|
3
|
+
|
|
4
|
+
import type {EventDetailHeaderOptions} from './types'
|
|
5
|
+
|
|
6
|
+
export const eventDetailHeaderDefaults: EventDetailHeaderOptions = {
|
|
7
|
+
name: 'brilo/event-detail-header',
|
|
8
|
+
parent: ['brilo/base-section'],
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type {BriloBlockType} from '~/types'
|
|
2
|
+
|
|
3
|
+
export interface EventDetailHeaderAttributes extends Record<string, unknown> {
|
|
4
|
+
showDate: boolean
|
|
5
|
+
showPlace: boolean
|
|
6
|
+
showPrice: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface EventDetailHeaderOptions extends BriloBlockType {}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {InspectorControls, useBlockProps} from '@wordpress/block-editor'
|
|
2
|
+
import {PanelBody, RangeControl, SelectControl, ToggleControl, TextControl} from '@wordpress/components'
|
|
3
|
+
import {__} from '@wordpress/i18n'
|
|
4
|
+
import {EVENTS_PER_PAGE_DEFAULT, EVENTS_PER_PAGE_MAX, EVENTS_PER_PAGE_MIN} from './constants'
|
|
5
|
+
import {EVENT_LIST_GROUP_MODES} from './types'
|
|
6
|
+
import type {EventListGroupAttributes, EventListGroupMode, EventListGroupOptions} from './types'
|
|
7
|
+
|
|
8
|
+
export function createEventListGroupEdit(options: EventListGroupOptions) {
|
|
9
|
+
return function Edit({
|
|
10
|
+
attributes,
|
|
11
|
+
setAttributes,
|
|
12
|
+
}: {
|
|
13
|
+
attributes: EventListGroupAttributes
|
|
14
|
+
setAttributes: (a: Partial<EventListGroupAttributes>) => void
|
|
15
|
+
}) {
|
|
16
|
+
const {mode, selectedEvents, selectedCategories, emptyMessage, postsPerPage, showPagination} = attributes
|
|
17
|
+
|
|
18
|
+
const blockProps = useBlockProps({className: 'g-event wp-block-section'})
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<InspectorControls>
|
|
23
|
+
<PanelBody title={__('Nastavení výpisu', 'brilo-blocks')} initialOpen>
|
|
24
|
+
<SelectControl
|
|
25
|
+
label={__('Režim', 'brilo-blocks')}
|
|
26
|
+
value={mode}
|
|
27
|
+
options={EVENT_LIST_GROUP_MODES.map((m) => ({label: m.label, value: m.value}))}
|
|
28
|
+
onChange={(mode) => setAttributes({mode: mode as EventListGroupMode})}
|
|
29
|
+
/>
|
|
30
|
+
|
|
31
|
+
{mode !== 'manual' && (
|
|
32
|
+
<>
|
|
33
|
+
<RangeControl
|
|
34
|
+
label={__('Počet příspěvků', 'brilo-blocks')}
|
|
35
|
+
value={postsPerPage}
|
|
36
|
+
onChange={(value) => setAttributes({postsPerPage: value ?? EVENTS_PER_PAGE_DEFAULT})}
|
|
37
|
+
min={EVENTS_PER_PAGE_MIN}
|
|
38
|
+
max={EVENTS_PER_PAGE_MAX}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
{mode !== 'filter' && (
|
|
42
|
+
<ToggleControl
|
|
43
|
+
label={__('Zobrazit stránkování', 'brilo-blocks')}
|
|
44
|
+
checked={showPagination}
|
|
45
|
+
onChange={(value) => setAttributes({showPagination: value})}
|
|
46
|
+
/>
|
|
47
|
+
)}
|
|
48
|
+
</>
|
|
49
|
+
)}
|
|
50
|
+
|
|
51
|
+
<TextControl
|
|
52
|
+
label={__('Zpráva při prázdném výsledku', 'brilo-blocks')}
|
|
53
|
+
value={emptyMessage}
|
|
54
|
+
onChange={(value) => setAttributes({emptyMessage: value})}
|
|
55
|
+
/>
|
|
56
|
+
</PanelBody>
|
|
57
|
+
</InspectorControls>
|
|
58
|
+
|
|
59
|
+
<div {...blockProps}>
|
|
60
|
+
<div className="g-event__label">{__('Seznam událostí', 'brilo-blocks')}</div>
|
|
61
|
+
<div className="g-event__instructions">
|
|
62
|
+
{mode === 'manual' && __('Ručně vybrané události.', 'brilo-blocks')}
|
|
63
|
+
{mode === 'upcoming' && __('Zobrazuje nadcházející události.', 'brilo-blocks')}
|
|
64
|
+
{mode === 'past' && __('Zobrazuje proběhlé události.', 'brilo-blocks')}
|
|
65
|
+
{mode === 'filter' && __('S filtrováním.', 'brilo-blocks')}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{mode !== 'manual' &&
|
|
69
|
+
mode !== 'filter' &&
|
|
70
|
+
options.renderCategoryControl?.({
|
|
71
|
+
selectedItems: selectedCategories,
|
|
72
|
+
onChange: (ids: number[]) => setAttributes({selectedCategories: ids}),
|
|
73
|
+
})}
|
|
74
|
+
|
|
75
|
+
{mode === 'manual' &&
|
|
76
|
+
options.renderEventsControl?.({
|
|
77
|
+
selectedItems: selectedEvents,
|
|
78
|
+
onChange: (ids: number[]) => setAttributes({selectedEvents: ids}),
|
|
79
|
+
})}
|
|
80
|
+
</div>
|
|
81
|
+
</>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {BlockConfiguration} from '@wordpress/blocks'
|
|
2
|
+
import {__} from '@wordpress/i18n'
|
|
3
|
+
import {DEFAULT_EMPTY_MESSAGE, EVENTS_PER_PAGE_DEFAULT} from './constants'
|
|
4
|
+
import {createEventListGroupEdit} from './edit'
|
|
5
|
+
import {icon as defaultIcon} from './icon'
|
|
6
|
+
import type {EventListGroupAttributes, EventListGroupOptions} from './types'
|
|
7
|
+
|
|
8
|
+
export type {EventListGroupAttributes, EventListGroupOptions}
|
|
9
|
+
|
|
10
|
+
export function createEventListGroupBlock(options: EventListGroupOptions): BlockConfiguration<EventListGroupAttributes> {
|
|
11
|
+
const emptyMessage = options.emptyMessage ?? DEFAULT_EMPTY_MESSAGE
|
|
12
|
+
const postsPerPage = options.postsPerPage ?? EVENTS_PER_PAGE_DEFAULT
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
name: options.name,
|
|
16
|
+
title: options.title ?? __('Seznam událostí', 'brilo-blocks'),
|
|
17
|
+
description: options.description,
|
|
18
|
+
category: options.category ?? 'layout',
|
|
19
|
+
icon: options.icon ?? {src: defaultIcon},
|
|
20
|
+
...(options.parent && {parent: options.parent}),
|
|
21
|
+
supports: {html: false, customClassName: false},
|
|
22
|
+
attributes: {
|
|
23
|
+
mode: {type: 'string', default: 'upcoming'},
|
|
24
|
+
selectedEvents: {type: 'array', default: []},
|
|
25
|
+
selectedCategories: {type: 'array', default: []},
|
|
26
|
+
emptyMessage: {type: 'string', default: emptyMessage},
|
|
27
|
+
postsPerPage: {type: 'number', default: postsPerPage},
|
|
28
|
+
showPagination: {type: 'boolean', default: false},
|
|
29
|
+
},
|
|
30
|
+
edit: createEventListGroupEdit(options),
|
|
31
|
+
save: () => null,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const icon = (
|
|
2
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M4 4H9V5H4V4Z" fill="black" />
|
|
4
|
+
<path d="M10 4H16V5H10V4Z" fill="black" />
|
|
5
|
+
<path
|
|
6
|
+
d="M12 2C12 1.44772 12.4477 1 13 1H14C14.5523 1 15 1.44772 15 2C15 2.55228 14.5523 3 14 3H13C12.4477 3 12 2.55228 12 2Z"
|
|
7
|
+
fill="black"
|
|
8
|
+
/>
|
|
9
|
+
<path d="M5 2C5 1.44772 5.44772 1 6 1H10C10.5523 1 11 1.44772 11 2C11 2.55228 10.5523 3 10 3H6C5.44772 3 5 2.55228 5 2Z" fill="black" />
|
|
10
|
+
<rect x="11.5" y="14.5" width="4" height="4" stroke="black" fill="none" />
|
|
11
|
+
<path d="M17 14H20V15H17V14Z" fill="black" />
|
|
12
|
+
<path d="M17 16H19V17H17V16Z" fill="black" />
|
|
13
|
+
<path d="M18 18H20V19H18V18Z" fill="#357AB5" />
|
|
14
|
+
<rect x="0.5" y="14.5" width="4" height="4" stroke="black" fill="none" />
|
|
15
|
+
<path d="M6 14H9V15H6V14Z" fill="black" />
|
|
16
|
+
<path d="M6 16H8V17H6V16Z" fill="black" />
|
|
17
|
+
<path d="M7 18H9V19H7V18Z" fill="#357AB5" />
|
|
18
|
+
<rect x="11.5" y="7.5" width="4" height="4" stroke="black" fill="none" />
|
|
19
|
+
<path d="M17 7H20V8H17V7Z" fill="black" />
|
|
20
|
+
<path d="M17 9H19V10H17V9Z" fill="black" />
|
|
21
|
+
<path d="M18 11H20V12H18V11Z" fill="#357AB5" />
|
|
22
|
+
<rect x="0.5" y="7.5" width="4" height="4" stroke="black" fill="none" />
|
|
23
|
+
<path d="M6 7H9V8H6V7Z" fill="black" />
|
|
24
|
+
<path d="M6 9H8V10H6V9Z" fill="black" />
|
|
25
|
+
<path d="M7 11H9V12H7V11Z" fill="#357AB5" />
|
|
26
|
+
</svg>
|
|
27
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type {EventListGroupAttributes, EventListGroupOptions, EventListGroupMode} from './types'
|
|
2
|
+
export {EVENT_LIST_GROUP_MODES} from './types'
|
|
3
|
+
export {createEventListGroupBlock} from './factory'
|
|
4
|
+
export {EventSelectModal} from '~/components/Event/EventSelectModal'
|
|
5
|
+
export {EventTermSelectModal} from '~/components/Event/EventTermSelectModal'
|
|
6
|
+
|
|
7
|
+
import type {EventListGroupOptions} from './types'
|
|
8
|
+
|
|
9
|
+
export const eventListGroupDefaults: EventListGroupOptions = {
|
|
10
|
+
name: 'brilo/event-list-group',
|
|
11
|
+
parent: ['brilo/base-section'],
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {__} from '@wordpress/i18n'
|
|
2
|
+
import type {BriloBlockType, SelectControlProps} from '~/types'
|
|
3
|
+
|
|
4
|
+
export const EVENT_LIST_GROUP_MODES = [
|
|
5
|
+
{value: 'manual', label: __('Výběr událostí', 'brilo-blocks')},
|
|
6
|
+
{value: 'upcoming', label: __('Nadcházející', 'brilo-blocks')},
|
|
7
|
+
{value: 'past', label: __('Proběhlé', 'brilo-blocks')},
|
|
8
|
+
{value: 'filter', label: __('S filtrováním', 'brilo-blocks')},
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
export type EventListGroupMode = (typeof EVENT_LIST_GROUP_MODES)[number]['value']
|
|
12
|
+
|
|
13
|
+
export interface EventListGroupAttributes extends Record<string, unknown> {
|
|
14
|
+
mode: EventListGroupMode
|
|
15
|
+
selectedEvents: number[]
|
|
16
|
+
selectedCategories: number[]
|
|
17
|
+
emptyMessage: string
|
|
18
|
+
postsPerPage: number
|
|
19
|
+
showPagination: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface EventListGroupOptions extends BriloBlockType {
|
|
23
|
+
emptyMessage?: string
|
|
24
|
+
postsPerPage?: number
|
|
25
|
+
renderCategoryControl?: (props: SelectControlProps) => JSX.Element
|
|
26
|
+
renderEventsControl?: (props: SelectControlProps) => JSX.Element
|
|
27
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import apiFetch from '@wordpress/api-fetch'
|
|
2
|
+
import {Button, Card, CardBody, __experimentalHStack as HStack, Modal, __experimentalVStack as VStack} from '@wordpress/components'
|
|
3
|
+
import {DataViews, type Field, type View} from '@wordpress/dataviews/wp'
|
|
4
|
+
import {__} from '@wordpress/i18n'
|
|
5
|
+
import {arrowDown, arrowUp, close, plusCircle} from '@wordpress/icons'
|
|
6
|
+
// biome-ignore lint/correctness/noUnusedImports: required in scope for classic JSX transform
|
|
7
|
+
import React, {useEffect, useMemo, useState} from 'react'
|
|
8
|
+
import type {SelectControlProps} from '~/types'
|
|
9
|
+
|
|
10
|
+
type EntityRecord = {id: number}
|
|
11
|
+
|
|
12
|
+
// `DataViews`' own prop type makes `getItemId` conditionally optional based on whether
|
|
13
|
+
// `Item extends {id: string}`, which TS can't resolve for an unresolved generic `T` — so
|
|
14
|
+
// re-type it here for a numeric-id `T`, where `getItemId` is always required.
|
|
15
|
+
type TypedDataViewsProps<T extends EntityRecord> = {
|
|
16
|
+
data: T[]
|
|
17
|
+
fields: Field<T>[]
|
|
18
|
+
view: View
|
|
19
|
+
onChangeView: (view: View) => void
|
|
20
|
+
paginationInfo: {totalItems: number; totalPages: number}
|
|
21
|
+
isLoading: boolean
|
|
22
|
+
defaultLayouts: {table: object}
|
|
23
|
+
getItemId: (item: T) => string
|
|
24
|
+
selection: string[]
|
|
25
|
+
onChangeSelection: (ids: string[]) => void
|
|
26
|
+
actions: Array<{
|
|
27
|
+
id: string
|
|
28
|
+
label: (items: T[]) => string
|
|
29
|
+
supportsBulk: boolean
|
|
30
|
+
callback: (items: T[]) => void
|
|
31
|
+
}>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TypedDataViews = DataViews as unknown as <T extends EntityRecord>(props: TypedDataViewsProps<T>) => JSX.Element
|
|
35
|
+
|
|
36
|
+
export type EntitySelectModalConfig<T extends EntityRecord> = {
|
|
37
|
+
endpoint: string
|
|
38
|
+
fields: Field<T>[]
|
|
39
|
+
defaultView: View
|
|
40
|
+
getItemLabel: (item: T) => string
|
|
41
|
+
matchesSearch: (item: T, needle: string) => boolean
|
|
42
|
+
sortValue: (item: T, field: string) => string | number
|
|
43
|
+
buttonLabel: string
|
|
44
|
+
modalTitle: string
|
|
45
|
+
emptyLabel: string
|
|
46
|
+
className?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function useEntities<T extends EntityRecord>(endpoint: string) {
|
|
50
|
+
const [entities, setEntities] = useState<T[]>([])
|
|
51
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setIsLoading(true)
|
|
55
|
+
apiFetch<T[]>({path: endpoint})
|
|
56
|
+
.then((result) => setEntities(result))
|
|
57
|
+
.catch(() => setEntities([]))
|
|
58
|
+
.finally(() => setIsLoading(false))
|
|
59
|
+
}, [endpoint])
|
|
60
|
+
|
|
61
|
+
return {entities, isLoading}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function SelectedOrderList<T extends EntityRecord>({
|
|
65
|
+
items,
|
|
66
|
+
getItemLabel,
|
|
67
|
+
onChange,
|
|
68
|
+
onRemove,
|
|
69
|
+
emptyLabel,
|
|
70
|
+
}: {
|
|
71
|
+
items: T[]
|
|
72
|
+
getItemLabel: (item: T) => string
|
|
73
|
+
onChange: (ids: number[]) => void
|
|
74
|
+
onRemove: (id: number) => void
|
|
75
|
+
emptyLabel: string
|
|
76
|
+
}) {
|
|
77
|
+
if (0 === items.length) {
|
|
78
|
+
return <p className="i-event-select-modal__empty">{emptyLabel}</p>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const move = (index: number, direction: -1 | 1) => {
|
|
82
|
+
const target = index + direction
|
|
83
|
+
|
|
84
|
+
if (target < 0 || target >= items.length) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const next = [...items]
|
|
89
|
+
;[next[index], next[target]] = [next[target], next[index]]
|
|
90
|
+
onChange(next.map((item) => item.id))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<VStack spacing={2}>
|
|
95
|
+
{items.map((item, index) => (
|
|
96
|
+
<Card size="small" className="i-event-select-modal__item" key={item.id}>
|
|
97
|
+
<CardBody>
|
|
98
|
+
<HStack alignment="center">
|
|
99
|
+
<span
|
|
100
|
+
className="i-event-select-modal__item-title"
|
|
101
|
+
style={{
|
|
102
|
+
flexGrow: 1,
|
|
103
|
+
minWidth: 0,
|
|
104
|
+
textAlign: 'left',
|
|
105
|
+
overflow: 'hidden',
|
|
106
|
+
textOverflow: 'ellipsis',
|
|
107
|
+
whiteSpace: 'nowrap',
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{getItemLabel(item) || __('(bez názvu)', 'brilo-blocks')}
|
|
111
|
+
</span>
|
|
112
|
+
|
|
113
|
+
<HStack expanded={false} spacing={1}>
|
|
114
|
+
<Button
|
|
115
|
+
size="small"
|
|
116
|
+
icon={arrowUp}
|
|
117
|
+
label={__('Posunout výš', 'brilo-blocks')}
|
|
118
|
+
onClick={() => move(index, -1)}
|
|
119
|
+
disabled={0 === index}
|
|
120
|
+
/>
|
|
121
|
+
<Button
|
|
122
|
+
size="small"
|
|
123
|
+
icon={arrowDown}
|
|
124
|
+
label={__('Posunout níž', 'brilo-blocks')}
|
|
125
|
+
onClick={() => move(index, 1)}
|
|
126
|
+
disabled={index === items.length - 1}
|
|
127
|
+
/>
|
|
128
|
+
<Button size="small" icon={close} isDestructive label={__('Odebrat', 'brilo-blocks')} onClick={() => onRemove(item.id)} />
|
|
129
|
+
</HStack>
|
|
130
|
+
</HStack>
|
|
131
|
+
</CardBody>
|
|
132
|
+
</Card>
|
|
133
|
+
))}
|
|
134
|
+
</VStack>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generic table-view entity picker built on @wordpress/dataviews (search, sort,
|
|
140
|
+
* checkbox selection, reorderable selection list). Feed it a REST endpoint +
|
|
141
|
+
* DataViews field config to get an `EventSelectModal`/`EventTermSelectModal`-style
|
|
142
|
+
* component without duplicating the DataViews wiring per entity type.
|
|
143
|
+
*/
|
|
144
|
+
export function createEntitySelectModal<T extends EntityRecord>(config: EntitySelectModalConfig<T>) {
|
|
145
|
+
return function EntitySelectModal({selectedItems, onChange}: SelectControlProps) {
|
|
146
|
+
const {entities, isLoading} = useEntities<T>(config.endpoint)
|
|
147
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
148
|
+
const [view, setView] = useState<View>(config.defaultView)
|
|
149
|
+
|
|
150
|
+
const selectedEntities = useMemo(
|
|
151
|
+
() => selectedItems.map((id) => entities.find((entity) => entity.id === id)).filter((entity): entity is T => undefined !== entity),
|
|
152
|
+
[selectedItems, entities],
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const {data, paginationInfo} = useMemo(() => {
|
|
156
|
+
let filtered = entities
|
|
157
|
+
|
|
158
|
+
if (view.search) {
|
|
159
|
+
const needle = view.search.toLowerCase()
|
|
160
|
+
filtered = filtered.filter((entity) => config.matchesSearch(entity, needle))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (view.sort?.field) {
|
|
164
|
+
const {field, direction} = view.sort
|
|
165
|
+
const sorted = [...filtered]
|
|
166
|
+
|
|
167
|
+
sorted.sort((a, b) => {
|
|
168
|
+
const valueA = config.sortValue(a, field)
|
|
169
|
+
const valueB = config.sortValue(b, field)
|
|
170
|
+
const result =
|
|
171
|
+
typeof valueA === 'number' && typeof valueB === 'number' ? valueA - valueB : String(valueA).localeCompare(String(valueB))
|
|
172
|
+
|
|
173
|
+
return 'desc' === direction ? -result : result
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
filtered = sorted
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const perPage = view.perPage ?? 20
|
|
180
|
+
const page = view.page ?? 1
|
|
181
|
+
const start = (page - 1) * perPage
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
data: filtered.slice(start, start + perPage),
|
|
185
|
+
paginationInfo: {totalItems: filtered.length, totalPages: Math.ceil(filtered.length / perPage) || 1},
|
|
186
|
+
}
|
|
187
|
+
}, [entities, view])
|
|
188
|
+
|
|
189
|
+
const toggleSelected = (id: number) => {
|
|
190
|
+
onChange(selectedItems.includes(id) ? selectedItems.filter((itemId) => itemId !== id) : [...selectedItems, id])
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<VStack spacing={3} className={config.className ?? 'i-event-select-modal'}>
|
|
195
|
+
<Button variant="secondary" icon={plusCircle} onClick={() => setIsOpen(true)}>
|
|
196
|
+
{config.buttonLabel}
|
|
197
|
+
</Button>
|
|
198
|
+
|
|
199
|
+
<SelectedOrderList
|
|
200
|
+
items={selectedEntities}
|
|
201
|
+
getItemLabel={config.getItemLabel}
|
|
202
|
+
emptyLabel={config.emptyLabel}
|
|
203
|
+
onChange={onChange}
|
|
204
|
+
onRemove={(id) => onChange(selectedItems.filter((itemId) => itemId !== id))}
|
|
205
|
+
/>
|
|
206
|
+
|
|
207
|
+
{isOpen && (
|
|
208
|
+
<Modal title={config.modalTitle} onRequestClose={() => setIsOpen(false)} size="fill">
|
|
209
|
+
<VStack spacing={4} style={{height: '100%'}}>
|
|
210
|
+
<div style={{flexGrow: 1, minHeight: 0}}>
|
|
211
|
+
<TypedDataViews<T>
|
|
212
|
+
data={data}
|
|
213
|
+
fields={config.fields}
|
|
214
|
+
view={view}
|
|
215
|
+
onChangeView={setView}
|
|
216
|
+
paginationInfo={paginationInfo}
|
|
217
|
+
isLoading={isLoading}
|
|
218
|
+
defaultLayouts={{table: {}}}
|
|
219
|
+
getItemId={(item) => String(item.id)}
|
|
220
|
+
selection={selectedItems.map(String)}
|
|
221
|
+
onChangeSelection={(ids) => onChange(ids.map(Number))}
|
|
222
|
+
actions={[
|
|
223
|
+
{
|
|
224
|
+
id: 'toggle',
|
|
225
|
+
label: (items) =>
|
|
226
|
+
items.length && selectedItems.includes(items[0].id) ? __('Odebrat', 'brilo-blocks') : __('Přidat', 'brilo-blocks'),
|
|
227
|
+
supportsBulk: true,
|
|
228
|
+
callback: (items) => {
|
|
229
|
+
items.forEach((item) => {
|
|
230
|
+
toggleSelected(item.id)
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
]}
|
|
235
|
+
/>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<HStack justify="flex-end">
|
|
239
|
+
<Button variant="primary" onClick={() => setIsOpen(false)}>
|
|
240
|
+
{__('Hotovo', 'brilo-blocks')}
|
|
241
|
+
</Button>
|
|
242
|
+
</HStack>
|
|
243
|
+
</VStack>
|
|
244
|
+
</Modal>
|
|
245
|
+
)}
|
|
246
|
+
</VStack>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type {View} from '@wordpress/dataviews/wp'
|
|
2
|
+
import {__} from '@wordpress/i18n'
|
|
3
|
+
import {createEntitySelectModal} from './EntitySelectModal'
|
|
4
|
+
|
|
5
|
+
type EventRecord = {
|
|
6
|
+
id: number
|
|
7
|
+
title: {rendered: string}
|
|
8
|
+
date: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const DEFAULT_VIEW: View = {
|
|
12
|
+
type: 'table',
|
|
13
|
+
search: '',
|
|
14
|
+
filters: [],
|
|
15
|
+
page: 1,
|
|
16
|
+
perPage: 20,
|
|
17
|
+
sort: {field: 'title', direction: 'asc'},
|
|
18
|
+
fields: ['date'],
|
|
19
|
+
titleField: 'title',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Manual brilo_event picker (table view, search, sort, checkbox selection,
|
|
24
|
+
* reorderable selection list) with title/date columns. See `EntitySelectModal`
|
|
25
|
+
* for the shared DataViews wiring.
|
|
26
|
+
*/
|
|
27
|
+
export const EventSelectModal = createEntitySelectModal<EventRecord>({
|
|
28
|
+
endpoint: '/wp/v2/brilo_event?per_page=100&orderby=title&order=asc&_fields=id,title,date',
|
|
29
|
+
defaultView: DEFAULT_VIEW,
|
|
30
|
+
buttonLabel: __('Vybrat události', 'brilo-blocks'),
|
|
31
|
+
modalTitle: __('Vybrat události', 'brilo-blocks'),
|
|
32
|
+
emptyLabel: __('Nevybrány žádné události.', 'brilo-blocks'),
|
|
33
|
+
getItemLabel: (item) => item.title.rendered,
|
|
34
|
+
matchesSearch: (item, needle) => item.title.rendered.toLowerCase().includes(needle),
|
|
35
|
+
sortValue: (item, field) => ('title' === field ? item.title.rendered : ((item as never)[field] ?? '')),
|
|
36
|
+
fields: [
|
|
37
|
+
{
|
|
38
|
+
id: 'title',
|
|
39
|
+
label: __('Název', 'brilo-blocks'),
|
|
40
|
+
enableHiding: false,
|
|
41
|
+
enableSorting: true,
|
|
42
|
+
render: ({item}: {item: EventRecord}) => item.title.rendered || __('(bez názvu)', 'brilo-blocks'),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'date',
|
|
46
|
+
label: __('Datum', 'brilo-blocks'),
|
|
47
|
+
enableSorting: true,
|
|
48
|
+
render: ({item}: {item: EventRecord}) => (item.date ? new Date(item.date).toLocaleDateString() : '—'),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type {View} from '@wordpress/dataviews/wp'
|
|
2
|
+
import {__} from '@wordpress/i18n'
|
|
3
|
+
import {createEntitySelectModal} from './EntitySelectModal'
|
|
4
|
+
|
|
5
|
+
type EventCategoryRecord = {
|
|
6
|
+
id: number
|
|
7
|
+
name: string
|
|
8
|
+
slug: string
|
|
9
|
+
count?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DEFAULT_VIEW: View = {
|
|
13
|
+
type: 'table',
|
|
14
|
+
search: '',
|
|
15
|
+
filters: [],
|
|
16
|
+
page: 1,
|
|
17
|
+
perPage: 20,
|
|
18
|
+
sort: {field: 'name', direction: 'asc'},
|
|
19
|
+
fields: ['slug', 'count'],
|
|
20
|
+
titleField: 'name',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Manual brilo_event_category picker (table view, search, sort, checkbox
|
|
25
|
+
* selection, reorderable selection list) with name/slug/count columns. See
|
|
26
|
+
* `EntitySelectModal` for the shared DataViews wiring.
|
|
27
|
+
*/
|
|
28
|
+
export const EventTermSelectModal = createEntitySelectModal<EventCategoryRecord>({
|
|
29
|
+
endpoint: '/wp/v2/brilo_event_category?per_page=100&orderby=name&order=asc&_fields=id,name,slug,count',
|
|
30
|
+
defaultView: DEFAULT_VIEW,
|
|
31
|
+
buttonLabel: __('Vybrat kategorie', 'brilo-blocks'),
|
|
32
|
+
modalTitle: __('Vybrat kategorie', 'brilo-blocks'),
|
|
33
|
+
emptyLabel: __('Nevybrány žádné kategorie.', 'brilo-blocks'),
|
|
34
|
+
getItemLabel: (item) => item.name,
|
|
35
|
+
matchesSearch: (item, needle) => item.name.toLowerCase().includes(needle) || item.slug.toLowerCase().includes(needle),
|
|
36
|
+
sortValue: (item, field) => (item as Record<string, unknown>)[field] as string | number,
|
|
37
|
+
fields: [
|
|
38
|
+
{
|
|
39
|
+
id: 'name',
|
|
40
|
+
label: __('Název', 'brilo-blocks'),
|
|
41
|
+
enableHiding: false,
|
|
42
|
+
enableSorting: true,
|
|
43
|
+
render: ({item}: {item: EventCategoryRecord}) => item.name || __('(bez názvu)', 'brilo-blocks'),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'slug',
|
|
47
|
+
label: __('Slug', 'brilo-blocks'),
|
|
48
|
+
enableSorting: true,
|
|
49
|
+
render: ({item}: {item: EventCategoryRecord}) => item.slug || '—',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'count',
|
|
53
|
+
label: __('Počet', 'brilo-blocks'),
|
|
54
|
+
enableSorting: true,
|
|
55
|
+
render: ({item}: {item: EventCategoryRecord}) => (undefined !== item.count ? String(item.count) : '—'),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
})
|
package/src/editor.scss
CHANGED
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
@forward "./blocks/usp/usp-item/editor";
|
|
37
37
|
@forward "./blocks/person/person-group/editor";
|
|
38
38
|
@forward "./blocks/person/person-item/editor";
|
|
39
|
+
@forward "./blocks/event/event-calendar-group/editor";
|
|
40
|
+
@forward "./blocks/event/event-list-group/editor";
|
|
39
41
|
@forward "./blocks/responsive-grid/responsive-grid-item/editor";
|
|
40
42
|
@forward "./blocks/layout/layout-aside/editor";
|
|
41
43
|
@forward "./blocks/layout/layout-content/editor";
|
package/src/index.ts
CHANGED
|
@@ -166,6 +166,42 @@ export type {ContentAttributes, ContentOptions} from './blocks/layout/layout-con
|
|
|
166
166
|
export {createContentBlock, contentDefaults} from './blocks/layout/layout-content'
|
|
167
167
|
export type {AsideAttributes, AsideOptions} from './blocks/layout/layout-aside'
|
|
168
168
|
export {createAsideBlock, asideDefaults} from './blocks/layout/layout-aside'
|
|
169
|
+
export type {
|
|
170
|
+
EventCalendarGroupAttributes,
|
|
171
|
+
EventCalendarGroupOptions,
|
|
172
|
+
} from './blocks/event/event-calendar-group'
|
|
173
|
+
export {
|
|
174
|
+
createEventCalendarGroupBlock,
|
|
175
|
+
eventCalendarGroupDefaults,
|
|
176
|
+
} from './blocks/event/event-calendar-group'
|
|
177
|
+
export type {
|
|
178
|
+
EventListGroupAttributes,
|
|
179
|
+
EventListGroupMode,
|
|
180
|
+
EventListGroupOptions,
|
|
181
|
+
} from './blocks/event/event-list-group'
|
|
182
|
+
export type {
|
|
183
|
+
EventDetailHeaderAttributes,
|
|
184
|
+
EventDetailHeaderOptions,
|
|
185
|
+
} from './blocks/event/event-detail-header'
|
|
186
|
+
export {
|
|
187
|
+
createEventDetailHeaderBlock,
|
|
188
|
+
eventDetailHeaderDefaults,
|
|
189
|
+
} from './blocks/event/event-detail-header'
|
|
190
|
+
export type {
|
|
191
|
+
EventDetailContentAttributes,
|
|
192
|
+
EventDetailContentOptions,
|
|
193
|
+
} from './blocks/event/event-detail-content'
|
|
194
|
+
export {
|
|
195
|
+
createEventDetailContentBlock,
|
|
196
|
+
eventDetailContentDefaults,
|
|
197
|
+
} from './blocks/event/event-detail-content'
|
|
198
|
+
export {
|
|
199
|
+
createEventListGroupBlock,
|
|
200
|
+
eventListGroupDefaults,
|
|
201
|
+
EVENT_LIST_GROUP_MODES,
|
|
202
|
+
EventSelectModal,
|
|
203
|
+
EventTermSelectModal,
|
|
204
|
+
} from './blocks/event/event-list-group'
|
|
169
205
|
export type {
|
|
170
206
|
JobPositionGroupAttributes,
|
|
171
207
|
JobPositionGroupOptions,
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,11 @@ export interface Icon {
|
|
|
17
17
|
media: Media
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export interface SelectControlProps {
|
|
21
|
+
selectedItems: number[]
|
|
22
|
+
onChange: (ids: number[]) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
export type OptionArray = ReadonlyArray<{value: string; label: string}>
|
|
21
26
|
export type NumberArray = ReadonlyArray<number>
|
|
22
27
|
export type StringArray = ReadonlyArray<string>
|