@brilo/base-blocks 0.1.51 → 0.1.54

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 (73) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/blocks/accordion/accordion-group/edit.d.ts.map +1 -1
  3. package/build/blocks/accordion/accordion-group/edit.js +8 -2
  4. package/build/blocks/accordion/accordion-group/factory.d.ts.map +1 -1
  5. package/build/blocks/accordion/accordion-group/factory.js +20 -11
  6. package/build/blocks/accordion/accordion-group/index.d.ts +2 -1
  7. package/build/blocks/accordion/accordion-group/index.d.ts.map +1 -1
  8. package/build/blocks/accordion/accordion-group/index.js +1 -0
  9. package/build/blocks/accordion/accordion-group/types.d.ts +8 -0
  10. package/build/blocks/accordion/accordion-group/types.d.ts.map +1 -1
  11. package/build/blocks/career/job-position-group/JobPositionsSelectModal.d.ts.map +1 -1
  12. package/build/blocks/career/job-position-group/JobPositionsSelectModal.js +1 -1
  13. package/build/blocks/document/documents-grid/DocumentsSelectModal.d.ts.map +1 -1
  14. package/build/blocks/document/documents-grid/DocumentsSelectModal.js +1 -1
  15. package/build/blocks/faq/faq-group/constants.d.ts +0 -10
  16. package/build/blocks/faq/faq-group/constants.d.ts.map +1 -1
  17. package/build/blocks/faq/faq-group/constants.js +0 -5
  18. package/build/blocks/faq/faq-group/edit.d.ts.map +1 -1
  19. package/build/blocks/faq/faq-group/edit.js +6 -4
  20. package/build/blocks/faq/faq-group/factory.d.ts.map +1 -1
  21. package/build/blocks/faq/faq-group/factory.js +9 -10
  22. package/build/blocks/faq/faq-group/types.d.ts +6 -7
  23. package/build/blocks/faq/faq-group/types.d.ts.map +1 -1
  24. package/build/blocks/layout/box/constants.d.ts +12 -0
  25. package/build/blocks/layout/box/constants.d.ts.map +1 -0
  26. package/build/blocks/layout/box/constants.js +15 -0
  27. package/build/blocks/layout/box/controls.d.ts.map +1 -1
  28. package/build/blocks/layout/box/controls.js +6 -5
  29. package/build/blocks/layout/box/deprecated.d.ts +71 -0
  30. package/build/blocks/layout/box/deprecated.d.ts.map +1 -0
  31. package/build/blocks/layout/box/deprecated.js +100 -0
  32. package/build/blocks/layout/box/edit.d.ts.map +1 -1
  33. package/build/blocks/layout/box/edit.js +14 -18
  34. package/build/blocks/layout/box/factory.d.ts.map +1 -1
  35. package/build/blocks/layout/box/factory.js +4 -2
  36. package/build/blocks/layout/box/index.d.ts +1 -1
  37. package/build/blocks/layout/box/index.d.ts.map +1 -1
  38. package/build/blocks/layout/box/types.d.ts +5 -14
  39. package/build/blocks/layout/box/types.d.ts.map +1 -1
  40. package/build/components/EntitySelectModal/EntitySelectModal.d.ts +1 -1
  41. package/build/components/EntitySelectModal/EntitySelectModal.d.ts.map +1 -1
  42. package/build/components/EntitySelectModal/EntitySelectModal.js +1 -1
  43. package/build/components/LazyDataViews/LazyDataViews.d.ts +8 -0
  44. package/build/components/LazyDataViews/LazyDataViews.d.ts.map +1 -0
  45. package/build/components/LazyDataViews/LazyDataViews.js +11 -0
  46. package/build/components/SurfaceStyleControl/index.d.ts +53 -0
  47. package/build/components/SurfaceStyleControl/index.d.ts.map +1 -0
  48. package/build/components/SurfaceStyleControl/index.js +36 -0
  49. package/build/index.d.ts +5 -3
  50. package/build/index.d.ts.map +1 -1
  51. package/build/index.js +2 -1
  52. package/package.json +1 -1
  53. package/src/blocks/accordion/accordion-group/edit.tsx +16 -1
  54. package/src/blocks/accordion/accordion-group/factory.tsx +19 -7
  55. package/src/blocks/accordion/accordion-group/index.ts +8 -1
  56. package/src/blocks/accordion/accordion-group/types.ts +11 -1
  57. package/src/blocks/career/job-position-group/JobPositionsSelectModal.tsx +2 -1
  58. package/src/blocks/document/documents-grid/DocumentsSelectModal.tsx +2 -1
  59. package/src/blocks/faq/faq-group/constants.ts +0 -6
  60. package/src/blocks/faq/faq-group/edit.tsx +11 -56
  61. package/src/blocks/faq/faq-group/factory.tsx +6 -6
  62. package/src/blocks/faq/faq-group/types.ts +6 -7
  63. package/src/blocks/layout/box/constants.ts +19 -0
  64. package/src/blocks/layout/box/controls.tsx +42 -21
  65. package/src/blocks/layout/box/deprecated.tsx +146 -0
  66. package/src/blocks/layout/box/edit.tsx +15 -18
  67. package/src/blocks/layout/box/factory.tsx +4 -2
  68. package/src/blocks/layout/box/index.ts +1 -1
  69. package/src/blocks/layout/box/types.ts +5 -14
  70. package/src/components/EntitySelectModal/EntitySelectModal.tsx +2 -1
  71. package/src/components/LazyDataViews/LazyDataViews.tsx +17 -0
  72. package/src/components/SurfaceStyleControl/index.tsx +114 -0
  73. package/src/index.ts +11 -3
@@ -1,27 +1,12 @@
1
1
  import {InspectorControls} from '@wordpress/block-editor'
2
- import {
3
- BaseControl,
4
- Button,
5
- ColorPalette,
6
- PanelBody,
7
- RangeControl,
8
- SelectControl,
9
- __experimentalToggleGroupControl as ToggleGroupControl,
10
- __experimentalToggleGroupControlOption as ToggleGroupControlOption,
11
- } from '@wordpress/components'
2
+ import {BaseControl, Button, ColorPalette, PanelBody, RangeControl, SelectControl} from '@wordpress/components'
12
3
  import {__} from '@wordpress/i18n'
13
4
  import {ItemSpacingPanel, itemSpacingStyle} from '~/components/ItemSpacingControl'
5
+ import {DEFAULT_SURFACE_SHADOWS, SurfaceStylePanel} from '~/components/SurfaceStyleControl'
14
6
  import {useGroupBlock} from '~/hooks/useGroupBlock'
15
7
  import {useTenantColorPalette} from '~/hooks/useTenantColorPalette'
16
- import {
17
- DEFAULT_ICON_SIZE_PRESETS,
18
- DEFAULT_ICON_VARIANTS,
19
- DEFAULT_SHADOWS,
20
- FAQ_HEADING_LEVELS,
21
- ICON_SIZE_MAX,
22
- ICON_SIZE_MIN,
23
- } from './constants'
24
- import type {FaqGroupAttributes, FaqGroupOptions, FaqHeadingLevel, FaqIconVariant, FaqShadow, FaqSizePreset} from './types'
8
+ import {DEFAULT_ICON_SIZE_PRESETS, DEFAULT_ICON_VARIANTS, FAQ_HEADING_LEVELS, ICON_SIZE_MAX, ICON_SIZE_MIN} from './constants'
9
+ import type {FaqGroupAttributes, FaqGroupOptions, FaqHeadingLevel, FaqIconVariant, FaqSizePreset} from './types'
25
10
 
26
11
  function PresetSizeControl({
27
12
  label,
@@ -56,6 +41,7 @@ export function createFaqGroupEdit(options: FaqGroupOptions) {
56
41
  const ICON_VARIANTS = options.iconVariants ?? DEFAULT_ICON_VARIANTS
57
42
  const ICON_SIZE_PRESETS = options.iconSizePresets ?? DEFAULT_ICON_SIZE_PRESETS
58
43
  const exclude = options.exclude ?? []
44
+ const shadows = options.shadows ?? DEFAULT_SURFACE_SHADOWS
59
45
 
60
46
  return function Edit({
61
47
  attributes,
@@ -123,43 +109,12 @@ export function createFaqGroupEdit(options: FaqGroupOptions) {
123
109
  )}
124
110
  </PanelBody>
125
111
  {!exclude.includes('colors') && style && (
126
- <PanelBody title={__('Styl', 'brilo-blocks')}>
127
- <ToggleGroupControl
128
- isBlock
129
- label={__('Stín', 'brilo-blocks')}
130
- value={style.shadow}
131
- onChange={(v) => setAttributes({style: {...style, shadow: v as FaqShadow}})}
132
- >
133
- {DEFAULT_SHADOWS.map((s) => (
134
- <ToggleGroupControlOption key={s.value} value={s.value} label={s.label} />
135
- ))}
136
- </ToggleGroupControl>
137
- <BaseControl
138
- label={__('Barva okraje', 'brilo-blocks')}
139
- help={__('Bez vybrané barvy se okraj nevykreslí.', 'brilo-blocks')}
140
- id="faq-group-border-color"
141
- >
142
- <ColorPalette
143
- colors={paletteColors}
144
- value={style.borderColor || undefined}
145
- onChange={(value) => setAttributes({style: {...style, borderColor: value ?? ''}})}
146
- />
147
- </BaseControl>
148
- <BaseControl label={__('Barva pozadí', 'brilo-blocks')} id="faq-group-background-color">
149
- <ColorPalette
150
- colors={paletteColors}
151
- value={style.backgroundColor || undefined}
152
- onChange={(value) => setAttributes({style: {...style, backgroundColor: value ?? ''}})}
153
- />
154
- </BaseControl>
155
- <BaseControl label={__('Barva textu', 'brilo-blocks')} id="faq-group-text-color">
156
- <ColorPalette
157
- colors={paletteColors}
158
- value={style.textColor || undefined}
159
- onChange={(value) => setAttributes({style: {...style, textColor: value ?? ''}})}
160
- />
161
- </BaseControl>
162
- </PanelBody>
112
+ <SurfaceStylePanel
113
+ style={style}
114
+ shadows={shadows}
115
+ idPrefix="faq-group"
116
+ onChange={(next) => setAttributes({style: {...style, ...next}})}
117
+ />
163
118
  )}
164
119
  <ItemSpacingPanel spacing={spacing} onChange={(v) => setAttributes({spacing: v})} />
165
120
  </InspectorControls>
@@ -3,7 +3,8 @@ import type {BlockConfiguration} from '@wordpress/blocks'
3
3
  import {__} from '@wordpress/i18n'
4
4
  import {ITEM_SPACING_ATTRIBUTE} from '~/components/ItemSpacingControl'
5
5
  import type {OptionArray} from '~/types'
6
- import {DEFAULT_ICON_SIZE, DEFAULT_ICON_VARIANTS, DEFAULT_SHADOWS, FAQ_HEADING_LEVELS} from './constants'
6
+ import {DEFAULT_SURFACE_SHADOWS, surfaceStyleAttribute} from '~/components/SurfaceStyleControl'
7
+ import {DEFAULT_ICON_SIZE, DEFAULT_ICON_VARIANTS, FAQ_HEADING_LEVELS} from './constants'
7
8
  import {createFaqGroupEdit} from './edit'
8
9
  import type {FaqGroupAttributes, FaqGroupOptions} from './types'
9
10
 
@@ -30,13 +31,12 @@ export function createFaqGroupBlock<TIconVariants extends OptionArray = typeof D
30
31
  }
31
32
 
32
33
  if (!exclude.includes('colors')) {
34
+ const surfaceStyle = surfaceStyleAttribute(options.shadows ?? DEFAULT_SURFACE_SHADOWS)
35
+
33
36
  attributes.style = {
34
- type: 'object',
37
+ ...surfaceStyle,
35
38
  default: {
36
- shadow: DEFAULT_SHADOWS[0].value,
37
- borderColor: '',
38
- backgroundColor: '',
39
- textColor: '',
39
+ ...surfaceStyle.default,
40
40
  icon: {
41
41
  color: '',
42
42
  background: '',
@@ -1,9 +1,10 @@
1
+ import type {SurfaceShadowOption, SurfaceStyle} from '~/components/SurfaceStyleControl'
1
2
  import type {BriloBlockType, OptionArray} from '~/types'
2
- import type {DEFAULT_ICON_VARIANTS, DEFAULT_SHADOWS, FAQ_HEADING_LEVELS} from './constants'
3
+ import type {DEFAULT_ICON_VARIANTS, FAQ_HEADING_LEVELS} from './constants'
3
4
 
4
5
  export type FaqHeadingLevel = (typeof FAQ_HEADING_LEVELS)[number]
5
6
  export type FaqIconVariant = (typeof DEFAULT_ICON_VARIANTS)[number]['value']
6
- export type FaqShadow = (typeof DEFAULT_SHADOWS)[number]['value']
7
+ export type FaqShadow = SurfaceStyle['shadow']
7
8
 
8
9
  export type ExcludableFaqAttr = 'colors'
9
10
 
@@ -15,11 +16,7 @@ export interface FaqSizePreset {
15
16
  export interface FaqGroupAttributes<TIconVariant extends string = FaqIconVariant> extends Record<string, unknown> {
16
17
  headingLevel: FaqHeadingLevel
17
18
  iconVariant: TIconVariant
18
- style: {
19
- shadow: FaqShadow
20
- borderColor: string
21
- backgroundColor: string
22
- textColor: string
19
+ style: SurfaceStyle<string> & {
23
20
  icon: {
24
21
  color: string
25
22
  background: string
@@ -33,4 +30,6 @@ export interface FaqGroupOptions<TIconVariants extends OptionArray = typeof DEFA
33
30
  iconVariants?: TIconVariants
34
31
  iconSizePresets?: readonly FaqSizePreset[]
35
32
  exclude?: ExcludableFaqAttr[]
33
+ /** Overrides the shared shadow presets — see `DEFAULT_SURFACE_SHADOWS`. */
34
+ shadows?: readonly SurfaceShadowOption[]
36
35
  }
@@ -0,0 +1,19 @@
1
+ import {__} from '@wordpress/i18n'
2
+ import {justifyBottom, justifyLeft, justifyRight, justifyTop} from '@wordpress/icons'
3
+ import type {ReactElement} from 'react'
4
+ import type {BoxSpacingSide} from './types'
5
+
6
+ /** Sides shared by the padding and margin panels, in CSS shorthand order. */
7
+ export const BOX_SPACING_SIDES: readonly {value: BoxSpacingSide; label: string; icon: ReactElement}[] = [
8
+ {value: 'top', label: __('Nahoře', 'brilo-blocks'), icon: justifyTop},
9
+ {value: 'right', label: __('Vpravo', 'brilo-blocks'), icon: justifyRight},
10
+ {value: 'bottom', label: __('Dole', 'brilo-blocks'), icon: justifyBottom},
11
+ {value: 'left', label: __('Vlevo', 'brilo-blocks'), icon: justifyLeft},
12
+ ] as const
13
+
14
+ // All box lengths are plain pixel numbers — the same convention as the shared
15
+ // `spacing` attribute — so the editor needs no unit picker and the template
16
+ // appends the unit.
17
+ export const BOX_SPACING_MAX = 120
18
+ export const BOX_BORDER_WIDTH_MAX = 20
19
+ export const BOX_BORDER_RADIUS_MAX = 64
@@ -1,43 +1,55 @@
1
- import * as blockEditor from '@wordpress/block-editor'
2
1
  import {ColorPalette, ContrastChecker} from '@wordpress/block-editor'
3
- import {BoxControl, PanelBody, __experimentalUnitControl as UnitControl} from '@wordpress/components'
2
+ import {PanelBody, RangeControl} from '@wordpress/components'
4
3
  import {__} from '@wordpress/i18n'
5
- import type {ComponentType} from 'react'
6
4
  import {useTenantColorPalette} from '~/hooks/useTenantColorPalette'
7
- import type {BoxAttributes, BoxBorderRadius} from './types'
8
-
9
- // `__experimentalBorderRadiusControl` is a real runtime export of @wordpress/block-editor (see
10
- // packages/block-editor/src/components/border-radius-control) but isn't covered by the community
11
- // @types/wordpress__block-editor typings, so it's pulled off the namespace import and cast by hand.
12
- interface BorderRadiusControlProps {
13
- values?: string | BoxBorderRadius
14
- onChange: (next: string | BoxBorderRadius | undefined) => void
15
- }
16
- const BorderRadiusControl = (blockEditor as unknown as {__experimentalBorderRadiusControl: ComponentType<BorderRadiusControlProps>})
17
- .__experimentalBorderRadiusControl
5
+ import {BOX_BORDER_RADIUS_MAX, BOX_BORDER_WIDTH_MAX, BOX_SPACING_MAX, BOX_SPACING_SIDES} from './constants'
6
+ import type {BoxAttributes, BoxSpacing} from './types'
18
7
 
19
8
  interface BoxControlsProps {
20
9
  attributes: BoxAttributes
21
10
  setAttributes: (attrs: Partial<BoxAttributes>) => void
22
11
  }
23
12
 
13
+ function SpacingPanel({title, values, onChange}: {title: string; values: BoxSpacing; onChange: (next: BoxSpacing) => void}) {
14
+ return (
15
+ <PanelBody title={title}>
16
+ {BOX_SPACING_SIDES.map((side) => (
17
+ <RangeControl
18
+ key={side.value}
19
+ label={side.label}
20
+ beforeIcon={side.icon}
21
+ value={values?.[side.value]}
22
+ onChange={(v) => onChange({...values, [side.value]: v})}
23
+ min={0}
24
+ max={BOX_SPACING_MAX}
25
+ step={1}
26
+ allowReset
27
+ withInputField
28
+ />
29
+ ))}
30
+ </PanelBody>
31
+ )
32
+ }
33
+
24
34
  export function BoxControls({attributes, setAttributes}: BoxControlsProps) {
25
35
  const {padding, margin, borderRadius, borderWidth, borderColor, background, textColor} = attributes
26
36
  const {paletteColors, resolveHex} = useTenantColorPalette()
27
37
 
28
38
  return (
29
39
  <>
30
- <PanelBody title={__('Rozestupy', 'brilo-blocks')}>
31
- <BoxControl label={__('Padding', 'brilo-blocks')} values={padding} onChange={(next) => setAttributes({padding: next})} />
32
- <BoxControl label={__('Margin', 'brilo-blocks')} values={margin} onChange={(next) => setAttributes({margin: next})} />
33
- </PanelBody>
40
+ <SpacingPanel title={__('Padding', 'brilo-blocks')} values={padding} onChange={(next) => setAttributes({padding: next})} />
41
+ <SpacingPanel title={__('Margin', 'brilo-blocks')} values={margin} onChange={(next) => setAttributes({margin: next})} />
34
42
 
35
43
  <PanelBody title={__('Ohraničení', 'brilo-blocks')}>
36
- <UnitControl
44
+ <RangeControl
37
45
  label={__('Šířka okraje', 'brilo-blocks')}
38
46
  value={borderWidth}
47
+ onChange={(v) => setAttributes({borderWidth: v ?? 0})}
39
48
  min={0}
40
- onChange={(next) => setAttributes({borderWidth: (next as string) ?? ''})}
49
+ max={BOX_BORDER_WIDTH_MAX}
50
+ step={1}
51
+ allowReset
52
+ withInputField
41
53
  />
42
54
  <p>{__('Barva okraje', 'brilo-blocks')}</p>
43
55
  <ColorPalette
@@ -45,7 +57,16 @@ export function BoxControls({attributes, setAttributes}: BoxControlsProps) {
45
57
  value={borderColor || undefined}
46
58
  onChange={(value) => setAttributes({borderColor: value ?? ''})}
47
59
  />
48
- <BorderRadiusControl values={borderRadius} onChange={(next) => setAttributes({borderRadius: next ?? ''})} />
60
+ <RangeControl
61
+ label={__('Zaoblení rohů', 'brilo-blocks')}
62
+ value={borderRadius}
63
+ onChange={(v) => setAttributes({borderRadius: v ?? 0})}
64
+ min={0}
65
+ max={BOX_BORDER_RADIUS_MAX}
66
+ step={1}
67
+ allowReset
68
+ withInputField
69
+ />
49
70
  </PanelBody>
50
71
 
51
72
  <PanelBody title={__('Barvy', 'brilo-blocks')}>
@@ -0,0 +1,146 @@
1
+ import {InnerBlocks} from '@wordpress/block-editor'
2
+ import type {BoxSpacing, BoxSpacingSide} from './types'
3
+
4
+ /**
5
+ * v1 stored every length as a CSS string (`"16px"`, `"1rem"`) and allowed a
6
+ * per-corner `borderRadius` object. v2 stores plain pixel numbers, so posts
7
+ * saved by v1 have to be migrated — the save output is unchanged
8
+ * (`<InnerBlocks.Content />`), which means block validation never fails and
9
+ * would otherwise leave the legacy values in place, silently rendering
10
+ * `16pxpx`. `isEligible` is what forces the migration.
11
+ */
12
+
13
+ const BOX_SIDES: readonly BoxSpacingSide[] = ['top', 'right', 'bottom', 'left']
14
+
15
+ interface LegacySpacing {
16
+ top?: string
17
+ right?: string
18
+ bottom?: string
19
+ left?: string
20
+ }
21
+
22
+ interface LegacyBorderRadius {
23
+ topLeft?: string
24
+ topRight?: string
25
+ bottomLeft?: string
26
+ bottomRight?: string
27
+ }
28
+
29
+ export interface LegacyBoxAttributes extends Record<string, unknown> {
30
+ padding: LegacySpacing
31
+ margin: LegacySpacing
32
+ borderRadius: string | LegacyBorderRadius
33
+ borderWidth: string
34
+ borderColor: string
35
+ background: string
36
+ textColor: string
37
+ }
38
+
39
+ /**
40
+ * Parses a v1 CSS length into pixels. `rem`/`em` are approximated at the
41
+ * 16px browser default; anything else unitless-or-px is taken at face value.
42
+ * Unparseable values become 0, which renders as "no declaration".
43
+ */
44
+ function toPixels(value: unknown): number {
45
+ if (typeof value === 'number') {
46
+ return Number.isFinite(value) ? value : 0
47
+ }
48
+
49
+ if (typeof value !== 'string') {
50
+ return 0
51
+ }
52
+
53
+ const match = value.trim().match(/^(-?\d*\.?\d+)\s*(px|rem|em)?$/)
54
+
55
+ if (!match) {
56
+ return 0
57
+ }
58
+
59
+ const amount = Number.parseFloat(match[1])
60
+
61
+ if (!Number.isFinite(amount)) {
62
+ return 0
63
+ }
64
+
65
+ return match[2] === 'rem' || match[2] === 'em' ? Math.round(amount * 16) : Math.round(amount)
66
+ }
67
+
68
+ function migrateSpacing(spacing: LegacySpacing | undefined): BoxSpacing {
69
+ const next: BoxSpacing = {}
70
+
71
+ for (const side of BOX_SIDES) {
72
+ const pixels = toPixels(spacing?.[side])
73
+
74
+ if (pixels !== 0) {
75
+ next[side] = pixels
76
+ }
77
+ }
78
+
79
+ return next
80
+ }
81
+
82
+ /**
83
+ * v1 could set each corner separately; v2 has a single radius. The first
84
+ * non-zero corner wins — lossy by design, and only for content that actually
85
+ * used per-corner radii.
86
+ */
87
+ function migrateBorderRadius(radius: string | LegacyBorderRadius | undefined): number {
88
+ if (radius && typeof radius === 'object') {
89
+ for (const corner of ['topLeft', 'topRight', 'bottomRight', 'bottomLeft'] as const) {
90
+ const pixels = toPixels(radius[corner])
91
+
92
+ if (pixels !== 0) {
93
+ return pixels
94
+ }
95
+ }
96
+
97
+ return 0
98
+ }
99
+
100
+ return toPixels(radius)
101
+ }
102
+
103
+ /** A v1 value still needing conversion: a non-empty string, or a corner object. */
104
+ function isLegacyLength(value: unknown): boolean {
105
+ return (typeof value === 'string' && value !== '') || (!!value && typeof value === 'object')
106
+ }
107
+
108
+ function hasLegacyLengths(attributes: LegacyBoxAttributes): boolean {
109
+ for (const spacing of [attributes.padding, attributes.margin]) {
110
+ for (const side of BOX_SIDES) {
111
+ if (isLegacyLength(spacing?.[side])) {
112
+ return true
113
+ }
114
+ }
115
+ }
116
+
117
+ return isLegacyLength(attributes.borderWidth) || isLegacyLength(attributes.borderRadius)
118
+ }
119
+
120
+ export const boxDeprecated = [
121
+ {
122
+ attributes: {
123
+ padding: {type: 'object', default: {}},
124
+ margin: {type: 'object', default: {}},
125
+ borderRadius: {type: ['string', 'object'], default: ''},
126
+ borderWidth: {type: 'string', default: ''},
127
+ borderColor: {type: 'string', default: ''},
128
+ background: {type: 'string', default: ''},
129
+ textColor: {type: 'string', default: ''},
130
+ },
131
+ supports: {html: false, customClassName: false},
132
+ isEligible: (attributes: Record<string, unknown>) => hasLegacyLengths(attributes as LegacyBoxAttributes),
133
+ migrate: (attributes: Record<string, unknown>) => {
134
+ const legacy = attributes as LegacyBoxAttributes
135
+
136
+ return {
137
+ ...legacy,
138
+ padding: migrateSpacing(legacy.padding),
139
+ margin: migrateSpacing(legacy.margin),
140
+ borderRadius: migrateBorderRadius(legacy.borderRadius),
141
+ borderWidth: toPixels(legacy.borderWidth),
142
+ }
143
+ },
144
+ save: () => <InnerBlocks.Content />,
145
+ },
146
+ ]
@@ -4,29 +4,26 @@ import type {CSSProperties} from 'react'
4
4
  import {BoxControls} from './controls'
5
5
  import type {BoxAttributes, BoxOptions} from './types'
6
6
 
7
+ /** Lengths are stored as plain pixel numbers; the unit is added on render. */
8
+ function px(value: number | undefined): string | undefined {
9
+ return typeof value === 'number' && value !== 0 ? `${value}px` : undefined
10
+ }
11
+
7
12
  export function createBoxEdit(options: BoxOptions) {
8
13
  return function Edit({attributes, setAttributes}: BlockEditProps<BoxAttributes>) {
9
14
  const {padding, margin, borderRadius, borderWidth, borderColor, background, textColor} = attributes
10
15
 
11
16
  const style: CSSProperties = {
12
- ...(padding?.top && {paddingTop: padding.top}),
13
- ...(padding?.right && {paddingRight: padding.right}),
14
- ...(padding?.bottom && {paddingBottom: padding.bottom}),
15
- ...(padding?.left && {paddingLeft: padding.left}),
16
- ...(margin?.top && {marginTop: margin.top}),
17
- ...(margin?.right && {marginRight: margin.right}),
18
- ...(margin?.bottom && {marginBottom: margin.bottom}),
19
- ...(margin?.left && {marginLeft: margin.left}),
20
- ...(typeof borderRadius === 'string' && borderRadius && {borderRadius}),
21
- ...(typeof borderRadius === 'object' && borderRadius !== null
22
- ? {
23
- ...(borderRadius.topLeft && {borderTopLeftRadius: borderRadius.topLeft}),
24
- ...(borderRadius.topRight && {borderTopRightRadius: borderRadius.topRight}),
25
- ...(borderRadius.bottomLeft && {borderBottomLeftRadius: borderRadius.bottomLeft}),
26
- ...(borderRadius.bottomRight && {borderBottomRightRadius: borderRadius.bottomRight}),
27
- }
28
- : {}),
29
- ...(borderWidth && {borderWidth, borderStyle: 'solid' as CSSProperties['borderStyle']}),
17
+ ...(px(padding?.top) && {paddingTop: px(padding?.top)}),
18
+ ...(px(padding?.right) && {paddingRight: px(padding?.right)}),
19
+ ...(px(padding?.bottom) && {paddingBottom: px(padding?.bottom)}),
20
+ ...(px(padding?.left) && {paddingLeft: px(padding?.left)}),
21
+ ...(px(margin?.top) && {marginTop: px(margin?.top)}),
22
+ ...(px(margin?.right) && {marginRight: px(margin?.right)}),
23
+ ...(px(margin?.bottom) && {marginBottom: px(margin?.bottom)}),
24
+ ...(px(margin?.left) && {marginLeft: px(margin?.left)}),
25
+ ...(px(borderRadius) && {borderRadius: px(borderRadius)}),
26
+ ...(px(borderWidth) && {borderWidth: px(borderWidth), borderStyle: 'solid' as CSSProperties['borderStyle']}),
30
27
  ...(borderColor && {borderColor}),
31
28
  ...(background && {backgroundColor: background}),
32
29
  ...(textColor && {color: textColor}),
@@ -1,6 +1,7 @@
1
1
  import {InnerBlocks} from '@wordpress/block-editor'
2
2
  import type {BlockConfiguration} from '@wordpress/blocks'
3
3
  import {__} from '@wordpress/i18n'
4
+ import {boxDeprecated} from './deprecated'
4
5
  import {createBoxEdit} from './edit'
5
6
  import {icon as defaultIcon} from './icon'
6
7
  import type {BoxAttributes, BoxOptions} from './types'
@@ -19,12 +20,13 @@ export function createBoxBlock(options: BoxOptions): BlockConfiguration<BoxAttri
19
20
  attributes: {
20
21
  padding: {type: 'object', default: {}},
21
22
  margin: {type: 'object', default: {}},
22
- borderRadius: {type: ['string', 'object'], default: ''},
23
- borderWidth: {type: 'string', default: ''},
23
+ borderRadius: {type: 'number', default: 0},
24
+ borderWidth: {type: 'number', default: 0},
24
25
  borderColor: {type: 'string', default: ''},
25
26
  background: {type: 'string', default: ''},
26
27
  textColor: {type: 'string', default: ''},
27
28
  },
29
+ deprecated: boxDeprecated,
28
30
  edit: createBoxEdit(options),
29
31
  save: () => <InnerBlocks.Content />,
30
32
  } as BlockConfiguration<BoxAttributes>
@@ -1,4 +1,4 @@
1
- export type {BoxAttributes, BoxBorderRadius, BoxOptions, BoxSpacing} from './types'
1
+ export type {BoxAttributes, BoxOptions, BoxSpacing, BoxSpacingSide} from './types'
2
2
  export {createBoxBlock} from './factory'
3
3
 
4
4
  import type {BoxOptions} from './types'
@@ -1,24 +1,15 @@
1
1
  import type {BriloBlockType} from '~/types'
2
2
 
3
- export interface BoxSpacing {
4
- top?: string
5
- right?: string
6
- bottom?: string
7
- left?: string
8
- }
3
+ export type BoxSpacingSide = 'top' | 'right' | 'bottom' | 'left'
9
4
 
10
- export interface BoxBorderRadius {
11
- topLeft?: string
12
- topRight?: string
13
- bottomLeft?: string
14
- bottomRight?: string
15
- }
5
+ /** Per-side spacing in pixels. An unset side means "no declaration". */
6
+ export type BoxSpacing = Partial<Record<BoxSpacingSide, number>>
16
7
 
17
8
  export interface BoxAttributes extends Record<string, unknown> {
18
9
  padding: BoxSpacing
19
10
  margin: BoxSpacing
20
- borderRadius: string | BoxBorderRadius
21
- borderWidth: string
11
+ borderRadius: number
12
+ borderWidth: number
22
13
  borderColor: string
23
14
  background: string
24
15
  textColor: string
@@ -1,6 +1,7 @@
1
1
  import apiFetch from '@wordpress/api-fetch'
2
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'
3
+ import type {Field, View} from '@wordpress/dataviews/wp'
4
+ import {LazyDataViews as DataViews} from '~/components/LazyDataViews/LazyDataViews'
4
5
  import {__} from '@wordpress/i18n'
5
6
  import {arrowDown, arrowUp, close, plusCircle} from '@wordpress/icons'
6
7
  // biome-ignore lint/correctness/noUnusedImports: required in scope for classic JSX transform
@@ -0,0 +1,17 @@
1
+ import {Spinner} from '@wordpress/components'
2
+ import type {DataViews as DataViewsComponent} from '@wordpress/dataviews/wp'
3
+ // biome-ignore lint/correctness/noUnusedImports: required in scope for classic JSX transform
4
+ import React, {type ComponentProps, lazy, Suspense} from 'react'
5
+
6
+ const DataViews = lazy(() => import('@wordpress/dataviews/wp').then((module) => ({default: module.DataViews})))
7
+
8
+ /**
9
+ * `@wordpress/dataviews/wp` is a ~2 MB prebundle that WP core doesn't expose as a script handle,
10
+ * so a static import gets copied into every block entry that uses it. Loading it via `import()`
11
+ * lets webpack emit a single shared async chunk, fetched only once a picker modal is opened.
12
+ */
13
+ export const LazyDataViews = ((props: ComponentProps<typeof DataViewsComponent>) => (
14
+ <Suspense fallback={<Spinner />}>
15
+ <DataViews {...props} />
16
+ </Suspense>
17
+ )) as typeof DataViewsComponent
@@ -0,0 +1,114 @@
1
+ import {
2
+ BaseControl,
3
+ ColorPalette,
4
+ PanelBody,
5
+ __experimentalToggleGroupControl as ToggleGroupControl,
6
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
7
+ } from '@wordpress/components'
8
+ import {__} from '@wordpress/i18n'
9
+ import {useTenantColorPalette} from '~/hooks/useTenantColorPalette'
10
+
11
+ /**
12
+ * Shadow presets shared by every "surface" block (accordion, faq, ...). The
13
+ * `DEFAULT_` prefix marks it as tenant-overridable — pass your own array as
14
+ * `options.shadows` and the factory/edit resolve it with `??`.
15
+ */
16
+ export const DEFAULT_SURFACE_SHADOWS = [
17
+ {value: 'none', label: __('Žádný', 'brilo-blocks')},
18
+ {value: 'sm', label: __('Malý', 'brilo-blocks')},
19
+ {value: 'md', label: __('Střední', 'brilo-blocks')},
20
+ ] as const
21
+
22
+ export type SurfaceShadow = (typeof DEFAULT_SURFACE_SHADOWS)[number]['value']
23
+
24
+ export interface SurfaceShadowOption {
25
+ value: string
26
+ label: string
27
+ }
28
+
29
+ /**
30
+ * The shadow + colour quartet that group blocks hand down to their items. Kept
31
+ * as one named type so the shape lives in a single place across TS, PHP and twig.
32
+ */
33
+ export interface SurfaceStyle<TShadow extends string = SurfaceShadow> {
34
+ shadow: TShadow
35
+ borderColor: string
36
+ backgroundColor: string
37
+ textColor: string
38
+ }
39
+
40
+ /** Attribute default for a `style` object holding a {@link SurfaceStyle}. */
41
+ export function surfaceStyleAttribute(shadows: readonly SurfaceShadowOption[] = DEFAULT_SURFACE_SHADOWS) {
42
+ return {
43
+ type: 'object',
44
+ default: {
45
+ shadow: shadows[0]?.value ?? 'none',
46
+ borderColor: '',
47
+ backgroundColor: '',
48
+ textColor: '',
49
+ },
50
+ } as const
51
+ }
52
+
53
+ /**
54
+ * Inspector panel for {@link SurfaceStyle}. `idPrefix` keeps the `BaseControl`
55
+ * ids unique per block (`faq-group`, `accordion-group`, ...).
56
+ */
57
+ export function SurfaceStylePanel<TStyle extends SurfaceStyle<string>>({
58
+ style,
59
+ onChange,
60
+ idPrefix,
61
+ shadows = DEFAULT_SURFACE_SHADOWS,
62
+ title = __('Styl', 'brilo-blocks'),
63
+ children,
64
+ }: {
65
+ style: TStyle
66
+ onChange: (style: TStyle) => void
67
+ idPrefix: string
68
+ shadows?: readonly SurfaceShadowOption[]
69
+ title?: string
70
+ children?: React.ReactNode
71
+ }) {
72
+ const {paletteColors} = useTenantColorPalette()
73
+
74
+ return (
75
+ <PanelBody title={title}>
76
+ <ToggleGroupControl
77
+ isBlock
78
+ label={__('Stín', 'brilo-blocks')}
79
+ value={style.shadow}
80
+ onChange={(value) => onChange({...style, shadow: value as TStyle['shadow']})}
81
+ >
82
+ {shadows.map((shadow) => (
83
+ <ToggleGroupControlOption key={shadow.value} value={shadow.value} label={shadow.label} />
84
+ ))}
85
+ </ToggleGroupControl>
86
+ <BaseControl
87
+ label={__('Barva okraje', 'brilo-blocks')}
88
+ help={__('Bez vybrané barvy se okraj nevykreslí.', 'brilo-blocks')}
89
+ id={`${idPrefix}-border-color`}
90
+ >
91
+ <ColorPalette
92
+ colors={paletteColors}
93
+ value={style.borderColor || undefined}
94
+ onChange={(value) => onChange({...style, borderColor: value ?? ''})}
95
+ />
96
+ </BaseControl>
97
+ <BaseControl label={__('Barva pozadí', 'brilo-blocks')} id={`${idPrefix}-background-color`}>
98
+ <ColorPalette
99
+ colors={paletteColors}
100
+ value={style.backgroundColor || undefined}
101
+ onChange={(value) => onChange({...style, backgroundColor: value ?? ''})}
102
+ />
103
+ </BaseControl>
104
+ <BaseControl label={__('Barva textu', 'brilo-blocks')} id={`${idPrefix}-text-color`}>
105
+ <ColorPalette
106
+ colors={paletteColors}
107
+ value={style.textColor || undefined}
108
+ onChange={(value) => onChange({...style, textColor: value ?? ''})}
109
+ />
110
+ </BaseControl>
111
+ {children}
112
+ </PanelBody>
113
+ )
114
+ }