@brilo/base-blocks 0.1.56 → 0.1.58

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 (62) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/blocks/column/column-item/factory.d.ts.map +1 -1
  3. package/build/blocks/column/column-item/factory.js +2 -1
  4. package/build/blocks/column/column-item/icon.d.ts +2 -0
  5. package/build/blocks/column/column-item/icon.d.ts.map +1 -0
  6. package/build/blocks/column/column-item/icon.js +2 -0
  7. package/build/blocks/column/column-item/index.d.ts.map +1 -1
  8. package/build/blocks/column/column-item/index.js +2 -0
  9. package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.d.ts +6 -0
  10. package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.d.ts.map +1 -0
  11. package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.js +167 -0
  12. package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.d.ts +7 -0
  13. package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.d.ts.map +1 -0
  14. package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.js +56 -0
  15. package/build/blocks/food-menu/food-menu-group/constants.d.ts +8 -0
  16. package/build/blocks/food-menu/food-menu-group/constants.d.ts.map +1 -0
  17. package/build/blocks/food-menu/food-menu-group/constants.js +7 -0
  18. package/build/blocks/food-menu/food-menu-group/edit.d.ts +7 -0
  19. package/build/blocks/food-menu/food-menu-group/edit.d.ts.map +1 -0
  20. package/build/blocks/food-menu/food-menu-group/edit.js +36 -0
  21. package/build/blocks/food-menu/food-menu-group/factory.d.ts +4 -0
  22. package/build/blocks/food-menu/food-menu-group/factory.d.ts.map +1 -0
  23. package/build/blocks/food-menu/food-menu-group/factory.js +57 -0
  24. package/build/blocks/food-menu/food-menu-group/icon.d.ts +2 -0
  25. package/build/blocks/food-menu/food-menu-group/icon.d.ts.map +1 -0
  26. package/build/blocks/food-menu/food-menu-group/icon.js +2 -0
  27. package/build/blocks/food-menu/food-menu-group/index.d.ts +6 -0
  28. package/build/blocks/food-menu/food-menu-group/index.d.ts.map +1 -0
  29. package/build/blocks/food-menu/food-menu-group/index.js +7 -0
  30. package/build/blocks/food-menu/food-menu-group/types.d.ts +19 -0
  31. package/build/blocks/food-menu/food-menu-group/types.d.ts.map +1 -0
  32. package/build/blocks/food-menu/food-menu-group/types.js +1 -0
  33. package/build/blocks/gallery/gallery-group/index.js +1 -1
  34. package/build/blocks/text-and-form/text-and-form-text-column/factory.js +1 -1
  35. package/build/components/EntitySelectModal/EntitySelectModal.d.ts +6 -0
  36. package/build/components/EntitySelectModal/EntitySelectModal.d.ts.map +1 -1
  37. package/build/components/EntitySelectModal/EntitySelectModal.js +3 -2
  38. package/build/index.d.ts +2 -0
  39. package/build/index.d.ts.map +1 -1
  40. package/build/index.js +1 -0
  41. package/build/utils/plural.d.ts +3 -0
  42. package/build/utils/plural.d.ts.map +1 -0
  43. package/build/utils/plural.js +7 -0
  44. package/package.json +1 -1
  45. package/src/blocks/column/column-item/factory.tsx +2 -1
  46. package/src/blocks/column/column-item/icon.tsx +10 -0
  47. package/src/blocks/column/column-item/index.ts +2 -0
  48. package/src/blocks/food-menu/food-menu-group/FoodMenuPreview.tsx +380 -0
  49. package/src/blocks/food-menu/food-menu-group/FoodMenuSelectModal.tsx +69 -0
  50. package/src/blocks/food-menu/food-menu-group/constants.ts +9 -0
  51. package/src/blocks/food-menu/food-menu-group/edit.tsx +153 -0
  52. package/src/blocks/food-menu/food-menu-group/editor.scss +269 -0
  53. package/src/blocks/food-menu/food-menu-group/factory.tsx +66 -0
  54. package/src/blocks/food-menu/food-menu-group/icon.tsx +8 -0
  55. package/src/blocks/food-menu/food-menu-group/index.ts +17 -0
  56. package/src/blocks/food-menu/food-menu-group/types.ts +21 -0
  57. package/src/blocks/gallery/gallery-group/index.ts +1 -1
  58. package/src/blocks/text-and-form/text-and-form-text-column/factory.tsx +1 -1
  59. package/src/components/EntitySelectModal/EntitySelectModal.tsx +19 -7
  60. package/src/editor.scss +1 -0
  61. package/src/index.ts +7 -0
  62. package/src/utils/plural.ts +13 -0
@@ -0,0 +1,153 @@
1
+ import {InspectorControls, useBlockProps} from '@wordpress/block-editor'
2
+ import {
3
+ PanelBody,
4
+ __experimentalText as Text,
5
+ TextareaControl,
6
+ ToggleControl,
7
+ __experimentalToggleGroupControl as ToggleGroupControl,
8
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
9
+ } from '@wordpress/components'
10
+ import {__, sprintf} from '@wordpress/i18n'
11
+ import clsx from 'clsx'
12
+ // biome-ignore lint/correctness/noUnusedImports: required in scope for classic JSX transform
13
+ import React from 'react'
14
+ import {plural} from '~/utils/plural'
15
+ import {FoodMenuPreview} from './FoodMenuPreview'
16
+ import type {FoodMenuGroupAttributes, FoodMenuGroupOptions} from './types'
17
+
18
+ const MODE_HINTS: Record<FoodMenuGroupAttributes['mode'], string> = {
19
+ manual: __('Zobrazí se ručně vybrané lístky.', 'brilo-blocks'),
20
+ today: __('Zobrazí se denní lístek s dnešním datem.', 'brilo-blocks'),
21
+ week: __('Zobrazí se denní lístky aktuálního pracovního týdne (pondělí až pátek).', 'brilo-blocks'),
22
+ }
23
+
24
+ const EMPTY_HINTS: Record<FoodMenuGroupAttributes['emptyBehaviour'], string> = {
25
+ message: __('Návštěvník uvidí krátkou větu, že lístek zatím není vypsaný.', 'brilo-blocks'),
26
+ permanent: __('Místo chybějícího denního lístku se nabídne stálá nabídka. Když žádná není, ukáže se věta.', 'brilo-blocks'),
27
+ hide: __('Blok ze stránky zmizí. Stránka pak může zůstat prázdná.', 'brilo-blocks'),
28
+ }
29
+
30
+ function pickedNote(count: number): string {
31
+ if (0 === count) {
32
+ return __('Zatím nevybrán žádný lístek.', 'brilo-blocks')
33
+ }
34
+
35
+ const picked = plural(
36
+ count,
37
+ __('Vybrán %d lístek', 'brilo-blocks'),
38
+ __('Vybrány %d lístky', 'brilo-blocks'),
39
+ __('Vybráno %d lístků', 'brilo-blocks'),
40
+ )
41
+
42
+ return `${sprintf(picked, count)} · ${__('pořadí změníte ve výběru lístků', 'brilo-blocks')}`
43
+ }
44
+
45
+ export function createFoodMenuGroupEdit(options: FoodMenuGroupOptions) {
46
+ return function Edit({
47
+ attributes,
48
+ setAttributes,
49
+ }: {
50
+ attributes: FoodMenuGroupAttributes
51
+ setAttributes: (a: Partial<FoodMenuGroupAttributes>) => void
52
+ }) {
53
+ const {mode, layout, menuIds, showPrices, showAllergens, showReservation, emptyBehaviour, emptyText} = attributes
54
+
55
+ const blockProps = useBlockProps({className: clsx(options.blockClassName ?? 'g-food-menu', `--${layout}`)})
56
+
57
+ return (
58
+ <>
59
+ <InspectorControls>
60
+ <PanelBody title={__('Nastavení lístku', 'brilo-blocks')} initialOpen>
61
+ <ToggleGroupControl
62
+ label={__('Zdroj lístků', 'brilo-blocks')}
63
+ help={MODE_HINTS[mode]}
64
+ value={mode}
65
+ onChange={(value) => setAttributes({mode: value as FoodMenuGroupAttributes['mode']})}
66
+ isBlock
67
+ >
68
+ <ToggleGroupControlOption value="manual" label={__('Ruční výběr', 'brilo-blocks')} />
69
+ <ToggleGroupControlOption value="today" label={__('Dnešní', 'brilo-blocks')} />
70
+ <ToggleGroupControlOption value="week" label={__('Týden', 'brilo-blocks')} />
71
+ </ToggleGroupControl>
72
+
73
+ <ToggleGroupControl
74
+ label={__('Rozvržení', 'brilo-blocks')}
75
+ help={__('Slider se použije jen tehdy, když je lístků víc než jeden.', 'brilo-blocks')}
76
+ value={layout}
77
+ onChange={(value) => setAttributes({layout: value as FoodMenuGroupAttributes['layout']})}
78
+ isBlock
79
+ >
80
+ <ToggleGroupControlOption value="slider" label={__('Slider', 'brilo-blocks')} />
81
+ <ToggleGroupControlOption value="list" label={__('Seznam', 'brilo-blocks')} />
82
+ </ToggleGroupControl>
83
+
84
+ <ToggleControl
85
+ __nextHasNoMarginBottom
86
+ label={__('Zobrazit ceny', 'brilo-blocks')}
87
+ checked={showPrices}
88
+ onChange={(value) => setAttributes({showPrices: value})}
89
+ />
90
+
91
+ <ToggleControl
92
+ __nextHasNoMarginBottom
93
+ label={__('Zobrazit alergeny a legendu', 'brilo-blocks')}
94
+ checked={showAllergens}
95
+ onChange={(value) => setAttributes({showAllergens: value})}
96
+ />
97
+
98
+ <ToggleControl
99
+ __nextHasNoMarginBottom
100
+ label={__('Umožnit objednání obědu', 'brilo-blocks')}
101
+ help={__(
102
+ 'Objednávkové tlačítko se ukáže jen u denních lístků a jen když jsou rezervace zapnuté v nastavení modulu.',
103
+ 'brilo-blocks',
104
+ )}
105
+ checked={showReservation}
106
+ onChange={(value) => setAttributes({showReservation: value})}
107
+ />
108
+ </PanelBody>
109
+
110
+ <PanelBody title={__('Když není co zobrazit', 'brilo-blocks')} initialOpen={false}>
111
+ <ToggleGroupControl
112
+ label={__('Chování', 'brilo-blocks')}
113
+ help={EMPTY_HINTS[emptyBehaviour]}
114
+ value={emptyBehaviour}
115
+ onChange={(value) => setAttributes({emptyBehaviour: value as FoodMenuGroupAttributes['emptyBehaviour']})}
116
+ isBlock
117
+ >
118
+ <ToggleGroupControlOption value="message" label={__('Zpráva', 'brilo-blocks')} />
119
+ <ToggleGroupControlOption value="permanent" label={__('Stálá nabídka', 'brilo-blocks')} />
120
+ <ToggleGroupControlOption value="hide" label={__('Skrýt', 'brilo-blocks')} />
121
+ </ToggleGroupControl>
122
+
123
+ {'hide' !== emptyBehaviour && (
124
+ <TextareaControl
125
+ __nextHasNoMarginBottom
126
+ label={__('Vlastní text zprávy', 'brilo-blocks')}
127
+ help={__('Když zůstane prázdné, použije se věta podle zvoleného zdroje lístků.', 'brilo-blocks')}
128
+ rows={2}
129
+ value={emptyText}
130
+ onChange={(value) => setAttributes({emptyText: value})}
131
+ />
132
+ )}
133
+ </PanelBody>
134
+ </InspectorControls>
135
+
136
+ <div {...blockProps}>
137
+ {'manual' === mode && (
138
+ <div className="brilo-food-menu-preview__pick">
139
+ {options.renderMenusControl?.({
140
+ selectedItems: menuIds,
141
+ onChange: (ids: number[]) => setAttributes({menuIds: ids}),
142
+ })}
143
+
144
+ <Text variant="muted">{pickedNote(menuIds.length)}</Text>
145
+ </div>
146
+ )}
147
+
148
+ <FoodMenuPreview attributes={attributes} />
149
+ </div>
150
+ </>
151
+ )
152
+ }
153
+ }
@@ -0,0 +1,269 @@
1
+ // Editor preview of the menu block: an admin card, not a copy of the page.
2
+ // wp-admin palette only — the canvas carries the theme's front-end styles,
3
+ // and none of its tokens belong here.
4
+ $fg: #1e1e1e;
5
+ $muted: #757575;
6
+ $line: #dcdcde;
7
+ $surface: #f6f7f7;
8
+ $accent: #3858e9;
9
+ $ok: #1d7c4d;
10
+ $ok-soft: #e5f3ec;
11
+ $warn: #9a6700;
12
+
13
+ @layer base {
14
+ .brilo-food-menu-preview {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 14px;
18
+ color: $fg;
19
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
20
+ font-size: 14px;
21
+ line-height: 1.5;
22
+ text-align: left;
23
+
24
+ &--loading {
25
+ opacity: 0.6;
26
+ }
27
+ }
28
+
29
+ .brilo-food-menu-preview__pick {
30
+ display: flex;
31
+ flex-wrap: wrap;
32
+ gap: 10px;
33
+ align-items: center;
34
+ padding: 10px 0 14px;
35
+ }
36
+
37
+ .brilo-food-menu-preview__head {
38
+ display: flex;
39
+ flex-wrap: wrap;
40
+ gap: 12px;
41
+ align-items: center;
42
+ justify-content: space-between;
43
+ padding: 10px 14px;
44
+ background: $surface;
45
+ border: 1px solid $line;
46
+ border-bottom: 0;
47
+ border-radius: 4px 4px 0 0;
48
+ }
49
+
50
+ .brilo-food-menu-preview__card--loading .brilo-food-menu-preview__head {
51
+ // Same height as a loaded head, so the block does not jump when it lands.
52
+ min-height: 43px;
53
+ justify-content: center;
54
+ border-bottom: 1px solid $line;
55
+ border-radius: 4px;
56
+
57
+ .components-spinner {
58
+ margin: 0;
59
+ }
60
+ }
61
+
62
+ .brilo-food-menu-preview__title {
63
+ display: flex;
64
+ gap: 10px;
65
+ align-items: center;
66
+ min-width: 0;
67
+ font-weight: 600;
68
+
69
+ svg {
70
+ flex: none;
71
+ width: 18px;
72
+ height: 18px;
73
+ color: $muted;
74
+ }
75
+ }
76
+
77
+ .brilo-food-menu-preview__meta {
78
+ display: flex;
79
+ flex-wrap: wrap;
80
+ gap: 6px;
81
+ }
82
+
83
+ .brilo-food-menu-preview__pill {
84
+ display: inline-flex;
85
+ gap: 6px;
86
+ align-items: center;
87
+ padding: 2px 9px;
88
+ color: $accent;
89
+ font-weight: 600;
90
+ font-size: 12px;
91
+ letter-spacing: 0.02em;
92
+ white-space: nowrap;
93
+ background: rgba($accent, 0.1);
94
+ border-radius: 999px;
95
+
96
+ &--ok {
97
+ color: $ok;
98
+ background: $ok-soft;
99
+ }
100
+
101
+ &--muted {
102
+ color: $muted;
103
+ background: $surface;
104
+ }
105
+ }
106
+
107
+ .brilo-food-menu-preview__body {
108
+ border: 1px solid $line;
109
+ border-radius: 0 0 4px 4px;
110
+ }
111
+
112
+ .brilo-food-menu-preview__days {
113
+ display: flex;
114
+ overflow-x: auto;
115
+ // A shadow, not a border: the selected tab's underline paints over it
116
+ // without a negative margin, which would make the strip scroll vertically.
117
+ box-shadow: inset 0 -1px 0 $line;
118
+ }
119
+
120
+ .brilo-food-menu-preview__day {
121
+ display: flex;
122
+ gap: 6px;
123
+ align-items: center;
124
+ margin: 0;
125
+ padding: 8px 14px;
126
+ color: $muted;
127
+ font: inherit;
128
+ font-size: 13px;
129
+ white-space: nowrap;
130
+ background: none;
131
+ border: 0;
132
+ border-bottom: 2px solid transparent;
133
+ border-radius: 0;
134
+ cursor: pointer;
135
+
136
+ &[aria-selected="true"] {
137
+ color: $fg;
138
+ font-weight: 600;
139
+ border-bottom-color: $accent;
140
+ }
141
+
142
+ &:focus-visible {
143
+ outline: 2px solid $accent;
144
+ outline-offset: -2px;
145
+ }
146
+ }
147
+
148
+ .brilo-food-menu-preview__dot {
149
+ width: 6px;
150
+ height: 6px;
151
+ background: $ok;
152
+ border-radius: 50%;
153
+
154
+ &--off {
155
+ background: $line;
156
+ }
157
+ }
158
+
159
+ .brilo-food-menu-preview__section {
160
+ padding: 10px 14px 4px;
161
+
162
+ & + & {
163
+ border-top: 1px solid $line;
164
+ }
165
+ }
166
+
167
+ .brilo-food-menu-preview__section-title {
168
+ margin: 0 0 4px;
169
+ color: $muted;
170
+ font-weight: 600;
171
+ font-size: 11px;
172
+ letter-spacing: 0.08em;
173
+ text-transform: uppercase;
174
+ }
175
+
176
+ .brilo-food-menu-preview__row {
177
+ display: grid;
178
+ grid-template-columns: 48px 1fr auto;
179
+ gap: 10px;
180
+ align-items: baseline;
181
+ padding: 6px 0;
182
+ font-size: 14px;
183
+
184
+ &--no-price {
185
+ grid-template-columns: 48px 1fr;
186
+ }
187
+ }
188
+
189
+ .brilo-food-menu-preview__unit {
190
+ color: $muted;
191
+ font-size: 12px;
192
+ font-variant-numeric: tabular-nums;
193
+ }
194
+
195
+ .brilo-food-menu-preview__name {
196
+ min-width: 0;
197
+
198
+ &--missing {
199
+ color: $muted;
200
+ font-style: italic;
201
+ }
202
+ }
203
+
204
+ .brilo-food-menu-preview__note {
205
+ display: block;
206
+ color: $muted;
207
+ font-size: 12px;
208
+ }
209
+
210
+ .brilo-food-menu-preview__chip {
211
+ display: inline-block;
212
+ margin-left: 4px;
213
+ padding: 0 5px;
214
+ color: $muted;
215
+ font-weight: 600;
216
+ font-size: 11px;
217
+ font-variant-numeric: tabular-nums;
218
+ line-height: 16px;
219
+ background: $surface;
220
+ border: 1px solid $line;
221
+ border-radius: 3px;
222
+ }
223
+
224
+ .brilo-food-menu-preview__price {
225
+ font-variant-numeric: tabular-nums;
226
+ white-space: nowrap;
227
+ }
228
+
229
+ .brilo-food-menu-preview__foot {
230
+ display: flex;
231
+ flex-wrap: wrap;
232
+ gap: 10px;
233
+ justify-content: space-between;
234
+ padding: 8px 14px;
235
+ color: $muted;
236
+ font-size: 12px;
237
+ background: $surface;
238
+ border-top: 1px solid $line;
239
+ border-radius: 0 0 4px 4px;
240
+ }
241
+
242
+ .brilo-food-menu-preview__empty {
243
+ display: flex;
244
+ gap: 12px;
245
+ align-items: flex-start;
246
+ padding: 14px 16px;
247
+ color: $muted;
248
+ font-size: 14px;
249
+ border: 1px dashed $line;
250
+ border-radius: 4px;
251
+
252
+ b {
253
+ display: block;
254
+ color: $fg;
255
+ font-weight: 600;
256
+ }
257
+ }
258
+
259
+ .brilo-food-menu-preview__empty-icon {
260
+ flex: none;
261
+ width: 20px;
262
+ height: 20px;
263
+ color: $warn;
264
+ }
265
+
266
+ .brilo-food-menu-preview__retry {
267
+ margin-top: 8px;
268
+ }
269
+ }
@@ -0,0 +1,66 @@
1
+ import type {BlockConfiguration} from '@wordpress/blocks'
2
+ import {__} from '@wordpress/i18n'
3
+ import {
4
+ FOOD_MENU_GROUP_EMPTY_BEHAVIOUR,
5
+ FOOD_MENU_GROUP_EMPTY_BEHAVIOURS,
6
+ FOOD_MENU_GROUP_LAYOUT,
7
+ FOOD_MENU_GROUP_LAYOUTS,
8
+ FOOD_MENU_GROUP_MODE,
9
+ FOOD_MENU_GROUP_MODES,
10
+ } from './constants'
11
+ import {createFoodMenuGroupEdit} from './edit'
12
+ import {icon as defaultIcon} from './icon'
13
+ import type {FoodMenuGroupAttributes, FoodMenuGroupOptions} from './types'
14
+
15
+ export function createFoodMenuGroupBlock(options: FoodMenuGroupOptions): BlockConfiguration<FoodMenuGroupAttributes> {
16
+ return {
17
+ name: options.name,
18
+ title: options.title ?? __('Jídelní lístek', 'brilo-blocks'),
19
+ description: options.description ?? __('Zobrazí vybraný lístek, dnešní denní lístek nebo celý týden.', 'brilo-blocks'),
20
+ category: options.category ?? 'widgets',
21
+ ...(options.parent && {parent: options.parent}),
22
+ icon: options.icon ?? {src: defaultIcon},
23
+ supports: {html: false, customClassName: false},
24
+ attributes: {
25
+ mode: {
26
+ type: 'string',
27
+ default: FOOD_MENU_GROUP_MODE,
28
+ enum: [...FOOD_MENU_GROUP_MODES],
29
+ },
30
+ layout: {
31
+ type: 'string',
32
+ default: FOOD_MENU_GROUP_LAYOUT,
33
+ enum: [...FOOD_MENU_GROUP_LAYOUTS],
34
+ },
35
+ menuIds: {
36
+ type: 'array',
37
+ default: [],
38
+ items: {type: 'number'},
39
+ },
40
+ showPrices: {
41
+ type: 'boolean',
42
+ default: true,
43
+ },
44
+ showAllergens: {
45
+ type: 'boolean',
46
+ default: true,
47
+ },
48
+ showReservation: {
49
+ type: 'boolean',
50
+ default: true,
51
+ },
52
+ emptyBehaviour: {
53
+ type: 'string',
54
+ default: FOOD_MENU_GROUP_EMPTY_BEHAVIOUR,
55
+ enum: [...FOOD_MENU_GROUP_EMPTY_BEHAVIOURS],
56
+ },
57
+ // Empty means "use the wording the server picks for this mode".
58
+ emptyText: {
59
+ type: 'string',
60
+ default: '',
61
+ },
62
+ },
63
+ edit: createFoodMenuGroupEdit(options),
64
+ save: () => null,
65
+ } as unknown as BlockConfiguration<FoodMenuGroupAttributes>
66
+ }
@@ -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" role="img" aria-label="Jídelní lístek">
3
+ <rect x="3" y="2" width="14" height="16" stroke="currentColor" fill="none" />
4
+ <line x1="6" y1="6" x2="14" y2="6" stroke="currentColor" />
5
+ <line x1="6" y1="10" x2="14" y2="10" stroke="currentColor" />
6
+ <line x1="6" y1="14" x2="11" y2="14" stroke="currentColor" />
7
+ </svg>
8
+ )
@@ -0,0 +1,17 @@
1
+ export type {
2
+ FoodMenuGroupAttributes,
3
+ FoodMenuGroupEmptyBehaviour,
4
+ FoodMenuGroupLayout,
5
+ FoodMenuGroupMode,
6
+ FoodMenuGroupOptions,
7
+ } from './types'
8
+ export {createFoodMenuGroupBlock} from './factory'
9
+ export {FoodMenuSelectModal} from './FoodMenuSelectModal'
10
+
11
+ import type {FoodMenuGroupOptions} from './types'
12
+
13
+ export const foodMenuGroupDefaults: FoodMenuGroupOptions = {
14
+ name: 'brilo/food-menu-group',
15
+ // Also allowed inside a content column, so a menu can sit next to a gallery.
16
+ parent: ['brilo/base-section', 'brilo/column-item'],
17
+ }
@@ -0,0 +1,21 @@
1
+ import type {BriloBlockType, SelectControlProps} from '~/types'
2
+ import type {FOOD_MENU_GROUP_EMPTY_BEHAVIOURS, FOOD_MENU_GROUP_LAYOUTS, FOOD_MENU_GROUP_MODES} from './constants'
3
+
4
+ export type FoodMenuGroupMode = (typeof FOOD_MENU_GROUP_MODES)[number]
5
+ export type FoodMenuGroupLayout = (typeof FOOD_MENU_GROUP_LAYOUTS)[number]
6
+ export type FoodMenuGroupEmptyBehaviour = (typeof FOOD_MENU_GROUP_EMPTY_BEHAVIOURS)[number]
7
+
8
+ export interface FoodMenuGroupAttributes extends Record<string, unknown> {
9
+ mode: FoodMenuGroupMode
10
+ layout: FoodMenuGroupLayout
11
+ menuIds: number[]
12
+ showPrices: boolean
13
+ showAllergens: boolean
14
+ showReservation: boolean
15
+ emptyBehaviour: FoodMenuGroupEmptyBehaviour
16
+ emptyText: string
17
+ }
18
+
19
+ export interface FoodMenuGroupOptions extends BriloBlockType {
20
+ renderMenusControl?: (props: SelectControlProps) => JSX.Element
21
+ }
@@ -5,7 +5,7 @@ import type {GalleryGroupOptions} from './types'
5
5
 
6
6
  export const galleryGroupDefaults: GalleryGroupOptions = {
7
7
  name: 'brilo/gallery-group',
8
- parent: ['brilo/base-section'],
8
+ parent: ['brilo/base-section', 'brilo/column-item'],
9
9
  allowedBlocks: ['brilo/gallery-item'],
10
10
  template: [],
11
11
  }
@@ -31,7 +31,7 @@ export function createTextAndFormTextColumnBlock<
31
31
  type: 'object',
32
32
  default: {
33
33
  heading: {title: '', level: HEADING_LEVELS[1], style: headingStyles[1]?.value ?? headingStyles[0].value},
34
- overline: {type: 'object', default: {title: '', color: '', background: ''}},
34
+ overline: {title: '', color: '', background: ''},
35
35
  },
36
36
  },
37
37
  content: {type: 'object', default: {size: contentSizes[1]?.value ?? contentSizes[0].value}},
@@ -45,6 +45,12 @@ export type EntitySelectModalConfig<T extends EntityRecord> = {
45
45
  modalTitle: string
46
46
  emptyLabel: string
47
47
  className?: string
48
+ /**
49
+ * Keep the reorderable selection inside the modal instead of under the
50
+ * button — for blocks whose canvas already previews the picked items in
51
+ * order, where a second list would only repeat them.
52
+ */
53
+ listInModal?: boolean
48
54
  }
49
55
 
50
56
  function useEntities<T extends EntityRecord>(endpoint: string) {
@@ -191,19 +197,23 @@ export function createEntitySelectModal<T extends EntityRecord>(config: EntitySe
191
197
  onChange(selectedItems.includes(id) ? selectedItems.filter((itemId) => itemId !== id) : [...selectedItems, id])
192
198
  }
193
199
 
200
+ const selectedList = (
201
+ <SelectedOrderList
202
+ items={selectedEntities}
203
+ getItemLabel={config.getItemLabel}
204
+ emptyLabel={config.emptyLabel}
205
+ onChange={onChange}
206
+ onRemove={(id) => onChange(selectedItems.filter((itemId) => itemId !== id))}
207
+ />
208
+ )
209
+
194
210
  return (
195
211
  <VStack spacing={3} className={config.className ?? 'i-event-select-modal'}>
196
212
  <Button variant="secondary" icon={plusCircle} onClick={() => setIsOpen(true)}>
197
213
  {config.buttonLabel}
198
214
  </Button>
199
215
 
200
- <SelectedOrderList
201
- items={selectedEntities}
202
- getItemLabel={config.getItemLabel}
203
- emptyLabel={config.emptyLabel}
204
- onChange={onChange}
205
- onRemove={(id) => onChange(selectedItems.filter((itemId) => itemId !== id))}
206
- />
216
+ {!config.listInModal && selectedList}
207
217
 
208
218
  {isOpen && (
209
219
  <Modal title={config.modalTitle} onRequestClose={() => setIsOpen(false)} size="fill">
@@ -236,6 +246,8 @@ export function createEntitySelectModal<T extends EntityRecord>(config: EntitySe
236
246
  />
237
247
  </div>
238
248
 
249
+ {config.listInModal && selectedList}
250
+
239
251
  <HStack justify="flex-end">
240
252
  <Button variant="primary" onClick={() => setIsOpen(false)}>
241
253
  {__('Hotovo', 'brilo-blocks')}
package/src/editor.scss CHANGED
@@ -41,6 +41,7 @@
41
41
  @forward "./blocks/person/person-item/editor";
42
42
  @forward "./blocks/event/event-calendar-group/editor";
43
43
  @forward "./blocks/event/event-list-group/editor";
44
+ @forward "./blocks/food-menu/food-menu-group/editor";
44
45
  @forward "./blocks/responsive-grid/responsive-grid-item/editor";
45
46
  @forward "./blocks/layout/layout-aside/editor";
46
47
  @forward "./blocks/layout/layout-content/editor";
package/src/index.ts CHANGED
@@ -189,6 +189,13 @@ export {
189
189
  DocumentsSelectModal,
190
190
  DocumentTermSelectModal,
191
191
  } from './blocks/document/documents-grid'
192
+ export type {
193
+ FoodMenuGroupAttributes,
194
+ FoodMenuGroupLayout,
195
+ FoodMenuGroupMode,
196
+ FoodMenuGroupOptions,
197
+ } from './blocks/food-menu/food-menu-group'
198
+ export {createFoodMenuGroupBlock, foodMenuGroupDefaults, FoodMenuSelectModal} from './blocks/food-menu/food-menu-group'
192
199
  export type {LinkItemAttributes, LinkItemIcon, LinkItemOptions} from './blocks/link/link-item'
193
200
  export {createLinkItemBlock, linkItemDefaults} from './blocks/link/link-item'
194
201
  export type {ContentWithAsideAttributes, ContentWithAsideOptions} from './blocks/layout/layout-content-w-aside'
@@ -0,0 +1,13 @@
1
+ /** Picks the Czech plural form: 1, 2–4, 0 and 5+. `_n` only knows two. */
2
+ export function plural<One extends string, Few extends string, Many extends string>(
3
+ count: number,
4
+ one: One,
5
+ few: Few,
6
+ many: Many,
7
+ ): One | Few | Many {
8
+ if (count === 1) {
9
+ return one
10
+ }
11
+
12
+ return count >= 2 && count <= 4 ? few : many
13
+ }