@brilo/base-blocks 0.1.57 → 0.1.60
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/form/form-group/index.js +1 -1
- package/build/blocks/gallery/gallery-group/constants.d.ts +1 -19
- package/build/blocks/gallery/gallery-group/constants.d.ts.map +1 -1
- package/build/blocks/gallery/gallery-group/constants.js +1 -8
- package/build/blocks/gallery/gallery-group/edit.d.ts.map +1 -1
- package/build/blocks/gallery/gallery-group/edit.js +3 -19
- package/build/blocks/gallery/gallery-group/index.js +1 -1
- package/build/blocks/gallery/gallery-item/index.js +1 -1
- package/build/blocks/gallery/gallery-strip-group/constants.d.ts +34 -0
- package/build/blocks/gallery/gallery-strip-group/constants.d.ts.map +1 -0
- package/build/blocks/gallery/gallery-strip-group/constants.js +17 -0
- package/build/blocks/gallery/gallery-strip-group/edit.d.ts +7 -0
- package/build/blocks/gallery/gallery-strip-group/edit.d.ts.map +1 -0
- package/build/blocks/gallery/gallery-strip-group/edit.js +34 -0
- package/build/blocks/gallery/gallery-strip-group/factory.d.ts +5 -0
- package/build/blocks/gallery/gallery-strip-group/factory.d.ts.map +1 -0
- package/build/blocks/gallery/gallery-strip-group/factory.js +28 -0
- package/build/blocks/gallery/gallery-strip-group/index.d.ts +5 -0
- package/build/blocks/gallery/gallery-strip-group/index.d.ts.map +1 -0
- package/build/blocks/gallery/gallery-strip-group/index.js +7 -0
- package/build/blocks/gallery/gallery-strip-group/types.d.ts +18 -0
- package/build/blocks/gallery/gallery-strip-group/types.d.ts.map +1 -0
- package/build/blocks/gallery/gallery-strip-group/types.js +1 -0
- package/build/blocks/groups/text-and-media-group/index.d.ts.map +1 -1
- package/build/blocks/groups/text-and-media-group/index.js +2 -0
- package/build/blocks/hero/hero-item/index.js +1 -1
- package/build/blocks/review/review-summary-item/constants.d.ts +3 -0
- package/build/blocks/review/review-summary-item/constants.d.ts.map +1 -0
- package/build/blocks/review/review-summary-item/constants.js +2 -0
- package/build/blocks/review/review-summary-item/edit.d.ts +6 -0
- package/build/blocks/review/review-summary-item/edit.d.ts.map +1 -0
- package/build/blocks/review/review-summary-item/edit.js +17 -0
- package/build/blocks/review/review-summary-item/factory.d.ts +5 -0
- package/build/blocks/review/review-summary-item/factory.d.ts.map +1 -0
- package/build/blocks/review/review-summary-item/factory.js +23 -0
- package/build/blocks/review/review-summary-item/index.d.ts +5 -0
- package/build/blocks/review/review-summary-item/index.d.ts.map +1 -0
- package/build/blocks/review/review-summary-item/index.js +5 -0
- package/build/blocks/review/review-summary-item/types.d.ts +11 -0
- package/build/blocks/review/review-summary-item/types.d.ts.map +1 -0
- package/build/blocks/review/review-summary-item/types.js +1 -0
- package/build/blocks/urgency/urgency-item/constants.d.ts +56 -0
- package/build/blocks/urgency/urgency-item/constants.d.ts.map +1 -0
- package/build/blocks/urgency/urgency-item/constants.js +32 -0
- package/build/blocks/urgency/urgency-item/edit.d.ts +7 -0
- package/build/blocks/urgency/urgency-item/edit.d.ts.map +1 -0
- package/build/blocks/urgency/urgency-item/edit.js +83 -0
- package/build/blocks/urgency/urgency-item/factory.d.ts +5 -0
- package/build/blocks/urgency/urgency-item/factory.d.ts.map +1 -0
- package/build/blocks/urgency/urgency-item/factory.js +31 -0
- package/build/blocks/urgency/urgency-item/index.d.ts +5 -0
- package/build/blocks/urgency/urgency-item/index.d.ts.map +1 -0
- package/build/blocks/urgency/urgency-item/index.js +5 -0
- package/build/blocks/urgency/urgency-item/types.d.ts +22 -0
- package/build/blocks/urgency/urgency-item/types.d.ts.map +1 -0
- package/build/blocks/urgency/urgency-item/types.js +1 -0
- 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/components/GalleryImagesAppender/index.d.ts +5 -0
- package/build/components/GalleryImagesAppender/index.d.ts.map +1 -0
- package/build/components/GalleryImagesAppender/index.js +12 -0
- package/build/constants.d.ts +19 -0
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +8 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -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/form/form-group/index.ts +1 -1
- package/src/blocks/gallery/gallery-group/constants.ts +1 -8
- package/src/blocks/gallery/gallery-group/edit.tsx +3 -39
- package/src/blocks/gallery/gallery-group/editor.scss +0 -12
- package/src/blocks/gallery/gallery-group/index.ts +1 -1
- package/src/blocks/gallery/gallery-item/index.ts +1 -1
- package/src/blocks/gallery/gallery-strip-group/constants.ts +22 -0
- package/src/blocks/gallery/gallery-strip-group/edit.tsx +112 -0
- package/src/blocks/gallery/gallery-strip-group/editor.scss +36 -0
- package/src/blocks/gallery/gallery-strip-group/factory.tsx +32 -0
- package/src/blocks/gallery/gallery-strip-group/index.ts +11 -0
- package/src/blocks/gallery/gallery-strip-group/types.ts +20 -0
- package/src/blocks/groups/text-and-media-group/index.ts +2 -0
- package/src/blocks/hero/hero-item/index.ts +1 -1
- package/src/blocks/review/review-summary-item/constants.ts +2 -0
- package/src/blocks/review/review-summary-item/edit.tsx +86 -0
- package/src/blocks/review/review-summary-item/editor.scss +44 -0
- package/src/blocks/review/review-summary-item/factory.tsx +27 -0
- package/src/blocks/review/review-summary-item/index.ts +9 -0
- package/src/blocks/review/review-summary-item/types.ts +11 -0
- package/src/blocks/urgency/urgency-item/constants.ts +39 -0
- package/src/blocks/urgency/urgency-item/edit.tsx +319 -0
- package/src/blocks/urgency/urgency-item/editor.scss +99 -0
- package/src/blocks/urgency/urgency-item/factory.tsx +36 -0
- package/src/blocks/urgency/urgency-item/index.ts +9 -0
- package/src/blocks/urgency/urgency-item/types.ts +23 -0
- package/src/components/EntitySelectModal/EntitySelectModal.tsx +19 -7
- package/src/components/GalleryImagesAppender/editor.scss +15 -0
- package/src/components/GalleryImagesAppender/index.tsx +29 -0
- package/src/constants.ts +9 -0
- package/src/editor.scss +5 -0
- package/src/index.ts +13 -0
- package/src/utils/plural.ts +13 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {InnerBlocks} from '@wordpress/block-editor'
|
|
2
|
+
import type {BlockConfiguration} from '@wordpress/blocks'
|
|
3
|
+
import {__} from '@wordpress/i18n'
|
|
4
|
+
import {ITEM_SPACING_ATTRIBUTE} from '~/components/ItemSpacingControl'
|
|
5
|
+
import {DEFAULT_RATIOS, DIRECTIONS, HEIGHTS, SPEEDS} from './constants'
|
|
6
|
+
import {createGalleryStripGroupEdit} from './edit'
|
|
7
|
+
import type {GalleryStripGroupAttributes, GalleryStripGroupOptions} from './types'
|
|
8
|
+
|
|
9
|
+
export type {GalleryStripGroupAttributes, GalleryStripGroupOptions}
|
|
10
|
+
|
|
11
|
+
export function createGalleryStripGroupBlock(options: GalleryStripGroupOptions): BlockConfiguration<GalleryStripGroupAttributes> {
|
|
12
|
+
const ratios = options.ratios ?? DEFAULT_RATIOS
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
name: options.name,
|
|
16
|
+
title: options.title ?? __('Pás obrázků', 'brilo-blocks'),
|
|
17
|
+
description: options.description ?? __('Nekonečně se posouvající pás obrázků', 'brilo-blocks'),
|
|
18
|
+
category: options.category ?? 'layout',
|
|
19
|
+
...(options.parent && {parent: options.parent}),
|
|
20
|
+
icon: options.icon ?? 'images-alt2',
|
|
21
|
+
supports: {html: false, customClassName: false},
|
|
22
|
+
attributes: {
|
|
23
|
+
height: {type: 'string', default: HEIGHTS[1].value, enum: HEIGHTS.map((h) => h.value)},
|
|
24
|
+
ratio: {type: 'string', default: ratios[0].value, enum: ratios.map((r) => r.value)},
|
|
25
|
+
speed: {type: 'string', default: SPEEDS[1].value, enum: SPEEDS.map((s) => s.value)},
|
|
26
|
+
direction: {type: 'string', default: DIRECTIONS[0].value, enum: DIRECTIONS.map((d) => d.value)},
|
|
27
|
+
...ITEM_SPACING_ATTRIBUTE,
|
|
28
|
+
},
|
|
29
|
+
edit: createGalleryStripGroupEdit(options),
|
|
30
|
+
save: () => <InnerBlocks.Content />,
|
|
31
|
+
} as unknown as BlockConfiguration<GalleryStripGroupAttributes>
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type {GalleryStripGroupAttributes, GalleryStripGroupOptions} from './types'
|
|
2
|
+
export {createGalleryStripGroupBlock} from './factory'
|
|
3
|
+
|
|
4
|
+
import type {GalleryStripGroupOptions} from './types'
|
|
5
|
+
|
|
6
|
+
export const galleryStripGroupDefaults: GalleryStripGroupOptions = {
|
|
7
|
+
name: 'brilo/gallery-strip-group',
|
|
8
|
+
parent: ['brilo/base-section'],
|
|
9
|
+
allowedBlocks: ['brilo/gallery-item'],
|
|
10
|
+
template: [],
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {BriloBlockType, OptionArray} from '~/types'
|
|
2
|
+
import type {DEFAULT_RATIOS, DIRECTIONS, HEIGHTS, SPEEDS} from './constants'
|
|
3
|
+
|
|
4
|
+
export type GalleryStripHeight = (typeof HEIGHTS)[number]['value']
|
|
5
|
+
export type GalleryStripSpeed = (typeof SPEEDS)[number]['value']
|
|
6
|
+
export type GalleryStripRatio = (typeof DEFAULT_RATIOS)[number]['value']
|
|
7
|
+
export type GalleryStripDirection = (typeof DIRECTIONS)[number]['value']
|
|
8
|
+
|
|
9
|
+
export interface GalleryStripGroupAttributes extends Record<string, unknown> {
|
|
10
|
+
height: GalleryStripHeight
|
|
11
|
+
ratio: GalleryStripRatio
|
|
12
|
+
speed: GalleryStripSpeed
|
|
13
|
+
direction: GalleryStripDirection
|
|
14
|
+
spacing?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface GalleryStripGroupOptions extends BriloBlockType {
|
|
18
|
+
allowedBlocks: string[]
|
|
19
|
+
ratios?: OptionArray
|
|
20
|
+
}
|
|
@@ -6,5 +6,5 @@ import type {HeroItemOptions} from './types'
|
|
|
6
6
|
export const heroItemDefaults: HeroItemOptions = {
|
|
7
7
|
name: 'brilo/hero-item',
|
|
8
8
|
parent: ['brilo/hero-group'],
|
|
9
|
-
allowedBlocks: ['core/list', 'core/paragraph', 'brilo/button-group'],
|
|
9
|
+
allowedBlocks: ['core/list', 'core/paragraph', 'brilo/button-group', 'brilo/review-summary-item'],
|
|
10
10
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {InspectorControls, RichText, useBlockProps} from '@wordpress/block-editor'
|
|
2
|
+
import {PanelBody, RangeControl, TextControl, ToggleControl} from '@wordpress/components'
|
|
3
|
+
import {__} from '@wordpress/i18n'
|
|
4
|
+
import clsx from 'clsx'
|
|
5
|
+
import type {CSSProperties} from 'react'
|
|
6
|
+
import {RATING_MAX, RATING_STEP} from './constants'
|
|
7
|
+
import type {ReviewSummaryItemAttributes, ReviewSummaryItemOptions} from './types'
|
|
8
|
+
|
|
9
|
+
const roundToHalf = (value: number) => Math.round(value * 2) / 2
|
|
10
|
+
|
|
11
|
+
export function createReviewSummaryItemEdit(options: ReviewSummaryItemOptions) {
|
|
12
|
+
return function Edit({
|
|
13
|
+
attributes,
|
|
14
|
+
setAttributes,
|
|
15
|
+
}: {
|
|
16
|
+
attributes: ReviewSummaryItemAttributes
|
|
17
|
+
setAttributes: (a: Partial<ReviewSummaryItemAttributes>) => void
|
|
18
|
+
}) {
|
|
19
|
+
const {rating, count, countLabel, showMax, description} = attributes
|
|
20
|
+
const hasRating = typeof rating === 'number'
|
|
21
|
+
const hasCount = typeof count === 'number' && count > 0
|
|
22
|
+
|
|
23
|
+
const blockProps = useBlockProps({className: clsx(options.blockClassName ?? 'i-review-summary-item wp-block-section')})
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<InspectorControls>
|
|
28
|
+
<PanelBody title={__('Hodnocení', 'brilo-blocks')}>
|
|
29
|
+
<RangeControl
|
|
30
|
+
label={__('Průměrné hodnocení', 'brilo-blocks')}
|
|
31
|
+
help={__('Bez hodnoty se hvězdičky ani číslo nezobrazí.', 'brilo-blocks')}
|
|
32
|
+
value={rating}
|
|
33
|
+
onChange={(v) => setAttributes({rating: v})}
|
|
34
|
+
min={0}
|
|
35
|
+
max={RATING_MAX}
|
|
36
|
+
step={RATING_STEP}
|
|
37
|
+
allowReset
|
|
38
|
+
withInputField
|
|
39
|
+
/>
|
|
40
|
+
<ToggleControl label={__('Zobrazit „/ 5"', 'brilo-blocks')} checked={showMax} onChange={(v) => setAttributes({showMax: v})} />
|
|
41
|
+
<TextControl
|
|
42
|
+
type="number"
|
|
43
|
+
min={0}
|
|
44
|
+
label={__('Počet recenzí', 'brilo-blocks')}
|
|
45
|
+
help={__('Při nule nebo prázdné hodnotě se počet nezobrazí.', 'brilo-blocks')}
|
|
46
|
+
value={typeof count === 'number' ? String(count) : ''}
|
|
47
|
+
onChange={(v) => setAttributes({count: v === '' ? undefined : Math.max(0, Number.parseInt(v, 10) || 0)})}
|
|
48
|
+
/>
|
|
49
|
+
<TextControl label={__('Popisek počtu', 'brilo-blocks')} value={countLabel} onChange={(v) => setAttributes({countLabel: v})} />
|
|
50
|
+
</PanelBody>
|
|
51
|
+
</InspectorControls>
|
|
52
|
+
|
|
53
|
+
<div {...blockProps}>
|
|
54
|
+
<div className="i-review-summary-item__score">
|
|
55
|
+
{hasRating && (
|
|
56
|
+
<>
|
|
57
|
+
<span
|
|
58
|
+
className="i-review-summary-item__stars"
|
|
59
|
+
style={{'--rating-fill': `${(roundToHalf(rating) / RATING_MAX) * 100}%`} as CSSProperties}
|
|
60
|
+
>
|
|
61
|
+
★★★★★
|
|
62
|
+
</span>
|
|
63
|
+
<strong className="i-review-summary-item__value">
|
|
64
|
+
{rating.toLocaleString(undefined, {minimumFractionDigits: 1, maximumFractionDigits: 1})}
|
|
65
|
+
{showMax && ` / ${RATING_MAX}`}
|
|
66
|
+
</strong>
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
{hasCount && <span className="i-review-summary-item__count">{`${count} ${countLabel}`}</span>}
|
|
70
|
+
{!hasRating && !hasCount && (
|
|
71
|
+
<span className="i-review-summary-item__placeholder">{__('Nastavte hodnocení v postranním panelu', 'brilo-blocks')}</span>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
<RichText
|
|
75
|
+
tagName="p"
|
|
76
|
+
className="i-review-summary-item__description u-text-editable"
|
|
77
|
+
value={description}
|
|
78
|
+
onChange={(v) => setAttributes({description: v})}
|
|
79
|
+
placeholder={__('Popis hodnocení...', 'brilo-blocks')}
|
|
80
|
+
allowedFormats={['core/bold', 'core/italic', 'core/link', 'brilo-blocks/text-color']}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
</>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
.i-review-summary-item {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 8px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.i-review-summary-item__score {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 8px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.i-review-summary-item__stars {
|
|
15
|
+
position: relative;
|
|
16
|
+
display: inline-block;
|
|
17
|
+
font-size: 20px;
|
|
18
|
+
line-height: 1;
|
|
19
|
+
letter-spacing: 2px;
|
|
20
|
+
color: #d0d5dd;
|
|
21
|
+
|
|
22
|
+
&::before {
|
|
23
|
+
content: "★★★★★";
|
|
24
|
+
position: absolute;
|
|
25
|
+
inset: 0;
|
|
26
|
+
width: var(--rating-fill, 0%);
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
color: #1e2d45;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.i-review-summary-item__value {
|
|
33
|
+
font-size: 18px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.i-review-summary-item__count,
|
|
37
|
+
.i-review-summary-item__placeholder {
|
|
38
|
+
font-size: 14px;
|
|
39
|
+
color: #50575e;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.i-review-summary-item__description {
|
|
43
|
+
margin: 0;
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {BlockConfiguration} from '@wordpress/blocks'
|
|
2
|
+
import {__} from '@wordpress/i18n'
|
|
3
|
+
import {createReviewSummaryItemEdit} from './edit'
|
|
4
|
+
import type {ReviewSummaryItemAttributes, ReviewSummaryItemOptions} from './types'
|
|
5
|
+
|
|
6
|
+
export type {ReviewSummaryItemAttributes, ReviewSummaryItemOptions}
|
|
7
|
+
|
|
8
|
+
export function createReviewSummaryItemBlock(options: ReviewSummaryItemOptions): BlockConfiguration<ReviewSummaryItemAttributes> {
|
|
9
|
+
return {
|
|
10
|
+
name: options.name,
|
|
11
|
+
title: options.title ?? __('Souhrn hodnocení', 'brilo-blocks'),
|
|
12
|
+
description: options.description ?? __('Průměrné hodnocení hvězdičkami s počtem recenzí a popisem', 'brilo-blocks'),
|
|
13
|
+
category: options.category ?? 'layout',
|
|
14
|
+
...(options.parent && {parent: options.parent}),
|
|
15
|
+
icon: options.icon ?? 'star-half',
|
|
16
|
+
supports: {html: false, customClassName: false},
|
|
17
|
+
attributes: {
|
|
18
|
+
rating: {type: 'number'},
|
|
19
|
+
count: {type: 'number'},
|
|
20
|
+
countLabel: {type: 'string', default: __('hodnocení', 'brilo-blocks')},
|
|
21
|
+
showMax: {type: 'boolean', default: false},
|
|
22
|
+
description: {type: 'string', default: ''},
|
|
23
|
+
},
|
|
24
|
+
edit: createReviewSummaryItemEdit(options),
|
|
25
|
+
save: () => null,
|
|
26
|
+
} as unknown as BlockConfiguration<ReviewSummaryItemAttributes>
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type {ReviewSummaryItemAttributes, ReviewSummaryItemOptions} from './types'
|
|
2
|
+
export {createReviewSummaryItemBlock} from './factory'
|
|
3
|
+
|
|
4
|
+
import type {ReviewSummaryItemOptions} from './types'
|
|
5
|
+
|
|
6
|
+
export const reviewSummaryItemDefaults: ReviewSummaryItemOptions = {
|
|
7
|
+
name: 'brilo/review-summary-item',
|
|
8
|
+
parent: ['brilo/base-section', 'brilo/text-and-media-group', 'brilo/hero-item'],
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {BriloBlockType} from '~/types'
|
|
2
|
+
|
|
3
|
+
export interface ReviewSummaryItemAttributes extends Record<string, unknown> {
|
|
4
|
+
rating?: number
|
|
5
|
+
count?: number
|
|
6
|
+
countLabel: string
|
|
7
|
+
showMax: boolean
|
|
8
|
+
description: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ReviewSummaryItemOptions extends BriloBlockType {}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {__} from '@wordpress/i18n'
|
|
2
|
+
|
|
3
|
+
export {CONTENT_SIZES as TEXT_SIZES} from '~/constants'
|
|
4
|
+
|
|
5
|
+
export const TYPES = [
|
|
6
|
+
{value: 'deadline', label: __('Termín', 'brilo-blocks')},
|
|
7
|
+
{value: 'capacity', label: __('Volná místa', 'brilo-blocks')},
|
|
8
|
+
{value: 'season', label: __('Sezóna', 'brilo-blocks')},
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
export const DEADLINE_DISPLAYS = [
|
|
12
|
+
{value: 'countdown', label: __('Odpočet', 'brilo-blocks')},
|
|
13
|
+
{value: 'date', label: __('Datum', 'brilo-blocks')},
|
|
14
|
+
] as const
|
|
15
|
+
|
|
16
|
+
export const TEXTS = {
|
|
17
|
+
countdown: __('Nabídka končí za', 'brilo-blocks'),
|
|
18
|
+
date: __('Nabídka platí do', 'brilo-blocks'),
|
|
19
|
+
capacity: __('Zbývá volných míst', 'brilo-blocks'),
|
|
20
|
+
season: __('Nabídka platí pouze tuto sezónu', 'brilo-blocks'),
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
export const COUNTDOWN_UNITS = [
|
|
24
|
+
{key: 'days', label: __('dní', 'brilo-blocks')},
|
|
25
|
+
{key: 'hours', label: __('hod', 'brilo-blocks')},
|
|
26
|
+
{key: 'minutes', label: __('min', 'brilo-blocks')},
|
|
27
|
+
{key: 'seconds', label: __('sek', 'brilo-blocks')},
|
|
28
|
+
] as const
|
|
29
|
+
|
|
30
|
+
export const NUMBER_SIZE_MIN = 16
|
|
31
|
+
export const NUMBER_SIZE_MAX = 120
|
|
32
|
+
|
|
33
|
+
export const NUMBER_SIZE_PRESETS = [
|
|
34
|
+
{value: 32, label: __('XS', 'brilo-blocks')},
|
|
35
|
+
{value: 40, label: __('SM', 'brilo-blocks')},
|
|
36
|
+
{value: 48, label: __('MD', 'brilo-blocks')},
|
|
37
|
+
{value: 60, label: __('LG', 'brilo-blocks')},
|
|
38
|
+
{value: 72, label: __('XL', 'brilo-blocks')},
|
|
39
|
+
] as const
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import {InspectorControls, useBlockProps} from '@wordpress/block-editor'
|
|
2
|
+
import {
|
|
3
|
+
BaseControl,
|
|
4
|
+
Button,
|
|
5
|
+
ColorPalette,
|
|
6
|
+
DateTimePicker,
|
|
7
|
+
Notice,
|
|
8
|
+
PanelBody,
|
|
9
|
+
RangeControl,
|
|
10
|
+
TextControl,
|
|
11
|
+
__experimentalToggleGroupControl as ToggleGroupControl,
|
|
12
|
+
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
|
|
13
|
+
} from '@wordpress/components'
|
|
14
|
+
import {useDispatch} from '@wordpress/data'
|
|
15
|
+
import {dateI18n, getDate, getSettings} from '@wordpress/date'
|
|
16
|
+
import {__} from '@wordpress/i18n'
|
|
17
|
+
import clsx from 'clsx'
|
|
18
|
+
import {type CSSProperties, useEffect} from 'react'
|
|
19
|
+
import {useTenantColorPalette} from '~/hooks/useTenantColorPalette'
|
|
20
|
+
import {
|
|
21
|
+
COUNTDOWN_UNITS,
|
|
22
|
+
DEADLINE_DISPLAYS,
|
|
23
|
+
TEXTS,
|
|
24
|
+
NUMBER_SIZE_MAX,
|
|
25
|
+
NUMBER_SIZE_MIN,
|
|
26
|
+
NUMBER_SIZE_PRESETS,
|
|
27
|
+
TEXT_SIZES,
|
|
28
|
+
TYPES,
|
|
29
|
+
} from './constants'
|
|
30
|
+
import type {UrgencyDeadlineDisplay, UrgencyItemAttributes, UrgencyItemOptions, UrgencyTextSize, UrgencyType} from './types'
|
|
31
|
+
|
|
32
|
+
const TEXT_VALUES: string[] = Object.values(TEXTS)
|
|
33
|
+
|
|
34
|
+
function defaultTextFor(type: UrgencyType, display: UrgencyDeadlineDisplay): string {
|
|
35
|
+
return type === 'deadline' ? TEXTS[display] : TEXTS[type]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseCount(value: string): number | undefined {
|
|
39
|
+
return value === '' ? undefined : Math.max(0, Number.parseInt(value, 10) || 0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function countdownParts(milliseconds: number): Record<(typeof COUNTDOWN_UNITS)[number]['key'], string> {
|
|
43
|
+
const totalSeconds = Math.max(0, Math.floor(milliseconds / 1000))
|
|
44
|
+
const pad = (n: number) => String(n).padStart(2, '0')
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
days: pad(Math.floor(totalSeconds / 86400)),
|
|
48
|
+
hours: pad(Math.floor((totalSeconds % 86400) / 3600)),
|
|
49
|
+
minutes: pad(Math.floor((totalSeconds % 3600) / 60)),
|
|
50
|
+
seconds: pad(totalSeconds % 60),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function formatDeadline(deadline: string): string {
|
|
55
|
+
const {formats} = getSettings()
|
|
56
|
+
const time = deadline.slice(11, 16)
|
|
57
|
+
const format = time === '00:00' || time === '23:59' ? formats.date : `${formats.date} ${formats.time}`
|
|
58
|
+
|
|
59
|
+
return dateI18n(format, getDate(deadline))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function endOfDay(date: string): number {
|
|
63
|
+
return getDate(`${date}T23:59:59`).getTime()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ColorControl({label, id, value, onChange}: {label: string; id: string; value: string; onChange: (v: string) => void}) {
|
|
67
|
+
const {paletteColors} = useTenantColorPalette()
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<BaseControl label={label} id={id}>
|
|
71
|
+
<ColorPalette colors={paletteColors} value={value || undefined} onChange={(v) => onChange(v ?? '')} />
|
|
72
|
+
</BaseControl>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function NumberSizeControl({value, onChange}: {value?: number; onChange: (v?: number) => void}) {
|
|
77
|
+
return (
|
|
78
|
+
<BaseControl
|
|
79
|
+
label={__('Velikost čísel (px)', 'brilo-blocks')}
|
|
80
|
+
help={__('Bez nastavení se použije velikost nadpisu ze šablony.', 'brilo-blocks')}
|
|
81
|
+
id="urgency-item-number-size"
|
|
82
|
+
>
|
|
83
|
+
<div className="i-urgency__number-presets">
|
|
84
|
+
{NUMBER_SIZE_PRESETS.map((p) => (
|
|
85
|
+
<Button key={p.label} variant={value === p.value ? 'primary' : 'secondary'} size="small" onClick={() => onChange(p.value)}>
|
|
86
|
+
{p.label}
|
|
87
|
+
</Button>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
<RangeControl value={value} onChange={onChange} min={NUMBER_SIZE_MIN} max={NUMBER_SIZE_MAX} step={1} withInputField allowReset />
|
|
91
|
+
</BaseControl>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createUrgencyItemEdit(options: UrgencyItemOptions) {
|
|
96
|
+
return function Edit({
|
|
97
|
+
attributes,
|
|
98
|
+
setAttributes,
|
|
99
|
+
clientId,
|
|
100
|
+
}: {
|
|
101
|
+
attributes: UrgencyItemAttributes
|
|
102
|
+
setAttributes: (a: Partial<UrgencyItemAttributes>) => void
|
|
103
|
+
clientId: string
|
|
104
|
+
}) {
|
|
105
|
+
const {
|
|
106
|
+
type,
|
|
107
|
+
text,
|
|
108
|
+
deadline,
|
|
109
|
+
display,
|
|
110
|
+
remaining,
|
|
111
|
+
total,
|
|
112
|
+
endDate,
|
|
113
|
+
textSize,
|
|
114
|
+
numberSize,
|
|
115
|
+
backgroundColor,
|
|
116
|
+
numberBackgroundColor,
|
|
117
|
+
textColor,
|
|
118
|
+
} = attributes
|
|
119
|
+
// core/editor is missing outside the post editor (widgets, site editor) – dispatch returns undefined there.
|
|
120
|
+
const editorDispatch = useDispatch('core/editor') as unknown as
|
|
121
|
+
| {lockPostSaving: (lockName: string) => void; unlockPostSaving: (lockName: string) => void}
|
|
122
|
+
| undefined
|
|
123
|
+
const lockPostSaving = editorDispatch?.lockPostSaving
|
|
124
|
+
const unlockPostSaving = editorDispatch?.unlockPostSaving
|
|
125
|
+
|
|
126
|
+
const isDeadlineMissing = type === 'deadline' && !deadline
|
|
127
|
+
const isCapacityMissing = type === 'capacity' && (typeof remaining !== 'number' || typeof total !== 'number' || total <= 0)
|
|
128
|
+
const expiresAt =
|
|
129
|
+
type === 'deadline' && deadline ? getDate(deadline).getTime() : type === 'season' && endDate ? endOfDay(endDate) : null
|
|
130
|
+
const isExpired = expiresAt !== null && expiresAt <= Date.now()
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!lockPostSaving || !unlockPostSaving) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const lockName = `brilo-urgency-item-${clientId}`
|
|
138
|
+
|
|
139
|
+
if (isDeadlineMissing || isCapacityMissing) {
|
|
140
|
+
lockPostSaving(lockName)
|
|
141
|
+
} else {
|
|
142
|
+
unlockPostSaving(lockName)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return () => unlockPostSaving(lockName)
|
|
146
|
+
}, [clientId, isDeadlineMissing, isCapacityMissing, lockPostSaving, unlockPostSaving])
|
|
147
|
+
|
|
148
|
+
const switchText = (nextType: UrgencyType, nextDisplay: UrgencyDeadlineDisplay) =>
|
|
149
|
+
!text || TEXT_VALUES.includes(text) ? {text: defaultTextFor(nextType, nextDisplay)} : {}
|
|
150
|
+
|
|
151
|
+
const countdown = countdownParts(deadline ? getDate(deadline).getTime() - Date.now() : 0)
|
|
152
|
+
const takenPercent =
|
|
153
|
+
type === 'capacity' && total ? Math.min(100, Math.max(0, ((total - Math.min(remaining ?? total, total)) / total) * 100)) : 0
|
|
154
|
+
|
|
155
|
+
const blockProps = useBlockProps({className: clsx(options.blockClassName ?? 'i-urgency wp-block-section', `--${type}`)})
|
|
156
|
+
const isCountdown = type === 'deadline' && display === 'countdown'
|
|
157
|
+
const cardStyle = {
|
|
158
|
+
color: textColor || undefined,
|
|
159
|
+
'--urgency-background': backgroundColor || undefined,
|
|
160
|
+
'--urgency-number-background': numberBackgroundColor || undefined,
|
|
161
|
+
'--urgency-text-color': textColor || undefined,
|
|
162
|
+
'--urgency-number-size': numberSize ? `${numberSize}px` : undefined,
|
|
163
|
+
} as CSSProperties
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<>
|
|
167
|
+
<InspectorControls>
|
|
168
|
+
<PanelBody title={__('Nastavení', 'brilo-blocks')}>
|
|
169
|
+
<ToggleGroupControl
|
|
170
|
+
isBlock
|
|
171
|
+
label={__('Co zobrazit', 'brilo-blocks')}
|
|
172
|
+
value={type}
|
|
173
|
+
onChange={(v) => setAttributes({type: v as UrgencyType, ...switchText(v as UrgencyType, display)})}
|
|
174
|
+
>
|
|
175
|
+
{TYPES.map((t) => (
|
|
176
|
+
<ToggleGroupControlOption key={t.value} value={t.value} label={t.label} />
|
|
177
|
+
))}
|
|
178
|
+
</ToggleGroupControl>
|
|
179
|
+
<TextControl
|
|
180
|
+
label={__('Text', 'brilo-blocks')}
|
|
181
|
+
value={text}
|
|
182
|
+
placeholder={defaultTextFor(type, display)}
|
|
183
|
+
onChange={(v) => setAttributes({text: v})}
|
|
184
|
+
/>
|
|
185
|
+
</PanelBody>
|
|
186
|
+
|
|
187
|
+
{type === 'deadline' && (
|
|
188
|
+
<PanelBody title={__('Termín', 'brilo-blocks')}>
|
|
189
|
+
<ToggleGroupControl
|
|
190
|
+
isBlock
|
|
191
|
+
label={__('Zobrazit jako', 'brilo-blocks')}
|
|
192
|
+
value={display}
|
|
193
|
+
onChange={(v) => setAttributes({display: v as UrgencyDeadlineDisplay, ...switchText(type, v as UrgencyDeadlineDisplay)})}
|
|
194
|
+
>
|
|
195
|
+
{DEADLINE_DISPLAYS.map((d) => (
|
|
196
|
+
<ToggleGroupControlOption key={d.value} value={d.value} label={d.label} />
|
|
197
|
+
))}
|
|
198
|
+
</ToggleGroupControl>
|
|
199
|
+
<DateTimePicker currentDate={deadline || null} onChange={(v) => setAttributes({deadline: v ?? ''})} is12Hour={false} />
|
|
200
|
+
</PanelBody>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
{type === 'capacity' && (
|
|
204
|
+
<PanelBody title={__('Volná místa', 'brilo-blocks')}>
|
|
205
|
+
<TextControl
|
|
206
|
+
type="number"
|
|
207
|
+
min={0}
|
|
208
|
+
label={__('Zbývá míst', 'brilo-blocks')}
|
|
209
|
+
value={typeof remaining === 'number' ? String(remaining) : ''}
|
|
210
|
+
onChange={(v) => setAttributes({remaining: parseCount(v)})}
|
|
211
|
+
/>
|
|
212
|
+
<TextControl
|
|
213
|
+
type="number"
|
|
214
|
+
min={1}
|
|
215
|
+
label={__('Celkem míst', 'brilo-blocks')}
|
|
216
|
+
value={typeof total === 'number' ? String(total) : ''}
|
|
217
|
+
onChange={(v) => setAttributes({total: parseCount(v)})}
|
|
218
|
+
/>
|
|
219
|
+
</PanelBody>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
{type === 'season' && (
|
|
223
|
+
<PanelBody title={__('Sezóna', 'brilo-blocks')}>
|
|
224
|
+
<TextControl
|
|
225
|
+
type="date"
|
|
226
|
+
label={__('Konec sezóny (volitelné)', 'brilo-blocks')}
|
|
227
|
+
help={__('Po tomto dni se blok na webu skryje.', 'brilo-blocks')}
|
|
228
|
+
value={endDate}
|
|
229
|
+
onChange={(v) => setAttributes({endDate: v})}
|
|
230
|
+
/>
|
|
231
|
+
</PanelBody>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
<PanelBody title={__('Styl', 'brilo-blocks')}>
|
|
235
|
+
<ToggleGroupControl
|
|
236
|
+
isBlock
|
|
237
|
+
label={__('Velikost textu', 'brilo-blocks')}
|
|
238
|
+
value={textSize}
|
|
239
|
+
onChange={(v) => setAttributes({textSize: v as UrgencyTextSize})}
|
|
240
|
+
>
|
|
241
|
+
{TEXT_SIZES.map((s) => (
|
|
242
|
+
<ToggleGroupControlOption key={s.value} value={s.value} label={s.label} />
|
|
243
|
+
))}
|
|
244
|
+
</ToggleGroupControl>
|
|
245
|
+
{type !== 'season' && <NumberSizeControl value={numberSize} onChange={(v) => setAttributes({numberSize: v})} />}
|
|
246
|
+
<ColorControl
|
|
247
|
+
label={__('Barva pozadí', 'brilo-blocks')}
|
|
248
|
+
id="urgency-item-background-color"
|
|
249
|
+
value={backgroundColor}
|
|
250
|
+
onChange={(v) => setAttributes({backgroundColor: v})}
|
|
251
|
+
/>
|
|
252
|
+
{isCountdown && (
|
|
253
|
+
<ColorControl
|
|
254
|
+
label={__('Pozadí čísel', 'brilo-blocks')}
|
|
255
|
+
id="urgency-item-number-background-color"
|
|
256
|
+
value={numberBackgroundColor}
|
|
257
|
+
onChange={(v) => setAttributes({numberBackgroundColor: v})}
|
|
258
|
+
/>
|
|
259
|
+
)}
|
|
260
|
+
<ColorControl
|
|
261
|
+
label={__('Barva textu', 'brilo-blocks')}
|
|
262
|
+
id="urgency-item-text-color"
|
|
263
|
+
value={textColor}
|
|
264
|
+
onChange={(v) => setAttributes({textColor: v})}
|
|
265
|
+
/>
|
|
266
|
+
</PanelBody>
|
|
267
|
+
</InspectorControls>
|
|
268
|
+
|
|
269
|
+
<div {...blockProps}>
|
|
270
|
+
{isDeadlineMissing && (
|
|
271
|
+
<Notice status="error" isDismissible={false}>
|
|
272
|
+
{__('Vyberte termín v postranním panelu – bez něj nelze stránku uložit.', 'brilo-blocks')}
|
|
273
|
+
</Notice>
|
|
274
|
+
)}
|
|
275
|
+
{isCapacityMissing && (
|
|
276
|
+
<Notice status="error" isDismissible={false}>
|
|
277
|
+
{__('Vyplňte volná a celková místa v postranním panelu – bez nich nelze stránku uložit.', 'brilo-blocks')}
|
|
278
|
+
</Notice>
|
|
279
|
+
)}
|
|
280
|
+
{isExpired && (
|
|
281
|
+
<Notice status="warning" isDismissible={false}>
|
|
282
|
+
{__('Termín vypršel – na webu se nezobrazuje.', 'brilo-blocks')}
|
|
283
|
+
</Notice>
|
|
284
|
+
)}
|
|
285
|
+
|
|
286
|
+
<div className={clsx('i-urgency__card', `--text-${textSize}`)} style={cardStyle}>
|
|
287
|
+
{text && <span className="i-urgency__label">{text}</span>}
|
|
288
|
+
|
|
289
|
+
{isCountdown && (
|
|
290
|
+
<span className="i-urgency__countdown">
|
|
291
|
+
{COUNTDOWN_UNITS.map((unit) => (
|
|
292
|
+
<span key={unit.key} className="i-urgency__unit">
|
|
293
|
+
<span className="i-urgency__number">{countdown[unit.key]}</span>
|
|
294
|
+
<span className="i-urgency__unit-label">{unit.label}</span>
|
|
295
|
+
</span>
|
|
296
|
+
))}
|
|
297
|
+
</span>
|
|
298
|
+
)}
|
|
299
|
+
|
|
300
|
+
{type === 'deadline' && display === 'date' && (
|
|
301
|
+
<span className="i-urgency__value">{deadline ? formatDeadline(deadline) : '—'}</span>
|
|
302
|
+
)}
|
|
303
|
+
|
|
304
|
+
{type === 'capacity' && (
|
|
305
|
+
<>
|
|
306
|
+
<span className="i-urgency__value">
|
|
307
|
+
{remaining ?? '—'} / {total ?? '—'}
|
|
308
|
+
</span>
|
|
309
|
+
<span className="i-urgency__progress">
|
|
310
|
+
<span className="i-urgency__progress-fill" style={{width: `${takenPercent}%`}} />
|
|
311
|
+
</span>
|
|
312
|
+
</>
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
}
|