Skip to content

Commit 4173e70

Browse files
Arukuenbfintal
andauthored
fix (type scale): added responsive typescale (#3513)
* minor bug fix * feat: give typescale responsive capabilities * docs: add jsdoc to new utils * Update src/plugins/global-settings/typography/utils.js * fix: add breaks to loop to get out early * fix: convert arrow to anonymous functino to support php 7.3 and below * fix: prevent forearch error --------- Co-authored-by: Benjamin Intal <[email protected]>
1 parent 518f42e commit 4173e70

File tree

7 files changed

+157
-56
lines changed

7 files changed

+157
-56
lines changed

src/components/advanced-range-control/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,11 @@ const AdvancedRangeControl = props => {
271271
// Since the actual previous value is a preset, force the new custom value
272272
// when changing unit
273273
controlProps.onChangeUnit = ( unit, unitAttrName ) => {
274+
dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent()
274275
setAttributes( { [ unitAttrName ]: unit } )
276+
if ( props.onChangeUnit ) {
277+
props.onChangeUnit( unit )
278+
}
275279
_onChange( _newValue )
276280
}
277281
}

src/components/font-size-control/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const FontSizeControl = props => {
3434
placeholder={ passedPlaceholder }
3535
onChangeUnit={ value => {
3636
// Change font-size so as not to surprise the user.
37-
if ( props.value !== '' && ! isNaN( Number( value ) ) ) {
37+
if ( props.value !== '' && ! isNaN( Number( props.value ) ) ) {
3838
if ( value === 'em' || value === 'rem' ) {
3939
props.onChange( String( pxToEm( props.value ) ) )
4040
} else if ( value === 'px' ) {

src/components/four-range-control/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,8 +440,12 @@ const FourRangeControl = memo( props => {
440440
// Since the actual previous value is a preset, force the new custom value
441441
// when changing unit
442442
controlProps.onChangeUnit = ( unit, unitAttrName ) => {
443-
initialOnChange( _newValue )
443+
dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent()
444444
setAttributes( { [ unitAttrName ]: unit } )
445+
if ( props.onChangeUnit ) {
446+
props.onChangeUnit( unit )
447+
}
448+
initialOnChange( _newValue )
445449
}
446450
}
447451

src/plugins/global-settings/preset-controls/index.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ public function generate_css_variables_styles( $property, $presets, $prefix, $is
133133

134134
$presets_by_slug = [];
135135
// Convert presets into an associative array with key 'slug'
136-
foreach ( $presets as $preset ) {
137-
$presets_by_slug[ $preset[ 'slug' ] ] = $preset;
136+
if ( is_array( $presets ) ) {
137+
foreach ( $presets as $preset ) {
138+
$presets_by_slug[ $preset[ 'slug' ] ] = $preset;
139+
}
138140
}
139141

140142
// There is no need to generate custom presets in the editor.
@@ -176,9 +178,10 @@ public function generate_css_variables_styles( $property, $presets, $prefix, $is
176178
* @return mixed
177179
*/
178180
public function deepGet( $array, $keys ) {
179-
return array_reduce( $keys, fn( $value, $key ) => $value[ $key ] ?? null, $array );
181+
return array_reduce( $keys, function( $value, $key ) {
182+
return $value[ $key ] ?? null;
183+
}, $array );
180184
}
181-
182185
/**
183186
* Add our global preset control styles.
184187
*

src/plugins/global-settings/typography/index.js

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,26 @@ import { GlobalTypographyStyles } from './editor-loader'
55
import TypographyPicker from './typography-picker'
66
import { getThemeStyles } from './get-theme-styles'
77
import FREE_FONT_PAIRS from './font-pairs.json'
8-
import { getAppliedTypeScale, cleanTypographyStyle } from './utils'
8+
import {
9+
getDevicePropertyKey,
10+
getAppliedTypeScale,
11+
cleanTypographyStyle,
12+
getTypographyTypeScale,
13+
} from './utils'
914

1015
/**
1116
* External dependencies
1217
*/
1318
import {
14-
PanelAdvancedSettings, AdvancedSelectControl, ControlSeparator, FontPairPicker, ProControlButton, AdvancedToggleControl,
19+
PanelAdvancedSettings,
20+
AdvancedSelectControl,
21+
ControlSeparator,
22+
FontPairPicker,
23+
ProControlButton,
24+
AdvancedToggleControl,
1525
} from '~stackable/components'
1626
import { fetchSettings } from '~stackable/util'
27+
import { useDeviceType } from '~stackable/hooks'
1728
import {
1829
i18n, isPro, showProNotice,
1930
} from 'stackable'
@@ -148,6 +159,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
148159
const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false
149160
return { useTypographyAsPresets: _useTypographyAsPresets }
150161
}, [] )
162+
const deviceType = useDeviceType()?.toLowerCase() || 'desktop'
151163

152164
const FONT_PAIRS = applyFilters( 'stackable.global-settings.typography.font-pairs.premium-font-pairs', FREE_FONT_PAIRS )
153165

@@ -157,7 +169,11 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
157169
const [ customFontPairs, setCustomFontPairs ] = useState( [] )
158170
const [ selectedFontPairName, setSelectedFontPairName ] = useState( '' )
159171
const [ isEditingFontPair, setIsEditingFontPair ] = useState( false )
160-
const [ selectedTypeScale, setSelectedTypeScale ] = useState( 'none' )
172+
const [ selectedTypeScale, setSelectedTypeScale ] = useState( {
173+
desktop: 'none',
174+
tablet: 'none',
175+
mobile: 'none',
176+
} )
161177
const [ isApplyBodyToHTML, setIsApplyBodyToHTML ] = useState( false )
162178

163179
const fontPairContainerRef = useRef( null )
@@ -172,22 +188,21 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
172188
setSelectedFontPairName( response.stackable_selected_font_pair || 'theme-heading-default/theme-body-default' )
173189
setIsApplyBodyToHTML( response.stackable_is_apply_body_to_html || false )
174190

175-
// Reversely compute the type scale from the font sizes
176-
let typeScale = _typographySettings?.h6?.fontSize
177-
if ( typeScale ) {
178-
const computedApplied = getAppliedTypeScale( typeScale ) ?? {}
179-
const tags = Object.keys( _typographySettings )
180-
for ( const tag of tags ) {
181-
// If font size mismatch, set typography scale to Custom
182-
if ( _typographySettings[ tag ]?.fontSize !== computedApplied[ tag ]?.fontSize ) {
183-
typeScale = 'custom'
184-
}
185-
}
186-
setSelectedTypeScale( typeScale )
187-
}
191+
// Reversely compute initial typescale for highlighting
192+
const typeScaleDesktop = getTypographyTypeScale( _typographySettings, 'desktop' )
193+
const typeScaleTablet = getTypographyTypeScale( _typographySettings, 'tablet' )
194+
const typeScaleMobile = getTypographyTypeScale( _typographySettings, 'mobile' )
195+
setSelectedTypeScale( {
196+
desktop: typeScaleDesktop, tablet: typeScaleTablet, mobile: typeScaleMobile,
197+
} )
188198
} )
189199
}, [] )
190200

201+
useEffect( () => {
202+
const typeScale = getTypographyTypeScale( typographySettings, deviceType )
203+
setSelectedTypeScale( prev => ( { ...prev, [ deviceType ]: typeScale } ) )
204+
}, [ deviceType, typographySettings ] )
205+
191206
useEffect( () => {
192207
// When typography styles are changed, trigger our editor style generator to update.
193208
// This also triggers updating presets with typography, and applying body font size to html.
@@ -269,26 +284,25 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
269284
}
270285

271286
const updateTypeScale = value => {
272-
setSelectedTypeScale( value )
273-
274-
// If value is custom, do not do anything
275-
if ( value === 'custom ' ) {
276-
return
277-
}
278-
279-
// If value is none, reset the font sizes and units
280287
if ( value === 'none' ) {
281288
const selectors = TYPOGRAPHY_TAGS.map( tag => tag.selector )
282-
const newSettings = selectors.reduce( ( acc, selector, ) => {
283-
acc[ selector ] = { fontSize: '', fontSizeUnit: '' }
289+
const fontSizeKey = getDevicePropertyKey( 'fontSize', deviceType )
290+
const fontSizeUnitKey = getDevicePropertyKey( 'fontSizeUnit', deviceType )
291+
292+
const newSettings = selectors.reduce( ( acc, selector ) => {
293+
acc[ selector ] = {
294+
[ fontSizeKey ]: '',
295+
[ fontSizeUnitKey ]: '',
296+
}
284297
return acc
285298
}, {} )
299+
286300
changeStyles( newSettings )
287301
return
288302
}
289303

290304
// If value is valid type scale, apply to the styles
291-
const newSettings = getAppliedTypeScale( value )
305+
const newSettings = getAppliedTypeScale( value, deviceType )
292306
changeStyles( newSettings )
293307
}
294308

@@ -513,9 +527,12 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
513527
<AdvancedSelectControl
514528
label={ __( 'Type Scale', i18n ) }
515529
options={ TYPE_SCALE }
516-
value={ selectedTypeScale }
530+
value={ selectedTypeScale[ deviceType ] }
517531
onChange={ updateTypeScale }
532+
responsive="all"
518533
default="none"
534+
hasTabletValue={ selectedTypeScale.tablet !== 'none' }
535+
hasMobileValue={ selectedTypeScale.mobile !== 'none' }
519536
/>
520537
{ TYPOGRAPHY_TAGS.map( ( {
521538
label, selector, help,
@@ -531,18 +548,9 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',
531548
isAllowReset={ getIsAllowReset( selector ) }
532549
onChange={ styles => {
533550
changeStyles( { [ selector ]: styles } )
534-
// Set typeScale to custom when editing font size or units
535-
if ( 'fontSize' in styles || 'fontSizeUnit' in styles ) {
536-
setSelectedTypeScale( 'custom' )
537-
}
538551
} }
539552
onReset={ () => {
540553
resetStyles( selector )
541-
// Set typeScale to custom when editing font size or units
542-
const styles = typographySettings[ selector ]
543-
if ( 'fontSize' in styles || 'fontSizeUnit' in styles ) {
544-
setSelectedTypeScale( 'custom' )
545-
}
546554
} }
547555
/>
548556
)

src/plugins/global-settings/typography/utils.js

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,59 @@
1+
/**
2+
* Returns the reversely computed typescale based on the typography settings and device type
3+
*
4+
* @param {Object} typographySettings - The typography settings object
5+
* @param {string} deviceType - The device type
6+
* @return {string} The computed typescale. Values can be none, custom, or the numeric scale.
7+
*/
8+
export const getTypographyTypeScale = ( typographySettings, deviceType ) => {
9+
const fontSizeKey = getDevicePropertyKey( 'fontSize', deviceType )
10+
const fontSizeUnitKey = getDevicePropertyKey( 'fontSizeUnit', deviceType )
11+
const tags = Object.keys( typographySettings )
12+
13+
// Reversely compute the type scale from the font sizes
14+
let typeScale = typographySettings?.h6?.[ fontSizeKey ]
15+
typeScale = typeScale && parseFloat( typeScale ).toString()
16+
17+
if ( typeScale ) {
18+
const computedApplied = getAppliedTypeScale( typeScale, deviceType ) ?? {}
19+
for ( const tag of tags ) {
20+
// console.log( typographySettings[ tag ]?.[ fontSizeKey ], computedApplied[ tag ]?.[ fontSizeKey ] )
21+
// If font size mismatch, set typography scale to Custom
22+
if ( typographySettings[ tag ]?.[ fontSizeKey ] !== computedApplied[ tag ]?.[ fontSizeKey ] ||
23+
typographySettings[ tag ]?.[ fontSizeUnitKey ] !== computedApplied[ tag ]?.[ fontSizeUnitKey ]
24+
) {
25+
typeScale = 'custom'
26+
break
27+
}
28+
}
29+
} else {
30+
typeScale = 'none'
31+
for ( const tag of tags ) {
32+
if ( typographySettings[ tag ]?.[ fontSizeKey ] ) {
33+
typeScale = 'custom'
34+
break
35+
}
36+
}
37+
}
38+
return typeScale
39+
}
40+
41+
/**
42+
* Returns the property key adjusted for the given device type.
43+
*
44+
* @param {string} baseProperty - The base property name
45+
* @param {string} deviceType - The device type
46+
* @return {string} The adjusted property key based on the device type.
47+
*
48+
*/
49+
export const getDevicePropertyKey = ( baseProperty, deviceType ) => {
50+
deviceType = deviceType.toLowerCase()
51+
if ( deviceType && deviceType !== 'desktop' ) {
52+
return `${ deviceType }${ baseProperty.charAt( 0 ).toUpperCase() }${ baseProperty.slice( 1 ) }`
53+
}
54+
return baseProperty
55+
}
56+
157
/**
258
* Generates a typographic scale based on the given value.
359
*
@@ -6,26 +62,52 @@
662
* calculated using an exponential scale.
763
*
864
* @param {string|number} value - The base number to use for the typographic scale.
65+
* @param {string} deviceType - The device type
966
* @return {Object|undefined} An object mapping CSS selectors to their corresponding
1067
* font size settings. Returns `undefined` if input is invalid.
1168
*/
1269

13-
export const getAppliedTypeScale = value => {
70+
export const getAppliedTypeScale = ( value, deviceType = '' ) => {
1471
const typeScale = Number( value )
1572
if ( Number.isNaN( typeScale ) ) {
1673
return
1774
}
18-
return {
19-
h1: { fontSize: String( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
20-
h2: { fontSize: String( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
21-
h3: { fontSize: String( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
22-
h4: { fontSize: String( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
23-
h5: { fontSize: String( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
24-
h6: { fontSize: String( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' },
25-
p: { fontSize: '1', fontSizeUnit: 'rem' },
26-
'.stk-subtitle': { fontSize: String( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' },
27-
'.stk-button__inner-text': { fontSize: '1', fontSizeUnit: 'rem' },
75+
76+
// These values are the exponent for the typeScale value
77+
const headings = {
78+
h1: 6,
79+
h2: 5,
80+
h3: 4,
81+
h4: 3,
82+
h5: 2,
83+
h6: 1,
84+
p: 0,
85+
'.stk-subtitle': -1,
86+
'.stk-button__inner-text': 0,
2887
}
88+
89+
const result = {}
90+
91+
Object.entries( headings ).forEach( ( [ key, power ] ) => {
92+
let fontSize
93+
if ( power > 0 ) {
94+
fontSize = String( Math.pow( typeScale, power ).toFixed( 3 ) )
95+
} else if ( power === 0 ) {
96+
fontSize = '1'
97+
} else {
98+
fontSize = String( ( 1 / typeScale ).toFixed( 3 ) )
99+
}
100+
101+
const fontSizeKey = getDevicePropertyKey( 'fontSize', deviceType )
102+
const fontSizeUnitKey = getDevicePropertyKey( 'fontSizeUnit', deviceType )
103+
104+
result[ key ] = {
105+
[ fontSizeKey ]: fontSize,
106+
[ fontSizeUnitKey ]: 'rem',
107+
}
108+
} )
109+
110+
return result
29111
}
30112

31113
/**

src/util/typography/styles.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export const createTypographyStyles = ( attrNameTemplate = '%s', screen = 'deskt
3939
const mobileFontSize = getValue( 'MobileFontSize' )
4040

4141
const desktopFontSizeUnit = isCSSVarValue( desktopFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px'
42-
const tabletFontSizeUnit = isCSSVarValue( tabletFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px'
43-
const mobileFontSizeUnit = isCSSVarValue( mobileFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px'
42+
const tabletFontSizeUnit = isCSSVarValue( tabletFontSize ) ? '' : getValue( 'TabletFontSizeUnit' ) || 'px'
43+
const mobileFontSizeUnit = isCSSVarValue( mobileFontSize ) ? '' : getValue( 'MobileFontSizeUnit' ) || 'px'
4444

4545
if ( screen !== 'tablet' && screen !== 'mobile' ) { // Desktop.
4646
styles = {

0 commit comments

Comments
 (0)