@wordpress/block-editor 10.0.6 → 10.0.8
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/components/block-inspector/index.js +3 -2
- package/build/components/block-inspector/index.js.map +1 -1
- package/build/components/font-sizes/fluid-utils.js +69 -21
- package/build/components/font-sizes/fluid-utils.js.map +1 -1
- package/build/components/iframe/index.js +1 -1
- package/build/components/iframe/index.js.map +1 -1
- package/build/components/inner-blocks/use-inner-block-template-sync.js +3 -2
- package/build/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
- package/build/components/preview-options/index.js +2 -3
- package/build/components/preview-options/index.js.map +1 -1
- package/build/components/spacing-sizes-control/spacing-input-control.js +4 -2
- package/build/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
- package/build-module/components/block-inspector/index.js +3 -2
- package/build-module/components/block-inspector/index.js.map +1 -1
- package/build-module/components/font-sizes/fluid-utils.js +69 -21
- package/build-module/components/font-sizes/fluid-utils.js.map +1 -1
- package/build-module/components/iframe/index.js +1 -1
- package/build-module/components/iframe/index.js.map +1 -1
- package/build-module/components/inner-blocks/use-inner-block-template-sync.js +3 -2
- package/build-module/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
- package/build-module/components/preview-options/index.js +2 -3
- package/build-module/components/preview-options/index.js.map +1 -1
- package/build-module/components/spacing-sizes-control/spacing-input-control.js +4 -2
- package/build-module/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
- package/build-style/style-rtl.css +27 -21
- package/build-style/style.css +27 -21
- package/package.json +3 -3
- package/src/components/block-inspector/index.js +6 -3
- package/src/components/block-patterns-list/style.scss +5 -0
- package/src/components/button-block-appender/style.scss +3 -2
- package/src/components/font-sizes/fluid-utils.js +100 -25
- package/src/components/font-sizes/test/fluid-utils.js +5 -5
- package/src/components/iframe/index.js +1 -1
- package/src/components/inner-blocks/use-inner-block-template-sync.js +3 -2
- package/src/components/preview-options/index.js +2 -2
- package/src/components/spacing-sizes-control/spacing-input-control.js +2 -0
- package/src/components/spacing-sizes-control/style.scss +26 -19
|
@@ -10,6 +10,7 @@ const DEFAULT_MINIMUM_VIEWPORT_WIDTH = '768px';
|
|
|
10
10
|
const DEFAULT_SCALE_FACTOR = 1;
|
|
11
11
|
const DEFAULT_MINIMUM_FONT_SIZE_FACTOR = 0.75;
|
|
12
12
|
const DEFAULT_MAXIMUM_FONT_SIZE_FACTOR = 1.5;
|
|
13
|
+
const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Computes a fluid font-size value that uses clamp(). A minimum and maxinmum
|
|
@@ -53,11 +54,20 @@ export function getComputedFluidTypographyValue( {
|
|
|
53
54
|
scaleFactor = DEFAULT_SCALE_FACTOR,
|
|
54
55
|
minimumFontSizeFactor = DEFAULT_MINIMUM_FONT_SIZE_FACTOR,
|
|
55
56
|
maximumFontSizeFactor = DEFAULT_MAXIMUM_FONT_SIZE_FACTOR,
|
|
57
|
+
minimumFontSizeLimit = DEFAULT_MINIMUM_FONT_SIZE_LIMIT,
|
|
56
58
|
} ) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/*
|
|
60
|
+
* Caches minimumFontSize in minimumFontSizeValue
|
|
61
|
+
* so we can check if minimumFontSize exists later.
|
|
62
|
+
*/
|
|
63
|
+
let minimumFontSizeValue = minimumFontSize;
|
|
64
|
+
|
|
65
|
+
/*
|
|
66
|
+
* Calculates missing minimumFontSize and maximumFontSize from
|
|
67
|
+
* defaultFontSize if provided.
|
|
68
|
+
*/
|
|
69
|
+
if ( fontSize ) {
|
|
70
|
+
// Parses default font size.
|
|
61
71
|
const fontSizeParsed = getTypographyValueAndUnit( fontSize );
|
|
62
72
|
|
|
63
73
|
// Protect against invalid units.
|
|
@@ -66,46 +76,95 @@ export function getComputedFluidTypographyValue( {
|
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
// If no minimumFontSize is provided, derive using min scale factor.
|
|
69
|
-
if ( !
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
if ( ! minimumFontSizeValue ) {
|
|
80
|
+
minimumFontSizeValue =
|
|
81
|
+
roundToPrecision(
|
|
82
|
+
fontSizeParsed.value * minimumFontSizeFactor,
|
|
83
|
+
3
|
|
84
|
+
) + fontSizeParsed.unit;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Parses the minimum font size limit, so we can perform checks using it.
|
|
88
|
+
const minimumFontSizeLimitParsed = getTypographyValueAndUnit(
|
|
89
|
+
minimumFontSizeLimit,
|
|
90
|
+
{
|
|
91
|
+
coerceTo: fontSizeParsed.unit,
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if ( !! minimumFontSizeLimitParsed?.value ) {
|
|
96
|
+
/*
|
|
97
|
+
* If a minimum size was not passed to this function
|
|
98
|
+
* and the user-defined font size is lower than `minimumFontSizeLimit`,
|
|
99
|
+
* then uses the user-defined font size as the minimum font-size.
|
|
100
|
+
*/
|
|
101
|
+
if (
|
|
102
|
+
! minimumFontSize &&
|
|
103
|
+
fontSizeParsed?.value < minimumFontSizeLimitParsed?.value
|
|
104
|
+
) {
|
|
105
|
+
minimumFontSizeValue = `${ fontSizeParsed.value }${ fontSizeParsed.unit }`;
|
|
106
|
+
} else {
|
|
107
|
+
const minimumFontSizeParsed = getTypographyValueAndUnit(
|
|
108
|
+
minimumFontSizeValue,
|
|
109
|
+
{
|
|
110
|
+
coerceTo: fontSizeParsed.unit,
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* Otherwise, if the passed or calculated minimum font size is lower than `minimumFontSizeLimit`
|
|
116
|
+
* use `minimumFontSizeLimit` instead.
|
|
117
|
+
*/
|
|
118
|
+
if (
|
|
119
|
+
!! minimumFontSizeParsed?.value &&
|
|
120
|
+
minimumFontSizeParsed.value <
|
|
121
|
+
minimumFontSizeLimitParsed.value
|
|
122
|
+
) {
|
|
123
|
+
minimumFontSizeValue = `${ minimumFontSizeLimitParsed.value }${ minimumFontSizeLimitParsed.unit }`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
73
126
|
}
|
|
74
127
|
|
|
75
128
|
// If no maximumFontSize is provided, derive using max scale factor.
|
|
76
129
|
if ( ! maximumFontSize ) {
|
|
77
130
|
maximumFontSize =
|
|
78
|
-
|
|
79
|
-
|
|
131
|
+
roundToPrecision(
|
|
132
|
+
fontSizeParsed.value * maximumFontSizeFactor,
|
|
133
|
+
3
|
|
134
|
+
) + fontSizeParsed.unit;
|
|
80
135
|
}
|
|
81
136
|
}
|
|
82
137
|
|
|
83
138
|
// Return early if one of the provided inputs is not provided.
|
|
84
|
-
if ( !
|
|
139
|
+
if ( ! minimumFontSizeValue || ! maximumFontSize ) {
|
|
85
140
|
return null;
|
|
86
141
|
}
|
|
87
142
|
|
|
88
143
|
// Grab the minimum font size and normalize it in order to use the value for calculations.
|
|
89
|
-
const minimumFontSizeParsed =
|
|
144
|
+
const minimumFontSizeParsed =
|
|
145
|
+
getTypographyValueAndUnit( minimumFontSizeValue );
|
|
90
146
|
|
|
91
147
|
// We get a 'preferred' unit to keep units consistent when calculating,
|
|
92
148
|
// otherwise the result will not be accurate.
|
|
93
149
|
const fontSizeUnit = minimumFontSizeParsed?.unit || 'rem';
|
|
94
150
|
|
|
95
|
-
//
|
|
151
|
+
// Grabs the maximum font size and normalize it in order to use the value for calculations.
|
|
96
152
|
const maximumFontSizeParsed = getTypographyValueAndUnit( maximumFontSize, {
|
|
97
153
|
coerceTo: fontSizeUnit,
|
|
98
154
|
} );
|
|
99
155
|
|
|
100
|
-
//
|
|
156
|
+
// Checks for mandatory min and max sizes, and protects against unsupported units.
|
|
101
157
|
if ( ! minimumFontSizeParsed || ! maximumFontSizeParsed ) {
|
|
102
158
|
return null;
|
|
103
159
|
}
|
|
104
160
|
|
|
105
|
-
//
|
|
106
|
-
const minimumFontSizeRem = getTypographyValueAndUnit(
|
|
107
|
-
|
|
108
|
-
|
|
161
|
+
// Uses rem for accessible fluid target font scaling.
|
|
162
|
+
const minimumFontSizeRem = getTypographyValueAndUnit(
|
|
163
|
+
minimumFontSizeValue,
|
|
164
|
+
{
|
|
165
|
+
coerceTo: 'rem',
|
|
166
|
+
}
|
|
167
|
+
);
|
|
109
168
|
|
|
110
169
|
// Viewport widths defined for fluid typography. Normalize units
|
|
111
170
|
const maximumViewPortWidthParsed = getTypographyValueAndUnit(
|
|
@@ -133,17 +192,20 @@ export function getComputedFluidTypographyValue( {
|
|
|
133
192
|
3
|
|
134
193
|
);
|
|
135
194
|
|
|
136
|
-
const viewPortWidthOffset =
|
|
137
|
-
|
|
195
|
+
const viewPortWidthOffset =
|
|
196
|
+
roundToPrecision( minViewPortWidthOffsetValue, 3 ) + fontSizeUnit;
|
|
197
|
+
const linearFactor =
|
|
138
198
|
100 *
|
|
139
199
|
( ( maximumFontSizeParsed.value - minimumFontSizeParsed.value ) /
|
|
140
200
|
( maximumViewPortWidthParsed.value -
|
|
141
201
|
minumumViewPortWidthParsed.value ) );
|
|
142
|
-
|
|
143
|
-
|
|
202
|
+
const linearFactorScaled = roundToPrecision(
|
|
203
|
+
( linearFactor || 1 ) * scaleFactor,
|
|
204
|
+
3
|
|
205
|
+
);
|
|
144
206
|
const fluidTargetFontSize = `${ minimumFontSizeRem.value }${ minimumFontSizeRem.unit } + ((1vw - ${ viewPortWidthOffset }) * ${ linearFactorScaled })`;
|
|
145
207
|
|
|
146
|
-
return `clamp(${
|
|
208
|
+
return `clamp(${ minimumFontSizeValue }, ${ fluidTargetFontSize }, ${ maximumFontSize })`;
|
|
147
209
|
}
|
|
148
210
|
|
|
149
211
|
/**
|
|
@@ -199,8 +261,20 @@ export function getTypographyValueAndUnit( rawValue, options = {} ) {
|
|
|
199
261
|
unit = coerceTo;
|
|
200
262
|
}
|
|
201
263
|
|
|
264
|
+
/*
|
|
265
|
+
* No calculation is required if swapping between em and rem yet,
|
|
266
|
+
* since we assume a root size value. Later we might like to differentiate between
|
|
267
|
+
* :root font size (rem) and parent element font size (em) relativity.
|
|
268
|
+
*/
|
|
269
|
+
if (
|
|
270
|
+
( 'em' === coerceTo || 'rem' === coerceTo ) &&
|
|
271
|
+
( 'em' === unit || 'rem' === unit )
|
|
272
|
+
) {
|
|
273
|
+
unit = coerceTo;
|
|
274
|
+
}
|
|
275
|
+
|
|
202
276
|
return {
|
|
203
|
-
value: returnValue,
|
|
277
|
+
value: roundToPrecision( returnValue, 3 ),
|
|
204
278
|
unit,
|
|
205
279
|
};
|
|
206
280
|
}
|
|
@@ -215,7 +289,8 @@ export function getTypographyValueAndUnit( rawValue, options = {} ) {
|
|
|
215
289
|
* @return {number|undefined} Value rounded to standard precision.
|
|
216
290
|
*/
|
|
217
291
|
export function roundToPrecision( value, digits = 3 ) {
|
|
292
|
+
const base = Math.pow( 10, digits );
|
|
218
293
|
return Number.isFinite( value )
|
|
219
|
-
? parseFloat(
|
|
294
|
+
? parseFloat( Math.round( value * base ) / base )
|
|
220
295
|
: undefined;
|
|
221
296
|
}
|
|
@@ -33,7 +33,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
|
|
|
33
33
|
fontSize: '30px',
|
|
34
34
|
} );
|
|
35
35
|
expect( fluidTypographyValues ).toBe(
|
|
36
|
-
'clamp(22.5px, 1.
|
|
36
|
+
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)'
|
|
37
37
|
);
|
|
38
38
|
} );
|
|
39
39
|
|
|
@@ -42,7 +42,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
|
|
|
42
42
|
fontSize: '30px',
|
|
43
43
|
} );
|
|
44
44
|
expect( fluidTypographyValues ).toBe(
|
|
45
|
-
'clamp(22.5px, 1.
|
|
45
|
+
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)'
|
|
46
46
|
);
|
|
47
47
|
} );
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
|
|
|
53
53
|
maximumViewPortWidth: '1000px',
|
|
54
54
|
} );
|
|
55
55
|
expect( fluidTypographyValues ).toBe(
|
|
56
|
-
'clamp(22.5px, 1.
|
|
56
|
+
'clamp(22.5px, 1.406rem + ((1vw - 5px) * 4.5), 45px)'
|
|
57
57
|
);
|
|
58
58
|
} );
|
|
59
59
|
|
|
@@ -63,7 +63,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
|
|
|
63
63
|
scaleFactor: '2',
|
|
64
64
|
} );
|
|
65
65
|
expect( fluidTypographyValues ).toBe(
|
|
66
|
-
'clamp(22.5px, 1.
|
|
66
|
+
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 5.409), 45px)'
|
|
67
67
|
);
|
|
68
68
|
} );
|
|
69
69
|
|
|
@@ -74,7 +74,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
|
|
|
74
74
|
maximumFontSizeFactor: '2',
|
|
75
75
|
} );
|
|
76
76
|
expect( fluidTypographyValues ).toBe(
|
|
77
|
-
'clamp(15px, 0.
|
|
77
|
+
'clamp(15px, 0.938rem + ((1vw - 7.68px) * 5.409), 60px)'
|
|
78
78
|
);
|
|
79
79
|
} );
|
|
80
80
|
|
|
@@ -282,7 +282,7 @@ function Iframe(
|
|
|
282
282
|
|
|
283
283
|
head = (
|
|
284
284
|
<>
|
|
285
|
-
<style>{ 'body{margin:0}' }</style>
|
|
285
|
+
<style>{ 'html{height:auto!important;}body{margin:0}' }</style>
|
|
286
286
|
{ styles.map(
|
|
287
287
|
( { tagName, href, id, rel, media, textContent } ) => {
|
|
288
288
|
const TagName = tagName.toLowerCase();
|
|
@@ -40,7 +40,7 @@ export default function useInnerBlockTemplateSync(
|
|
|
40
40
|
templateLock,
|
|
41
41
|
templateInsertUpdatesSelection
|
|
42
42
|
) {
|
|
43
|
-
const { getSelectedBlocksInitialCaretPosition } =
|
|
43
|
+
const { getSelectedBlocksInitialCaretPosition, isBlockSelected } =
|
|
44
44
|
useSelect( blockEditorStore );
|
|
45
45
|
const { replaceInnerBlocks } = useDispatch( blockEditorStore );
|
|
46
46
|
const innerBlocks = useSelect(
|
|
@@ -86,7 +86,8 @@ export default function useInnerBlockTemplateSync(
|
|
|
86
86
|
nextBlocks,
|
|
87
87
|
currentInnerBlocks.length === 0 &&
|
|
88
88
|
templateInsertUpdatesSelection &&
|
|
89
|
-
nextBlocks.length !== 0
|
|
89
|
+
nextBlocks.length !== 0 &&
|
|
90
|
+
isBlockSelected( clientId ),
|
|
90
91
|
// This ensures the "initialPosition" doesn't change when applying the template
|
|
91
92
|
// If we're supposed to focus the block, we'll focus the first inner block
|
|
92
93
|
// otherwise, we won't apply any auto-focus.
|
|
@@ -13,6 +13,7 @@ import { check } from '@wordpress/icons';
|
|
|
13
13
|
|
|
14
14
|
export default function PreviewOptions( {
|
|
15
15
|
children,
|
|
16
|
+
viewLabel,
|
|
16
17
|
className,
|
|
17
18
|
isEnabled = true,
|
|
18
19
|
deviceType,
|
|
@@ -32,8 +33,7 @@ export default function PreviewOptions( {
|
|
|
32
33
|
variant: 'tertiary',
|
|
33
34
|
className: 'block-editor-post-preview__button-toggle',
|
|
34
35
|
disabled: ! isEnabled,
|
|
35
|
-
|
|
36
|
-
children: __( 'View' ),
|
|
36
|
+
children: viewLabel,
|
|
37
37
|
};
|
|
38
38
|
const menuProps = {
|
|
39
39
|
'aria-label': __( 'View options' ),
|
|
@@ -221,6 +221,7 @@ export default function SpacingInputControl( {
|
|
|
221
221
|
hideLabelFromVision={ true }
|
|
222
222
|
className="components-spacing-sizes-control__custom-value-input"
|
|
223
223
|
style={ { gridColumn: '1' } }
|
|
224
|
+
size={ '__unstable-large' }
|
|
224
225
|
/>
|
|
225
226
|
|
|
226
227
|
<RangeControl
|
|
@@ -279,6 +280,7 @@ export default function SpacingInputControl( {
|
|
|
279
280
|
label={ ariaLabel }
|
|
280
281
|
hideLabelFromVision={ true }
|
|
281
282
|
__nextUnconstrainedWidth={ true }
|
|
283
|
+
size={ '__unstable-large' }
|
|
282
284
|
/>
|
|
283
285
|
) }
|
|
284
286
|
</>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
display: grid;
|
|
3
3
|
grid-template-columns: auto 1fr auto;
|
|
4
4
|
align-items: center;
|
|
5
|
-
grid-template-rows:
|
|
5
|
+
grid-template-rows: 16px auto;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.component-spacing-sizes-control {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
grid-column: 1 / 1;
|
|
28
28
|
justify-content: left;
|
|
29
29
|
height: $grid-unit-20;
|
|
30
|
-
margin-top: $grid-unit-
|
|
30
|
+
margin-top: $grid-unit-20;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.components-spacing-sizes-control__side-label {
|
|
@@ -37,8 +37,9 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
&.is-unlinked {
|
|
40
|
-
.components-range-control.components-spacing-sizes-control__range-control
|
|
41
|
-
|
|
40
|
+
.components-range-control.components-spacing-sizes-control__range-control,
|
|
41
|
+
.components-spacing-sizes-control__custom-value-input {
|
|
42
|
+
margin-top: $grid-unit-10;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -60,12 +61,7 @@
|
|
|
60
61
|
grid-column: 2 / 2;
|
|
61
62
|
grid-row: 1 / 1;
|
|
62
63
|
justify-self: end;
|
|
63
|
-
|
|
64
|
-
&.is-small.has-icon {
|
|
65
|
-
padding: 0;
|
|
66
|
-
min-width: $icon-size;
|
|
67
|
-
height: $grid-unit-20;
|
|
68
|
-
}
|
|
64
|
+
margin-top: -4px;
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
.component-spacing-sizes-control__linked-button ~ .components-spacing-sizes-control__custom-toggle-all {
|
|
@@ -75,33 +71,43 @@
|
|
|
75
71
|
.components-spacing-sizes-control__custom-toggle-single {
|
|
76
72
|
grid-column: 3 / 3;
|
|
77
73
|
justify-self: end;
|
|
78
|
-
|
|
79
|
-
padding: 0;
|
|
80
|
-
min-width: $icon-size;
|
|
81
|
-
height: $grid-unit-20;
|
|
82
|
-
margin-top: $grid-unit-15;
|
|
83
|
-
}
|
|
74
|
+
margin-top: $grid-unit-15;
|
|
84
75
|
}
|
|
85
76
|
|
|
86
77
|
.component-spacing-sizes-control__linked-button {
|
|
87
78
|
grid-column: 3 / 3;
|
|
88
79
|
grid-row: 1 / 1;
|
|
89
80
|
justify-self: end;
|
|
81
|
+
line-height: 0;
|
|
82
|
+
margin-top: -4px;
|
|
90
83
|
}
|
|
91
84
|
|
|
92
85
|
.components-spacing-sizes-control__custom-value-range {
|
|
93
86
|
grid-column: span 2;
|
|
94
|
-
margin-left: $grid-unit-
|
|
95
|
-
|
|
87
|
+
margin-left: $grid-unit-20;
|
|
88
|
+
margin-top: 8px;
|
|
96
89
|
}
|
|
97
90
|
|
|
98
91
|
.components-spacing-sizes-control__custom-value-input {
|
|
99
92
|
width: 124px;
|
|
93
|
+
margin-top: 8px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.components-range-control {
|
|
97
|
+
height: 40px;
|
|
98
|
+
/* Vertically center the RangeControl until it has true 40px height. */
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
|
|
102
|
+
> .components-base-control__field {
|
|
103
|
+
/* Fixes RangeControl contents when the outer wrapper is flex */
|
|
104
|
+
flex: 1;
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
.components-spacing-sizes-control__range-control {
|
|
103
109
|
grid-column: span 3;
|
|
104
|
-
|
|
110
|
+
margin-top: 8px;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
.components-range-control__mark {
|
|
@@ -125,5 +131,6 @@
|
|
|
125
131
|
|
|
126
132
|
.components-spacing-sizes-control__custom-select-control {
|
|
127
133
|
grid-column: span 3;
|
|
134
|
+
margin-top: $grid-unit-10;
|
|
128
135
|
}
|
|
129
136
|
}
|