From 9b0fadf2e74e828aff387b47b4deae7b954960ca Mon Sep 17 00:00:00 2001 From: Johan Lahti Date: Fri, 17 Nov 2023 17:17:27 +0100 Subject: [PATCH] feat: add font styles to properties --- .../FoldedListbox/FoldedListbox.tsx | 4 +- .../presentation/styling-panel-def.js | 297 ++++++++++-------- .../src/hooks/types/components.d.ts | 31 +- .../sn-filter-pane/src/hooks/use-styling.ts | 9 +- 4 files changed, 185 insertions(+), 156 deletions(-) diff --git a/packages/sn-filter-pane/src/components/FoldedListbox/FoldedListbox.tsx b/packages/sn-filter-pane/src/components/FoldedListbox/FoldedListbox.tsx index 2aea4b55..15473df7 100644 --- a/packages/sn-filter-pane/src/components/FoldedListbox/FoldedListbox.tsx +++ b/packages/sn-filter-pane/src/components/FoldedListbox/FoldedListbox.tsx @@ -81,10 +81,8 @@ const StyledGrid = styled(Grid, { shouldForwardProp: (p) => !['constraints', 'st const Title = styled(Typography, { shouldForwardProp: (p) => !['styles'].includes(p as string) })<{ styles?: IStyles }>( ({ styles }) => ({ - color: styles?.header?.color, + ...(styles?.header || {}), fontSize: '13px', // hard-coded since space is limited in collapsed mode - fontFamily: styles?.header?.fontFamily, - fontWeight: styles?.header?.fontWeight, }), ); diff --git a/packages/sn-filter-pane/src/ext/property-panel/settings/presentation/styling-panel-def.js b/packages/sn-filter-pane/src/ext/property-panel/settings/presentation/styling-panel-def.js index f1a22a34..6bf996ab 100644 --- a/packages/sn-filter-pane/src/ext/property-panel/settings/presentation/styling-panel-def.js +++ b/packages/sn-filter-pane/src/ext/property-panel/settings/presentation/styling-panel-def.js @@ -7,6 +7,8 @@ export default function getStyling(env) { const { translator, flags, anything } = env || {}; const { theme } = anything?.sense || {}; + const stylingPart2 = flags?.isEnabled('IM_5452_FILTERPANE_STYLING'); + const fontResolver = createFontResolver({ theme, translator, @@ -48,15 +50,24 @@ export default function getStyling(env) { ref: 'components', key: 'theme', items: { - fontFamilyItem: { - component: 'dropdown', - ref: 'header.fontFamily', - options: () => fontResolver.getOptions('header.fontFamily'), - defaultValue: () => fontResolver.getDefaultValue('header.fontFamily'), - }, + ...(stylingPart2 ? { + fontFamilyItem: { + component: 'dropdown', + ref: 'header.fontFamily', + options: () => fontResolver.getOptions('header.fontFamily'), + defaultValue: () => fontResolver.getDefaultValue('header.fontFamily'), + }, + } : {}), fontWrapperItem: { component: 'inline-wrapper', items: { + fontStyle: { + type: 'array', + component: 'font-style-buttons', + width: false, + ref: 'header.fontStyle', + defaultValue: ['bold'], + }, fontSizeItem: { component: 'dropdown', ref: 'header.fontSize', @@ -88,20 +99,29 @@ export default function getStyling(env) { ref: 'components', key: 'theme', items: { - contentFontWrapper: { + ...(stylingPart2 ? { + fontFamilyItem: { + component: 'dropdown', + ref: 'content.fontFamily', + options: () => fontResolver.getOptions('content.fontFamily'), + defaultValue: () => fontResolver.getDefaultValue('content.fontFamily'), + }, + } : {}), + fontWrapperItem: { component: 'inline-wrapper', items: { - fontFamilyItem: { - component: 'dropdown', - ref: 'content.fontFamily', - options: () => fontResolver.getOptions('content.fontFamily'), - defaultValue: () => fontResolver.getDefaultValue('content.fontFamily'), + fontStyle: { + type: 'array', + component: 'font-style-buttons', + width: false, + ref: 'content.fontStyle', + defaultValue: ['normal'], }, contentFontSizeItem: { component: 'dropdown', ref: 'content.fontSize', options: getFontSizes({ - min: 5, max: 24, theme, defaultFontSize, + min: 5, max: 18, theme, defaultFontSize, }), defaultValue: defaultContentFontSize, }, @@ -179,143 +199,146 @@ export default function getStyling(env) { }, }, }, - backgroundOptions: { - component: 'panel-section', - translation: 'properties.background.options', - items: { - background: { - items: { - backgroundColor: { - ref: 'components', - key: 'theme', - type: 'items', - items: { - useColorExpression: { - type: 'boolean', - component: 'dropdown', - ref: 'background.useExpression', - translation: 'properties.color', - defaultValue: false, - options: [ - { - value: false, - translation: 'properties.colorMode.primary', - }, - { - value: true, - translation: 'properties.colorMode.byExpression', - }, - ], - }, - colorExpression: { - type: 'string', - component: 'input-field-expression', - ref: 'background.colorExpression', - translation: 'Common.Expression', - expression: 'optional', - show: (data) => data?.background?.useExpression, - }, - colorPicker: { - type: 'object', - component: 'color-picker', - ref: 'background.color', - translation: 'properties.color.used', - disableNone: false, - defaultValue: styleDefaults.COLOR, - dualOutput: true, - show: (data) => !data?.background?.useExpression, - }, - }, - }, - backgroundImage: { - type: 'items', - ref: 'components', - key: 'theme', - items: { - backgroundImageMode: { - component: 'dropdown', - ref: 'background.image.mode', - translation: 'properties.backgroundImage', - defaultValue: styleDefaults.BGIMAGE_MODE, - options: [ - { - value: 'none', - translation: 'Background.None', - }, - { - value: 'media', - translation: 'MediaLibrary.Header', - }, - ], - change(data = {}) { - data.background = data.background || {}; - data.background.image = data.background.image || {}; - data.background.image.qStaticContentUrlDef = data.background.image.qStaticContentUrlDef || {}; - data.background.image.url = data.background.image.url || {}; + + ...(stylingPart2 ? { + backgroundOptions: { + component: 'panel-section', + translation: 'properties.background.options', + items: { + background: { + items: { + backgroundColor: { + ref: 'components', + key: 'theme', + type: 'items', + items: { + useColorExpression: { + type: 'boolean', + component: 'dropdown', + ref: 'background.useExpression', + translation: 'properties.color', + defaultValue: false, + options: [ + { + value: false, + translation: 'properties.colorMode.primary', + }, + { + value: true, + translation: 'properties.colorMode.byExpression', + }, + ], }, - }, - MediaLibrary: { - component: 'media-library-button', - ref: 'background.image.url', - translation: 'MediaLibrary.Header', - show(data) { - return data?.background?.image?.mode === 'media'; + colorExpression: { + type: 'string', + component: 'input-field-expression', + ref: 'background.colorExpression', + translation: 'Common.Expression', + expression: 'optional', + show: (data) => data?.background?.useExpression, + }, + colorPicker: { + type: 'object', + component: 'color-picker', + ref: 'background.color', + translation: 'properties.color.used', + disableNone: false, + defaultValue: styleDefaults.COLOR, + dualOutput: true, + show: (data) => !data?.background?.useExpression, }, }, - imageSize: { - component: 'dropdown', - ref: 'background.image.size', - defaultValue: styleDefaults.BACKGROUND_SIZE, - options: [ - { - value: 'auto', - translation: 'properties.backgroundImage.originalSize', - }, - { - value: 'alwaysFit', - translation: 'properties.backgroundImage.sizeAlwaysFit', - }, - { - value: 'fitWidth', - translation: 'properties.backgroundImage.sizeFitWidth', - }, - { - value: 'fitHeight', - translation: 'properties.backgroundImage.sizeFitHeight', + }, + backgroundImage: { + type: 'items', + ref: 'components', + key: 'theme', + items: { + backgroundImageMode: { + component: 'dropdown', + ref: 'background.image.mode', + translation: 'properties.backgroundImage', + defaultValue: styleDefaults.BGIMAGE_MODE, + options: [ + { + value: 'none', + translation: 'Background.None', + }, + { + value: 'media', + translation: 'MediaLibrary.Header', + }, + ], + change(data = {}) { + data.background = data.background || {}; + data.background.image = data.background.image || {}; + data.background.image.qStaticContentUrlDef = data.background.image.qStaticContentUrlDef || {}; + data.background.image.url = data.background.image.url || {}; }, - { - value: 'stretchFit', - translation: 'properties.backgroundImage.sizeStretch', + }, + MediaLibrary: { + component: 'media-library-button', + ref: 'background.image.url', + translation: 'MediaLibrary.Header', + show(data) { + return data?.background?.image?.mode === 'media'; }, - { - value: 'alwaysFill', - translation: 'properties.backgroundImage.sizeAlwaysFill', + }, + imageSize: { + component: 'dropdown', + ref: 'background.image.size', + defaultValue: styleDefaults.BACKGROUND_SIZE, + options: [ + { + value: 'auto', + translation: 'properties.backgroundImage.originalSize', + }, + { + value: 'alwaysFit', + translation: 'properties.backgroundImage.sizeAlwaysFit', + }, + { + value: 'fitWidth', + translation: 'properties.backgroundImage.sizeFitWidth', + }, + { + value: 'fitHeight', + translation: 'properties.backgroundImage.sizeFitHeight', + }, + { + value: 'stretchFit', + translation: 'properties.backgroundImage.sizeStretch', + }, + { + value: 'alwaysFill', + translation: 'properties.backgroundImage.sizeAlwaysFill', + }, + ], + change: (data) => { + if (data?.background?.image?.position) { + data.background.image.position = styleDefaults.BGIMAGE_POSITION; + } }, - ], - change: (data) => { - if (data?.background?.image?.position) { - data.background.image.position = styleDefaults.BGIMAGE_POSITION; - } + show: (data) => data?.background?.image?.mode === 'media' + && !!data?.background?.image?.url?.qStaticContentUrlDef?.qUrl, + }, + position: { + component: 'position-grid', + ref: 'background.image.position', + translation: 'properties.backgroundImage.position', + defaultValue: styleDefaults.BGIMAGE_POSITION, + currentSizeRef: 'background.size', + show: (data) => data?.background?.image?.mode === 'media' + && data?.background?.image?.url?.qStaticContentUrlDef?.qUrl + && data?.background?.image?.size !== 'stretchFit', }, - show: (data) => data?.background?.image?.mode === 'media' - && !!data?.background?.image?.url?.qStaticContentUrlDef?.qUrl, - }, - position: { - component: 'position-grid', - ref: 'background.image.position', - translation: 'properties.backgroundImage.position', - defaultValue: styleDefaults.BGIMAGE_POSITION, - currentSizeRef: 'background.size', - show: (data) => data?.background?.image?.mode === 'media' - && data?.background?.image?.url?.qStaticContentUrlDef?.qUrl - && data?.background?.image?.size !== 'stretchFit', }, }, }, }, }, }, - }, + } : {}), }, }, }; diff --git a/packages/sn-filter-pane/src/hooks/types/components.d.ts b/packages/sn-filter-pane/src/hooks/types/components.d.ts index aee49be4..f10065df 100644 --- a/packages/sn-filter-pane/src/hooks/types/components.d.ts +++ b/packages/sn-filter-pane/src/hooks/types/components.d.ts @@ -2,23 +2,28 @@ import { Theme as StardustTheme } from '@nebula.js/stardust'; export type ISizing = 'originalSize' | 'alwaysFit' | 'fitWidth' | 'fitHeight' | 'stretchFit' | 'alwaysFill'; +type IFontStyle = { + bold?: boolean; + normal?: boolean; + underline?: boolean; + italic?: boolean; +}; + +type IFontStyles = { + fontColor?: { + color?: string; + }; + fontSize?: string; + fontFamily?: string; + fontStyle?: IFontStyle; +} + export type IThemeComponent = { key: 'theme'; - content?: { - fontSize?: string; - fontColor?: { - color?: string; - }; + content?: IFontStyles & { useContrastColor?: boolean; }; - header?: { - fontColor?: { - color?: string; - }; - fontSize?: string; - fontFamily?: string; - fontWeight?: string; - }; + header?: IFontStyles; background?: { useColorExpression?: string; colorExpression?: string; diff --git a/packages/sn-filter-pane/src/hooks/use-styling.ts b/packages/sn-filter-pane/src/hooks/use-styling.ts index b097b3b9..024df96b 100644 --- a/packages/sn-filter-pane/src/hooks/use-styling.ts +++ b/packages/sn-filter-pane/src/hooks/use-styling.ts @@ -50,6 +50,8 @@ export default function useStyling({ app, components = [] }: ICreateStylingArgs) const imgDef = componentsOverrides.theme?.background?.image; const bgImage = resolveBgImage({ bgImage: imgDef }, app); + const headerFontStyle = componentsOverrides.theme?.header?.fontStyle || {}; + const mergedStyle = { listbox: { background: { @@ -71,10 +73,11 @@ export default function useStyling({ app, components = [] }: ICreateStylingArgs) // Listbox style included here because it is applied on collapsed headers, which resides in Filter pane code. color: componentsOverrides.theme?.header?.fontColor?.color || getListboxStyle('title.main', 'color'), fontSize: componentsOverrides.theme?.header?.fontSize || getListboxStyle('title.main', 'fontSize') || '13px', - fontFamily: getListboxStyle('title.main', 'fontFamily') || '"Source Sans Pro", sans-serif', - fontWeight: getListboxStyle('title.main', 'fontWeight') || '700', + fontFamily: componentsOverrides.theme?.header?.fontFamily || getListboxStyle('title.main', 'fontFamily') || '"Source Sans Pro", sans-serif', + fontWeight: (headerFontStyle.bold && 'bold') || (headerFontStyle.normal && 'normal') || getListboxStyle('title.main', 'fontWeight') || 'bold', + textDecoration: headerFontStyle.underline ? 'underline' : 'initial', + fontStyle: (headerFontStyle.italic && 'italic') || (headerFontStyle.normal && 'normal') || getListboxStyle('title.main', 'fontStyle') || 'initial', }, - // The theme is only exposed here temporarilly. The idea is to not expose any theme directly, // but instead let them populate the merged style. Remove when possible. stardustTheme,