@brilo/base-blocks 0.1.57 → 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.
- package/build/.tsbuildinfo +1 -1
- package/build/blocks/column/column-item/factory.d.ts.map +1 -1
- package/build/blocks/column/column-item/factory.js +2 -1
- package/build/blocks/column/column-item/icon.d.ts +2 -0
- package/build/blocks/column/column-item/icon.d.ts.map +1 -0
- package/build/blocks/column/column-item/icon.js +2 -0
- package/build/blocks/column/column-item/index.d.ts.map +1 -1
- package/build/blocks/column/column-item/index.js +2 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.d.ts +6 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuPreview.js +167 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.d.ts +7 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/FoodMenuSelectModal.js +56 -0
- package/build/blocks/food-menu/food-menu-group/constants.d.ts +8 -0
- package/build/blocks/food-menu/food-menu-group/constants.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/constants.js +7 -0
- package/build/blocks/food-menu/food-menu-group/edit.d.ts +7 -0
- package/build/blocks/food-menu/food-menu-group/edit.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/edit.js +36 -0
- package/build/blocks/food-menu/food-menu-group/factory.d.ts +4 -0
- package/build/blocks/food-menu/food-menu-group/factory.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/factory.js +57 -0
- package/build/blocks/food-menu/food-menu-group/icon.d.ts +2 -0
- package/build/blocks/food-menu/food-menu-group/icon.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/icon.js +2 -0
- package/build/blocks/food-menu/food-menu-group/index.d.ts +6 -0
- package/build/blocks/food-menu/food-menu-group/index.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/index.js +7 -0
- package/build/blocks/food-menu/food-menu-group/types.d.ts +19 -0
- package/build/blocks/food-menu/food-menu-group/types.d.ts.map +1 -0
- package/build/blocks/food-menu/food-menu-group/types.js +1 -0
- package/build/blocks/gallery/gallery-group/index.js +1 -1
- package/build/components/EntitySelectModal/EntitySelectModal.d.ts +6 -0
- package/build/components/EntitySelectModal/EntitySelectModal.d.ts.map +1 -1
- package/build/components/EntitySelectModal/EntitySelectModal.js +3 -2
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/utils/plural.d.ts +3 -0
- package/build/utils/plural.d.ts.map +1 -0
- package/build/utils/plural.js +7 -0
- package/package.json +1 -1
- package/src/blocks/column/column-item/factory.tsx +2 -1
- package/src/blocks/column/column-item/icon.tsx +10 -0
- package/src/blocks/column/column-item/index.ts +2 -0
- package/src/blocks/food-menu/food-menu-group/FoodMenuPreview.tsx +380 -0
- package/src/blocks/food-menu/food-menu-group/FoodMenuSelectModal.tsx +69 -0
- package/src/blocks/food-menu/food-menu-group/constants.ts +9 -0
- package/src/blocks/food-menu/food-menu-group/edit.tsx +153 -0
- package/src/blocks/food-menu/food-menu-group/editor.scss +269 -0
- package/src/blocks/food-menu/food-menu-group/factory.tsx +66 -0
- package/src/blocks/food-menu/food-menu-group/icon.tsx +8 -0
- package/src/blocks/food-menu/food-menu-group/index.ts +17 -0
- package/src/blocks/food-menu/food-menu-group/types.ts +21 -0
- package/src/blocks/gallery/gallery-group/index.ts +1 -1
- package/src/components/EntitySelectModal/EntitySelectModal.tsx +19 -7
- package/src/editor.scss +1 -0
- package/src/index.ts +7 -0
- 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
|
}
|
|
@@ -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
|
-
|
|
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
|
+
}
|