@brilo/base-blocks 0.1.36 → 0.1.38

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.
Files changed (176) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/blocks/anchor/anchor-group/factory.d.ts.map +1 -1
  3. package/build/blocks/anchor/anchor-group/factory.js +2 -1
  4. package/build/blocks/anchor/anchor-group/icon.d.ts +2 -0
  5. package/build/blocks/anchor/anchor-group/icon.d.ts.map +1 -0
  6. package/build/blocks/anchor/anchor-group/icon.js +2 -0
  7. package/build/blocks/anchor/anchor-group/index.d.ts.map +1 -1
  8. package/build/blocks/anchor/anchor-group/index.js +1 -0
  9. package/build/blocks/career/career-header/constants.d.ts +2 -0
  10. package/build/blocks/career/career-header/constants.d.ts.map +1 -0
  11. package/build/blocks/career/career-header/constants.js +1 -0
  12. package/build/blocks/career/career-header/edit.d.ts +1 -1
  13. package/build/blocks/career/career-header/edit.d.ts.map +1 -1
  14. package/build/blocks/career/career-header/edit.js +6 -3
  15. package/build/blocks/career/career-header/factory.d.ts.map +1 -1
  16. package/build/blocks/career/career-header/factory.js +3 -1
  17. package/build/blocks/career/career-header/index.d.ts +1 -0
  18. package/build/blocks/career/career-header/index.d.ts.map +1 -1
  19. package/build/blocks/career/career-header/index.js +4 -0
  20. package/build/blocks/career/job-position-group/index.d.ts.map +1 -1
  21. package/build/blocks/career/job-position-group/index.js +1 -0
  22. package/build/blocks/document/documents-grid/index.d.ts +1 -0
  23. package/build/blocks/document/documents-grid/index.d.ts.map +1 -1
  24. package/build/blocks/document/documents-grid/index.js +2 -0
  25. package/build/blocks/event/event-calendar-group/constants.d.ts +2 -0
  26. package/build/blocks/event/event-calendar-group/constants.d.ts.map +1 -0
  27. package/build/blocks/event/event-calendar-group/constants.js +2 -0
  28. package/build/blocks/event/event-calendar-group/edit.d.ts +6 -0
  29. package/build/blocks/event/event-calendar-group/edit.d.ts.map +1 -0
  30. package/build/blocks/event/event-calendar-group/edit.js +18 -0
  31. package/build/blocks/event/event-calendar-group/factory.d.ts +5 -0
  32. package/build/blocks/event/event-calendar-group/factory.d.ts.map +1 -0
  33. package/build/blocks/event/event-calendar-group/factory.js +25 -0
  34. package/build/blocks/event/event-calendar-group/icon.d.ts +2 -0
  35. package/build/blocks/event/event-calendar-group/icon.d.ts.map +1 -0
  36. package/build/blocks/event/event-calendar-group/icon.js +2 -0
  37. package/build/blocks/event/event-calendar-group/index.d.ts +6 -0
  38. package/build/blocks/event/event-calendar-group/index.d.ts.map +1 -0
  39. package/build/blocks/event/event-calendar-group/index.js +6 -0
  40. package/build/blocks/event/event-calendar-group/types.d.ts +12 -0
  41. package/build/blocks/event/event-calendar-group/types.d.ts.map +1 -0
  42. package/build/blocks/event/event-calendar-group/types.js +1 -0
  43. package/build/blocks/event/event-detail-content/constants.d.ts +2 -0
  44. package/build/blocks/event/event-detail-content/constants.d.ts.map +1 -0
  45. package/build/blocks/event/event-detail-content/constants.js +1 -0
  46. package/build/blocks/event/event-detail-content/edit.d.ts +6 -0
  47. package/build/blocks/event/event-detail-content/edit.d.ts.map +1 -0
  48. package/build/blocks/event/event-detail-content/edit.js +9 -0
  49. package/build/blocks/event/event-detail-content/factory.d.ts +5 -0
  50. package/build/blocks/event/event-detail-content/factory.d.ts.map +1 -0
  51. package/build/blocks/event/event-detail-content/factory.js +18 -0
  52. package/build/blocks/event/event-detail-content/icon.d.ts +2 -0
  53. package/build/blocks/event/event-detail-content/icon.d.ts.map +1 -0
  54. package/build/blocks/event/event-detail-content/icon.js +2 -0
  55. package/build/blocks/event/event-detail-content/index.d.ts +5 -0
  56. package/build/blocks/event/event-detail-content/index.d.ts.map +1 -0
  57. package/build/blocks/event/event-detail-content/index.js +5 -0
  58. package/build/blocks/event/event-detail-content/types.d.ts +6 -0
  59. package/build/blocks/event/event-detail-content/types.d.ts.map +1 -0
  60. package/build/blocks/event/event-detail-content/types.js +1 -0
  61. package/build/blocks/event/event-detail-header/constants.d.ts +2 -0
  62. package/build/blocks/event/event-detail-header/constants.d.ts.map +1 -0
  63. package/build/blocks/event/event-detail-header/constants.js +1 -0
  64. package/build/blocks/event/event-detail-header/edit.d.ts +6 -0
  65. package/build/blocks/event/event-detail-header/edit.d.ts.map +1 -0
  66. package/build/blocks/event/event-detail-header/edit.js +11 -0
  67. package/build/blocks/event/event-detail-header/factory.d.ts +5 -0
  68. package/build/blocks/event/event-detail-header/factory.d.ts.map +1 -0
  69. package/build/blocks/event/event-detail-header/factory.js +22 -0
  70. package/build/blocks/event/event-detail-header/icon.d.ts +2 -0
  71. package/build/blocks/event/event-detail-header/icon.d.ts.map +1 -0
  72. package/build/blocks/event/event-detail-header/icon.js +2 -0
  73. package/build/blocks/event/event-detail-header/index.d.ts +5 -0
  74. package/build/blocks/event/event-detail-header/index.d.ts.map +1 -0
  75. package/build/blocks/event/event-detail-header/index.js +5 -0
  76. package/build/blocks/event/event-detail-header/types.d.ts +9 -0
  77. package/build/blocks/event/event-detail-header/types.d.ts.map +1 -0
  78. package/build/blocks/event/event-detail-header/types.js +1 -0
  79. package/build/blocks/event/event-list-group/constants.d.ts +5 -0
  80. package/build/blocks/event/event-list-group/constants.d.ts.map +1 -0
  81. package/build/blocks/event/event-list-group/constants.js +5 -0
  82. package/build/blocks/event/event-list-group/edit.d.ts +6 -0
  83. package/build/blocks/event/event-list-group/edit.d.ts.map +1 -0
  84. package/build/blocks/event/event-list-group/edit.js +23 -0
  85. package/build/blocks/event/event-list-group/factory.d.ts +5 -0
  86. package/build/blocks/event/event-list-group/factory.d.ts.map +1 -0
  87. package/build/blocks/event/event-list-group/factory.js +28 -0
  88. package/build/blocks/event/event-list-group/icon.d.ts +2 -0
  89. package/build/blocks/event/event-list-group/icon.d.ts.map +1 -0
  90. package/build/blocks/event/event-list-group/icon.js +2 -0
  91. package/build/blocks/event/event-list-group/index.d.ts +8 -0
  92. package/build/blocks/event/event-list-group/index.d.ts.map +1 -0
  93. package/build/blocks/event/event-list-group/index.js +8 -0
  94. package/build/blocks/event/event-list-group/types.d.ts +30 -0
  95. package/build/blocks/event/event-list-group/types.d.ts.map +1 -0
  96. package/build/blocks/event/event-list-group/types.js +7 -0
  97. package/build/blocks/post/post-content/index.d.ts.map +1 -1
  98. package/build/blocks/post/post-content/index.js +1 -0
  99. package/build/blocks/sections/base-section/controls.d.ts.map +1 -1
  100. package/build/blocks/sections/base-section/edit.d.ts.map +1 -1
  101. package/build/blocks/sections/base-section/edit.js +3 -1
  102. package/build/blocks/sections/base-section/types.d.ts.map +1 -1
  103. package/build/components/Document/DocumentTermSelectModal.d.ts +8 -0
  104. package/build/components/Document/DocumentTermSelectModal.d.ts.map +1 -0
  105. package/build/components/Document/DocumentTermSelectModal.js +49 -0
  106. package/build/components/Event/EntitySelectModal.d.ts +27 -0
  107. package/build/components/Event/EntitySelectModal.d.ts.map +1 -0
  108. package/build/components/Event/EntitySelectModal.js +100 -0
  109. package/build/components/Event/EventSelectModal.d.ts +7 -0
  110. package/build/components/Event/EventSelectModal.d.ts.map +1 -0
  111. package/build/components/Event/EventSelectModal.js +42 -0
  112. package/build/components/Event/EventTermSelectModal.d.ts +7 -0
  113. package/build/components/Event/EventTermSelectModal.d.ts.map +1 -0
  114. package/build/components/Event/EventTermSelectModal.js +48 -0
  115. package/build/components/Post/PostSelectModal.d.ts +7 -0
  116. package/build/components/Post/PostSelectModal.d.ts.map +1 -0
  117. package/build/components/Post/PostSelectModal.js +42 -0
  118. package/build/components/Post/PostTermSelectModal.d.ts +8 -0
  119. package/build/components/Post/PostTermSelectModal.d.ts.map +1 -0
  120. package/build/components/Post/PostTermSelectModal.js +49 -0
  121. package/build/index.d.ts +11 -1
  122. package/build/index.d.ts.map +1 -1
  123. package/build/index.js +7 -1
  124. package/build/types.d.ts +4 -0
  125. package/build/types.d.ts.map +1 -1
  126. package/package.json +1 -1
  127. package/src/blocks/anchor/anchor-group/editor.scss +1 -1
  128. package/src/blocks/anchor/anchor-group/factory.tsx +2 -1
  129. package/src/blocks/anchor/anchor-group/icon.tsx +23 -0
  130. package/src/blocks/anchor/anchor-group/index.ts +1 -0
  131. package/src/blocks/anchor/anchor-item/editor.scss +1 -1
  132. package/src/blocks/career/career-header/constants.ts +1 -0
  133. package/src/blocks/career/career-header/edit.tsx +5 -2
  134. package/src/blocks/career/career-header/factory.tsx +2 -1
  135. package/src/blocks/career/career-header/index.ts +4 -0
  136. package/src/blocks/career/job-position-group/index.ts +1 -0
  137. package/src/blocks/document/documents-grid/index.ts +2 -0
  138. package/src/blocks/event/event-calendar-group/constants.ts +3 -0
  139. package/src/blocks/event/event-calendar-group/edit.tsx +60 -0
  140. package/src/blocks/event/event-calendar-group/editor.scss +13 -0
  141. package/src/blocks/event/event-calendar-group/factory.tsx +30 -0
  142. package/src/blocks/event/event-calendar-group/icon.tsx +13 -0
  143. package/src/blocks/event/event-calendar-group/index.tsx +10 -0
  144. package/src/blocks/event/event-calendar-group/types.ts +13 -0
  145. package/src/blocks/event/event-detail-content/constants.ts +1 -0
  146. package/src/blocks/event/event-detail-content/edit.tsx +24 -0
  147. package/src/blocks/event/event-detail-content/factory.tsx +22 -0
  148. package/src/blocks/event/event-detail-content/icon.tsx +9 -0
  149. package/src/blocks/event/event-detail-content/index.ts +9 -0
  150. package/src/blocks/event/event-detail-content/types.ts +5 -0
  151. package/src/blocks/event/event-detail-header/constants.ts +2 -0
  152. package/src/blocks/event/event-detail-header/edit.tsx +49 -0
  153. package/src/blocks/event/event-detail-header/factory.tsx +26 -0
  154. package/src/blocks/event/event-detail-header/icon.tsx +8 -0
  155. package/src/blocks/event/event-detail-header/index.ts +9 -0
  156. package/src/blocks/event/event-detail-header/types.ts +9 -0
  157. package/src/blocks/event/event-list-group/constants.ts +6 -0
  158. package/src/blocks/event/event-list-group/edit.tsx +84 -0
  159. package/src/blocks/event/event-list-group/editor.scss +9 -0
  160. package/src/blocks/event/event-list-group/factory.tsx +33 -0
  161. package/src/blocks/event/event-list-group/icon.tsx +27 -0
  162. package/src/blocks/event/event-list-group/index.tsx +12 -0
  163. package/src/blocks/event/event-list-group/types.ts +27 -0
  164. package/src/blocks/post/post-content/index.ts +1 -0
  165. package/src/blocks/sections/base-section/controls.tsx +5 -1
  166. package/src/blocks/sections/base-section/edit.tsx +3 -1
  167. package/src/blocks/sections/base-section/types.ts +9 -3
  168. package/src/components/Document/DocumentTermSelectModal.tsx +60 -0
  169. package/src/components/Event/EntitySelectModal.tsx +249 -0
  170. package/src/components/Event/EventSelectModal.tsx +51 -0
  171. package/src/components/Event/EventTermSelectModal.tsx +58 -0
  172. package/src/components/Post/PostSelectModal.tsx +51 -0
  173. package/src/components/Post/PostTermSelectModal.tsx +60 -0
  174. package/src/editor.scss +2 -0
  175. package/src/index.ts +44 -1
  176. 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,6 @@
1
+ import {__} from '@wordpress/i18n'
2
+
3
+ export const DEFAULT_EMPTY_MESSAGE = __('Nebyly nalezeny žádné události.', 'brilo-blocks')
4
+ export const EVENTS_PER_PAGE_MIN = 1
5
+ export const EVENTS_PER_PAGE_MAX = 18
6
+ export const EVENTS_PER_PAGE_DEFAULT = 6
@@ -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,9 @@
1
+ @layer base {
2
+ .g-event {
3
+ padding: 32px 16px;
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 8px;
7
+ text-align: center;
8
+ }
9
+ }
@@ -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
+ }
@@ -5,4 +5,5 @@ import type {PostContentOptions} from './types'
5
5
 
6
6
  export const postContentDefaults: PostContentOptions = {
7
7
  name: 'brilo/post-content',
8
+ parent: ['brilo/base-section'],
8
9
  }
@@ -289,7 +289,11 @@ function BackgroundImageControl({value, onChange}: BackgroundImageControlProps)
289
289
  onChange={(next) => update({position: next as BackgroundImagePosition})}
290
290
  />
291
291
 
292
- <BaseControl label={__('Překryv barvou', 'brilo-blocks')} __nextHasNoMarginBottom help={__('Ztmavení/zabarvení obrázku pro lepší čitelnost textu.', 'brilo-blocks')}>
292
+ <BaseControl
293
+ label={__('Překryv barvou', 'brilo-blocks')}
294
+ __nextHasNoMarginBottom
295
+ help={__('Ztmavení/zabarvení obrázku pro lepší čitelnost textu.', 'brilo-blocks')}
296
+ >
293
297
  <ColorPalette
294
298
  colors={paletteColors}
295
299
  value={overlayColor || undefined}
@@ -26,7 +26,9 @@ export function createBaseSectionEdit<TExtra extends Record<string, unknown> = R
26
26
  ? {
27
27
  backgroundImage: [tintLayer, `url(${bgImageSrc})`].filter(Boolean).join(', '),
28
28
  backgroundSize: [tintLayer ? '100% 100%' : null, backgroundImage?.fit ?? 'cover'].filter(Boolean).join(', '),
29
- backgroundPosition: [tintLayer ? 'center center' : null, backgroundImage?.position ?? 'center center'].filter(Boolean).join(', '),
29
+ backgroundPosition: [tintLayer ? 'center center' : null, backgroundImage?.position ?? 'center center']
30
+ .filter(Boolean)
31
+ .join(', '),
30
32
  backgroundRepeat: 'no-repeat',
31
33
  padding: '16px',
32
34
  }
@@ -34,9 +34,15 @@ export interface Decoration {
34
34
  export type BackgroundImageFit = 'cover' | 'contain'
35
35
 
36
36
  export type BackgroundImagePosition =
37
- | 'left top' | 'center top' | 'right top'
38
- | 'left center' | 'center center' | 'right center'
39
- | 'left bottom' | 'center bottom' | 'right bottom'
37
+ | 'left top'
38
+ | 'center top'
39
+ | 'right top'
40
+ | 'left center'
41
+ | 'center center'
42
+ | 'right center'
43
+ | 'left bottom'
44
+ | 'center bottom'
45
+ | 'right bottom'
40
46
 
41
47
  export interface BackgroundImageOverlay {
42
48
  color?: string
@@ -0,0 +1,60 @@
1
+ import type {View} from '@wordpress/dataviews/wp'
2
+ import {__} from '@wordpress/i18n'
3
+ import {createEntitySelectModal} from '~/components/Event/EntitySelectModal'
4
+
5
+ type DocumentCategoryRecord = {
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 document_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 DocumentTermSelectModal = createEntitySelectModal<DocumentCategoryRecord>({
29
+ endpoint: '/wp/v2/document_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: DocumentCategoryRecord}) => item.name || __('(bez názvu)', 'brilo-blocks'),
44
+ },
45
+ {
46
+ id: 'slug',
47
+ label: __('Slug', 'brilo-blocks'),
48
+ enableSorting: true,
49
+ render: ({item}: {item: DocumentCategoryRecord}) => item.slug || '—',
50
+ },
51
+ {
52
+ id: 'count',
53
+ label: __('Počet', 'brilo-blocks'),
54
+ enableSorting: true,
55
+ render: ({item}: {item: DocumentCategoryRecord}) => (undefined !== item.count ? String(item.count) : '—'),
56
+ },
57
+ ],
58
+ })
59
+
60
+ export const DocumentTermSelect = DocumentTermSelectModal
@@ -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
+ })