From 317253c7919bd0715eb328a23890d83d98406fad Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sat, 14 Dec 2024 11:57:41 +0100 Subject: [PATCH] WIP - First components --- .stylelintignore | 1 + .../configuration/settings-reference.md | 7 + packages/cmsui/components/Field/Field.tsx | 185 ++++++++++++++++++ packages/cmsui/config/widgets.ts | 9 + packages/cmsui/index.ts | 4 + packages/cmsui/package.json | 1 + packages/components/README.md | 12 +- .../components/src/stories/Introduction.mdx | 18 +- .../src/styles/basic/BlockToolbar.css | 2 +- .../src/styles/basic/Breadcrumbs.css | 2 +- .../components/src/styles/basic/Button.css | 2 +- .../components/src/styles/basic/Calendar.css | 2 +- .../components/src/styles/basic/Checkbox.css | 2 +- .../src/styles/basic/CheckboxGroup.css | 2 +- .../components/src/styles/basic/ColorArea.css | 2 +- .../src/styles/basic/ColorField.css | 2 +- .../src/styles/basic/ColorPicker.css | 2 +- .../src/styles/basic/ColorSlider.css | 2 +- .../src/styles/basic/ColorSwatch.css | 2 +- .../src/styles/basic/ColorSwatchPicker.css | 2 +- .../src/styles/basic/ColorWheel.css | 2 +- .../components/src/styles/basic/ComboBox.css | 2 +- .../components/src/styles/basic/Container.css | 2 +- .../components/src/styles/basic/DateField.css | 2 +- .../src/styles/basic/DatePicker.css | 2 +- .../src/styles/basic/DateRangePicker.css | 2 +- .../components/src/styles/basic/Dialog.css | 2 +- .../src/styles/basic/Disclosure.css | 2 +- packages/components/src/styles/basic/Form.css | 2 +- .../components/src/styles/basic/GridList.css | 2 +- .../components/src/styles/basic/Label.css | 2 +- packages/components/src/styles/basic/Link.css | 2 +- .../components/src/styles/basic/ListBox.css | 2 +- packages/components/src/styles/basic/Menu.css | 2 +- .../components/src/styles/basic/Meter.css | 2 +- .../components/src/styles/basic/Modal.css | 2 +- .../src/styles/basic/NumberField.css | 2 +- .../components/src/styles/basic/Popover.css | 2 +- .../src/styles/basic/ProgressBar.css | 2 +- .../src/styles/basic/RadioGroup.css | 2 +- .../src/styles/basic/RangeCalendar.css | 2 +- .../src/styles/basic/SearchField.css | 2 +- .../components/src/styles/basic/Select.css | 2 +- .../components/src/styles/basic/Slider.css | 2 +- .../components/src/styles/basic/Switch.css | 2 +- .../components/src/styles/basic/Table.css | 2 +- packages/components/src/styles/basic/Tabs.css | 2 +- .../components/src/styles/basic/TagGroup.css | 2 +- .../components/src/styles/basic/TextField.css | 2 +- .../components/src/styles/basic/TimeField.css | 2 +- .../src/styles/basic/ToggleButton.css | 2 +- .../components/src/styles/basic/Toolbar.css | 2 +- .../components/src/styles/basic/Tooltip.css | 2 +- .../components/src/styles/basic/icons.css | 2 +- packages/types/src/config/Settings.d.ts | 1 + packages/volto/package.json | 4 + .../volto/src/components/manage/Form/Form.jsx | 3 +- .../src/components/manage/Form/InlineForm.jsx | 2 +- packages/volto/src/config/index.js | 8 + packages/volto/src/helpers/Html/Html.jsx | 6 + packages/volto/src/loadSemanticUI.less | 4 + packages/volto/src/styles/main.css | 2 + packages/volto/src/styles/sidebar.css | 19 ++ packages/volto/src/styles/variables.css | 3 + packages/volto/src/theme.js | 15 +- pnpm-lock.yaml | 17 +- 66 files changed, 339 insertions(+), 74 deletions(-) create mode 100644 packages/cmsui/components/Field/Field.tsx create mode 100644 packages/cmsui/config/widgets.ts create mode 100644 packages/volto/src/loadSemanticUI.less create mode 100644 packages/volto/src/styles/main.css create mode 100644 packages/volto/src/styles/sidebar.css create mode 100644 packages/volto/src/styles/variables.css diff --git a/.stylelintignore b/.stylelintignore index 4b2be235a0..f52d70d0db 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,3 +2,4 @@ dist docs node_modules packages/registry/lib +build diff --git a/docs/source/configuration/settings-reference.md b/docs/source/configuration/settings-reference.md index 54eaebef20..ae5e32cc76 100644 --- a/docs/source/configuration/settings-reference.md +++ b/docs/source/configuration/settings-reference.md @@ -463,6 +463,13 @@ querystringSearchGet [See an explanation of character limits in URLs](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers/417184#417184). Please test this setting properly before enabling in a production site. +cssLayers + If you want to use CSS layers in Volto styling, it's possible to define them making sure that they are defined and applied at the very top level of the page (head tag). + By using this configuration, you can pass the layer list definition as an array: + + ```js + config.settings.cssLayers = ['reset', 'plone-components', 'layout', 'addons', 'theme']; + ``` ``` ## Views settings diff --git a/packages/cmsui/components/Field/Field.tsx b/packages/cmsui/components/Field/Field.tsx new file mode 100644 index 0000000000..9e91e94eb0 --- /dev/null +++ b/packages/cmsui/components/Field/Field.tsx @@ -0,0 +1,185 @@ +import config from '@plone/registry'; +import type { + WidgetsConfigById, + WidgetsConfigByFactory, + WidgetsConfigByType, + WidgetsConfigByVocabulary, + WidgetsConfigByWidget, +} from '@plone/types'; + +type FieldProps = { + id: keyof WidgetsConfigById; + widget: keyof WidgetsConfigByWidget; + vocabulary: { '@id': keyof WidgetsConfigByVocabulary }; + choices: string; + type: keyof WidgetsConfigByType; + focus: boolean; + mode: string; + widgetOptions: any; + factory: keyof WidgetsConfigByFactory; + onChange: (id: string, value: any) => void; + placeholder: string; + title: string; +}; + +const MODE_HIDDEN = 'hidden'; //hidden mode. If mode is hidden, field is not rendered + +/** + * Get default widget + */ +const getWidgetDefault = (): React.ComponentType => config.widgets.default; + +/** + * Get widget by field's `id` attribute + */ +const getWidgetByFieldId = ( + id: FieldProps['id'], +): React.ComponentType | null => config.widgets.id[id] || null; + +/** + * Get widget by factory attribute + */ +const getWidgetByFactory = ( + factory: FieldProps['factory'], +): React.ComponentType | null => config.widgets.factory?.[factory] || null; + +/** + * Get widget by field's `widget` attribute + */ +const getWidgetByName = ( + widget: FieldProps['widget'], +): React.ComponentType | null => + typeof widget === 'string' + ? config.widgets.widget[widget] || getWidgetDefault() + : null; + +/** + * Get widget by tagged values + * + +directives.widget( + 'fieldname', + frontendOptions={ + "widget": 'specialwidget', + "version": 'extra' + }) + + */ +const getWidgetFromTaggedValues = (widgetOptions: { + frontendOptions: { widget: FieldProps['widget']; widgetProps: any }; +}): React.ComponentType | null => + typeof widgetOptions?.frontendOptions?.widget === 'string' + ? config.widgets.widget[widgetOptions.frontendOptions.widget] + : null; + +/** + * Get widget props from tagged values + * + +directives.widget( + "fieldname", + frontendOptions={ + "widget": "specialwidget", + "widgetProps": {"prop1": "specialprop"} + }) + + */ +const getWidgetPropsFromTaggedValues = (widgetOptions: { + frontendOptions: { widget: string; widgetProps: any }; +}): React.ComponentType | null => + typeof widgetOptions?.frontendOptions?.widgetProps === 'object' + ? widgetOptions.frontendOptions.widgetProps + : null; + +/** + * Get widget by field's `vocabulary` attribute + */ +const getWidgetByVocabulary = ( + vocabulary: FieldProps['vocabulary'], +): React.ComponentType | null => + vocabulary && vocabulary['@id'] + ? config.widgets.vocabulary[ + vocabulary['@id'].replace( + /^.*\/@vocabularies\//, + '', + ) as keyof WidgetsConfigByVocabulary + ] + : null; + +/** + * Get widget by field's hints `vocabulary` attribute in widgetOptions + */ +const getWidgetByVocabularyFromHint = ( + props: FieldProps, +): React.ComponentType | null => + props.widgetOptions && props.widgetOptions.vocabulary + ? config.widgets.vocabulary[ + props.widgetOptions.vocabulary['@id'].replace( + /^.*\/@vocabularies\//, + '', + ) as keyof WidgetsConfigByVocabulary + ] + : null; + +/** + * Get widget by field's `choices` attribute + */ +const getWidgetByChoices = ( + props: FieldProps, +): React.ComponentType | null => { + if (props.choices) { + return config.widgets.choices; + } + + if (props.vocabulary) { + // If vocabulary exists, then it means it's a choice field in disguise with + // no widget specified that probably contains a string then we force it + // to be a select widget instead + return config.widgets.choices; + } + + return null; +}; + +/** + * Get widget by field's `type` attribute + */ +const getWidgetByType = (type: FieldProps['type']): React.ComponentType => + config.widgets.type[type] || null; + +const Field = (props: FieldProps) => { + const Widget = + getWidgetByFieldId(props.id) || + getWidgetFromTaggedValues(props.widgetOptions) || + getWidgetByName(props.widget) || + getWidgetByChoices(props) || + getWidgetByVocabulary(props.vocabulary) || + getWidgetByVocabularyFromHint(props) || + getWidgetByFactory(props.factory) || + getWidgetByType(props.type) || + getWidgetDefault(); + + if (props.mode === MODE_HIDDEN) { + return null; + } + + // Adding the widget props from tagged values (if any) + const widgetProps = { + ...props, + label: props.title, + placeholder: props.placeholder || 'Type something...', + // Temporary translator from the old widget signature (id, value) to the new one (value) + onChange: (arg1: string, arg2: string | undefined) => { + if (!arg2 === undefined) { + props.onChange(props.id, arg1); + } else { + props.onChange(props.id, arg2); + } + }, + ...getWidgetPropsFromTaggedValues(props.widgetOptions), + }; + + return ; +}; + +export default Field; diff --git a/packages/cmsui/config/widgets.ts b/packages/cmsui/config/widgets.ts new file mode 100644 index 0000000000..3ff5ba82fb --- /dev/null +++ b/packages/cmsui/config/widgets.ts @@ -0,0 +1,9 @@ +import type { ConfigType } from '@plone/registry'; +import { QuantaTextAreaField, QuantaTextField } from '@plone/components'; + +export default function install(config: ConfigType) { + config.widgets.default = QuantaTextField; + config.widgets.widget.textarea = QuantaTextAreaField; + + return config; +} diff --git a/packages/cmsui/index.ts b/packages/cmsui/index.ts index 7258290153..4804a02ba9 100644 --- a/packages/cmsui/index.ts +++ b/packages/cmsui/index.ts @@ -1,5 +1,9 @@ import type { ConfigType } from '@plone/registry'; +import installWidgets from './config/widgets'; +import '@plone/components/dist/basic.css'; +import '@plone/components/dist/quanta.css'; export default function install(config: ConfigType) { + installWidgets(config); return config; } diff --git a/packages/cmsui/package.json b/packages/cmsui/package.json index 28597732df..d397a3f337 100644 --- a/packages/cmsui/package.json +++ b/packages/cmsui/package.json @@ -28,6 +28,7 @@ "publishConfig": { "access": "public" }, + "type": "module", "main": "index.ts", "scripts": { "test": "vitest", diff --git a/packages/components/README.md b/packages/components/README.md index af2f8c0589..ba7a55d313 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -62,11 +62,19 @@ Using them as a baseline will allow you to quickly build your theme around them. `@plone/components` basic styles provide a simple, yet powerful, set of tokenized custom CSS properties that will help you customize your own styles on the top of the basic styling. You can override them in your classes while maintaining them for others. +### CSS layers + +This package use CSS layers to provide a way to style the components in a more flexible way. +It uses the `plone-components` layer name to scope all the CSS declarations in the package. +The basic styling uses the nested `plone-components.base` named layer. +You can use the `plone-components` layer to override the basic styling, or use the `plone-components.base` layer to override the basic styling in a more specific way. + ### Quanta This package also features the Quanta components. -The Quanta theme is an example of it. -These components use the basic styling as a baseline, not only in styling, but also in the component side, reusing the CSS and custom CSS properties in it. +These components use the basic styling as a baseline, extending them to achieve Quanta look and feel. +They also extend the basic React components in a composable way. +The Quanta styling is scoped in the `plone-components.quanta` named layer. Quanta is built upon the basic styles in an additive way. The use of the Quanta CSS implies using it upon basic styling. diff --git a/packages/components/src/stories/Introduction.mdx b/packages/components/src/stories/Introduction.mdx index 41ecc9f5d6..8ddefc56b6 100644 --- a/packages/components/src/stories/Introduction.mdx +++ b/packages/components/src/stories/Introduction.mdx @@ -5,16 +5,17 @@ import { Meta } from '@storybook/blocks'; # `@plone/components` -This package contains ReactJS components for using Plone as a headless CMS. +This package contains ReactJS components for Plone 7 and Plone's API-first CMS story. The purpose of this package is to provide an agnostic set of baseline components to build Plone sites upon. ## Usage -Using the package manager of your choice (npm, yarn, pnpm) install the package: +Using the package manager of your choice (npm, yarn, pnpm) to install the package in your app. +Use pnpm in case you are adding them a Volto add-on/workspace: ```shell -yarn add @plone/components +pnpm add @plone/components ``` Whenever you want to use the components, all are exported as named exports: @@ -53,12 +54,19 @@ or selectively: import '@plone/components/src/styles/basic/TextField.css'; ``` +### CSS layers + +This package use CSS layers to provide a way to style the components in a more flexible way. +It uses the `plone-components` layer name to scope all the CSS declarations in the package. +The basic styling uses the nested `plone-components.base` named layer. +You can use the `plone-components` layer to override the basic styling, or use the `plone-components.base` layer to override the basic styling in a more specific way. ## Quanta This package also features the Quanta components. -The Quanta theme is an example of it. -These components use the basic styling as a baseline, not only in styling, but also in the component side, reusing the CSS and custom CSS properties in it. +These components use the basic styling as a baseline, extending them to achieve Quanta look and feel. +They also extend the basic React components in a composable way. +The Quanta styling is scoped in the `plone-components.quanta` named layer. Quanta is built upon the basic styles in an additive way. The use of the Quanta CSS implies using it upon basic styling. diff --git a/packages/components/src/styles/basic/BlockToolbar.css b/packages/components/src/styles/basic/BlockToolbar.css index 19c101a290..1799ce9bb0 100644 --- a/packages/components/src/styles/basic/BlockToolbar.css +++ b/packages/components/src/styles/basic/BlockToolbar.css @@ -4,7 +4,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .blocks-toolbar { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/Breadcrumbs.css b/packages/components/src/styles/basic/Breadcrumbs.css index 0aaa86cac1..d6d5a8e83c 100644 --- a/packages/components/src/styles/basic/Breadcrumbs.css +++ b/packages/components/src/styles/basic/Breadcrumbs.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Breadcrumbs { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/Button.css b/packages/components/src/styles/basic/Button.css index c6849f7fb4..d4e79025e3 100644 --- a/packages/components/src/styles/basic/Button.css +++ b/packages/components/src/styles/basic/Button.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Button { padding: 8px 8px; border: 1px solid var(--border-color); diff --git a/packages/components/src/styles/basic/Calendar.css b/packages/components/src/styles/basic/Calendar.css index ec81b10c4d..0c16b5071c 100644 --- a/packages/components/src/styles/basic/Calendar.css +++ b/packages/components/src/styles/basic/Calendar.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Calendar { width: fit-content; max-width: 100%; diff --git a/packages/components/src/styles/basic/Checkbox.css b/packages/components/src/styles/basic/Checkbox.css index 46f856ac1d..7ca3ed1197 100644 --- a/packages/components/src/styles/basic/Checkbox.css +++ b/packages/components/src/styles/basic/Checkbox.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Checkbox { --selected-color: var(--highlight-background); --selected-color-pressed: var(--highlight-background-pressed); diff --git a/packages/components/src/styles/basic/CheckboxGroup.css b/packages/components/src/styles/basic/CheckboxGroup.css index 1939115950..ca543fcb41 100644 --- a/packages/components/src/styles/basic/CheckboxGroup.css +++ b/packages/components/src/styles/basic/CheckboxGroup.css @@ -3,7 +3,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-CheckboxGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/ColorArea.css b/packages/components/src/styles/basic/ColorArea.css index e5a81b7512..fa49256001 100644 --- a/packages/components/src/styles/basic/ColorArea.css +++ b/packages/components/src/styles/basic/ColorArea.css @@ -1,6 +1,6 @@ @import './ColorSlider.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorArea { width: 192px; height: 192px; diff --git a/packages/components/src/styles/basic/ColorField.css b/packages/components/src/styles/basic/ColorField.css index 41442cbbe8..d09a7709f5 100644 --- a/packages/components/src/styles/basic/ColorField.css +++ b/packages/components/src/styles/basic/ColorField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorField { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/ColorPicker.css b/packages/components/src/styles/basic/ColorPicker.css index 9bfd25e9cb..344565fdbf 100644 --- a/packages/components/src/styles/basic/ColorPicker.css +++ b/packages/components/src/styles/basic/ColorPicker.css @@ -10,7 +10,7 @@ @import './Select.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .color-picker { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/ColorSlider.css b/packages/components/src/styles/basic/ColorSlider.css index f61f4b0d5e..cd54265e3a 100644 --- a/packages/components/src/styles/basic/ColorSlider.css +++ b/packages/components/src/styles/basic/ColorSlider.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSlider { display: grid; max-width: 300px; diff --git a/packages/components/src/styles/basic/ColorSwatch.css b/packages/components/src/styles/basic/ColorSwatch.css index f617ec5341..f8127e8756 100644 --- a/packages/components/src/styles/basic/ColorSwatch.css +++ b/packages/components/src/styles/basic/ColorSwatch.css @@ -1,6 +1,6 @@ @import './ColorSlider.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSwatch { width: 32px; height: 32px; diff --git a/packages/components/src/styles/basic/ColorSwatchPicker.css b/packages/components/src/styles/basic/ColorSwatchPicker.css index 5a82e24a96..c91c37180a 100644 --- a/packages/components/src/styles/basic/ColorSwatchPicker.css +++ b/packages/components/src/styles/basic/ColorSwatchPicker.css @@ -2,7 +2,7 @@ @import './ColorField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ColorSwatchPicker { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/ColorWheel.css b/packages/components/src/styles/basic/ColorWheel.css index c7a953896d..fcff769737 100644 --- a/packages/components/src/styles/basic/ColorWheel.css +++ b/packages/components/src/styles/basic/ColorWheel.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .react-aria-ColorThumb { width: 20px; height: 20px; diff --git a/packages/components/src/styles/basic/ComboBox.css b/packages/components/src/styles/basic/ComboBox.css index fc597eb7ee..f3a2d64b7d 100644 --- a/packages/components/src/styles/basic/ComboBox.css +++ b/packages/components/src/styles/basic/ComboBox.css @@ -5,7 +5,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ComboBox { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Container.css b/packages/components/src/styles/basic/Container.css index f7448a20b2..da97f93bf3 100644 --- a/packages/components/src/styles/basic/Container.css +++ b/packages/components/src/styles/basic/Container.css @@ -1,4 +1,4 @@ -@layer plone-components { +@layer plone-components.base { .q.container { container-type: inline-size; diff --git a/packages/components/src/styles/basic/DateField.css b/packages/components/src/styles/basic/DateField.css index d0220e4999..7eca458b11 100644 --- a/packages/components/src/styles/basic/DateField.css +++ b/packages/components/src/styles/basic/DateField.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DateField { color: var(--text-color); } diff --git a/packages/components/src/styles/basic/DatePicker.css b/packages/components/src/styles/basic/DatePicker.css index dcfd458c83..796e0439e5 100644 --- a/packages/components/src/styles/basic/DatePicker.css +++ b/packages/components/src/styles/basic/DatePicker.css @@ -6,7 +6,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DatePicker { color: var(--text-color); diff --git a/packages/components/src/styles/basic/DateRangePicker.css b/packages/components/src/styles/basic/DateRangePicker.css index bb44b073c1..c48d9ac33f 100644 --- a/packages/components/src/styles/basic/DateRangePicker.css +++ b/packages/components/src/styles/basic/DateRangePicker.css @@ -6,7 +6,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-DateRangePicker { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Dialog.css b/packages/components/src/styles/basic/Dialog.css index 45ca2210c8..64366c4693 100644 --- a/packages/components/src/styles/basic/Dialog.css +++ b/packages/components/src/styles/basic/Dialog.css @@ -3,7 +3,7 @@ @import './Modal.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Dialog { padding: 30px; outline: none; diff --git a/packages/components/src/styles/basic/Disclosure.css b/packages/components/src/styles/basic/Disclosure.css index 5704d093c1..cb8cb529db 100644 --- a/packages/components/src/styles/basic/Disclosure.css +++ b/packages/components/src/styles/basic/Disclosure.css @@ -1,7 +1,7 @@ @import './theme.css'; @import './Button.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Disclosure { .react-aria-Button[slot='trigger'] { display: flex; diff --git a/packages/components/src/styles/basic/Form.css b/packages/components/src/styles/basic/Form.css index cebcb43466..00b864fa21 100644 --- a/packages/components/src/styles/basic/Form.css +++ b/packages/components/src/styles/basic/Form.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Form { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/GridList.css b/packages/components/src/styles/basic/GridList.css index 22ab490bcf..91944e9b21 100644 --- a/packages/components/src/styles/basic/GridList.css +++ b/packages/components/src/styles/basic/GridList.css @@ -3,7 +3,7 @@ @import './ToggleButton.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-GridList { display: flex; overflow: auto; diff --git a/packages/components/src/styles/basic/Label.css b/packages/components/src/styles/basic/Label.css index f3755f1ddc..1544ab27d9 100644 --- a/packages/components/src/styles/basic/Label.css +++ b/packages/components/src/styles/basic/Label.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Label { /* TODO: Review since taken from default quanta */ font-size: 0.9rem; diff --git a/packages/components/src/styles/basic/Link.css b/packages/components/src/styles/basic/Link.css index 8f91a261ed..08e46bddd5 100644 --- a/packages/components/src/styles/basic/Link.css +++ b/packages/components/src/styles/basic/Link.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Link { position: relative; color: var(--link-color); diff --git a/packages/components/src/styles/basic/ListBox.css b/packages/components/src/styles/basic/ListBox.css index 9cec61d80a..e9a5d92adf 100644 --- a/packages/components/src/styles/basic/ListBox.css +++ b/packages/components/src/styles/basic/ListBox.css @@ -1,7 +1,7 @@ @import './Checkbox.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ListBox { display: flex; overflow: auto; diff --git a/packages/components/src/styles/basic/Menu.css b/packages/components/src/styles/basic/Menu.css index 594f7b7ea1..dbf9370209 100644 --- a/packages/components/src/styles/basic/Menu.css +++ b/packages/components/src/styles/basic/Menu.css @@ -2,7 +2,7 @@ @import './Popover.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Menu { overflow: auto; min-width: 150px; diff --git a/packages/components/src/styles/basic/Meter.css b/packages/components/src/styles/basic/Meter.css index 2ab6709510..2947240afe 100644 --- a/packages/components/src/styles/basic/Meter.css +++ b/packages/components/src/styles/basic/Meter.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Meter { --fill-color: forestgreen; diff --git a/packages/components/src/styles/basic/Modal.css b/packages/components/src/styles/basic/Modal.css index 544e1c224d..b1c90d8578 100644 --- a/packages/components/src/styles/basic/Modal.css +++ b/packages/components/src/styles/basic/Modal.css @@ -2,7 +2,7 @@ @import './TextField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ModalOverlay { position: fixed; z-index: 100; diff --git a/packages/components/src/styles/basic/NumberField.css b/packages/components/src/styles/basic/NumberField.css index a5ed24c70a..b657f6b201 100644 --- a/packages/components/src/styles/basic/NumberField.css +++ b/packages/components/src/styles/basic/NumberField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-NumberField { margin-bottom: 8px; color: var(--text-color); diff --git a/packages/components/src/styles/basic/Popover.css b/packages/components/src/styles/basic/Popover.css index 89e8f419e5..5712ae8fcf 100644 --- a/packages/components/src/styles/basic/Popover.css +++ b/packages/components/src/styles/basic/Popover.css @@ -3,7 +3,7 @@ @import './Switch.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Popover { --background-color: var(--overlay-background); max-width: 250px; diff --git a/packages/components/src/styles/basic/ProgressBar.css b/packages/components/src/styles/basic/ProgressBar.css index 2a171ea53b..12119e67eb 100644 --- a/packages/components/src/styles/basic/ProgressBar.css +++ b/packages/components/src/styles/basic/ProgressBar.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ProgressBar { display: grid; width: 250px; diff --git a/packages/components/src/styles/basic/RadioGroup.css b/packages/components/src/styles/basic/RadioGroup.css index 2a05774dcc..07b905498d 100644 --- a/packages/components/src/styles/basic/RadioGroup.css +++ b/packages/components/src/styles/basic/RadioGroup.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-RadioGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/RangeCalendar.css b/packages/components/src/styles/basic/RangeCalendar.css index 025c9ee54d..f409bb678b 100644 --- a/packages/components/src/styles/basic/RangeCalendar.css +++ b/packages/components/src/styles/basic/RangeCalendar.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-RangeCalendar { width: fit-content; max-width: 100%; diff --git a/packages/components/src/styles/basic/SearchField.css b/packages/components/src/styles/basic/SearchField.css index ef23082851..ccd7d27570 100644 --- a/packages/components/src/styles/basic/SearchField.css +++ b/packages/components/src/styles/basic/SearchField.css @@ -2,7 +2,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-SearchField { display: grid; width: fit-content; diff --git a/packages/components/src/styles/basic/Select.css b/packages/components/src/styles/basic/Select.css index bcffcaedf1..592796bc13 100644 --- a/packages/components/src/styles/basic/Select.css +++ b/packages/components/src/styles/basic/Select.css @@ -4,7 +4,7 @@ @import './Form.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Select { color: var(--text-color); diff --git a/packages/components/src/styles/basic/Slider.css b/packages/components/src/styles/basic/Slider.css index 00f6a8bb76..2de1068078 100644 --- a/packages/components/src/styles/basic/Slider.css +++ b/packages/components/src/styles/basic/Slider.css @@ -1,7 +1,7 @@ @import './NumberField.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Slider { display: grid; max-width: 300px; diff --git a/packages/components/src/styles/basic/Switch.css b/packages/components/src/styles/basic/Switch.css index 85b4e44b9f..0508d2aa29 100644 --- a/packages/components/src/styles/basic/Switch.css +++ b/packages/components/src/styles/basic/Switch.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Switch { display: flex; align-items: center; diff --git a/packages/components/src/styles/basic/Table.css b/packages/components/src/styles/basic/Table.css index 5ac6d90256..6a55a242cc 100644 --- a/packages/components/src/styles/basic/Table.css +++ b/packages/components/src/styles/basic/Table.css @@ -5,7 +5,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { :root { --plone-table-border: 0 none; --plone-table-border-radius: 0; diff --git a/packages/components/src/styles/basic/Tabs.css b/packages/components/src/styles/basic/Tabs.css index 9662ecd6a4..811168c931 100644 --- a/packages/components/src/styles/basic/Tabs.css +++ b/packages/components/src/styles/basic/Tabs.css @@ -2,7 +2,7 @@ @import './Link.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Tabs { display: flex; color: var(--text-color); diff --git a/packages/components/src/styles/basic/TagGroup.css b/packages/components/src/styles/basic/TagGroup.css index d50abf9140..9fc9df8916 100644 --- a/packages/components/src/styles/basic/TagGroup.css +++ b/packages/components/src/styles/basic/TagGroup.css @@ -1,7 +1,7 @@ @import './ToggleButton.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TagGroup { display: flex; flex-direction: column; diff --git a/packages/components/src/styles/basic/TextField.css b/packages/components/src/styles/basic/TextField.css index f99b9178df..4be3b73c95 100644 --- a/packages/components/src/styles/basic/TextField.css +++ b/packages/components/src/styles/basic/TextField.css @@ -2,7 +2,7 @@ @import './Label.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TextField { display: flex; width: fit-content; diff --git a/packages/components/src/styles/basic/TimeField.css b/packages/components/src/styles/basic/TimeField.css index 4fb876a93b..b1ca5453e7 100644 --- a/packages/components/src/styles/basic/TimeField.css +++ b/packages/components/src/styles/basic/TimeField.css @@ -2,7 +2,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-TimeField { color: var(--text-color); } diff --git a/packages/components/src/styles/basic/ToggleButton.css b/packages/components/src/styles/basic/ToggleButton.css index 6e7ad2bb39..50dd494b1a 100644 --- a/packages/components/src/styles/basic/ToggleButton.css +++ b/packages/components/src/styles/basic/ToggleButton.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-ToggleButton { padding: 8px 8px; border: 1px solid var(--border-color); diff --git a/packages/components/src/styles/basic/Toolbar.css b/packages/components/src/styles/basic/Toolbar.css index 306f570fc7..07b03e84a9 100644 --- a/packages/components/src/styles/basic/Toolbar.css +++ b/packages/components/src/styles/basic/Toolbar.css @@ -4,7 +4,7 @@ @import './Menu.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Toolbar { display: flex; flex-wrap: wrap; diff --git a/packages/components/src/styles/basic/Tooltip.css b/packages/components/src/styles/basic/Tooltip.css index 8965f2bd7e..8bec7761ed 100644 --- a/packages/components/src/styles/basic/Tooltip.css +++ b/packages/components/src/styles/basic/Tooltip.css @@ -1,7 +1,7 @@ @import './Button.css'; @import './theme.css'; -@layer plone-components { +@layer plone-components.base { .react-aria-Tooltip { max-width: 150px; padding: 2px 8px; diff --git a/packages/components/src/styles/basic/icons.css b/packages/components/src/styles/basic/icons.css index ec69303237..6cfd1b3a50 100644 --- a/packages/components/src/styles/basic/icons.css +++ b/packages/components/src/styles/basic/icons.css @@ -1,6 +1,6 @@ @import './theme.css'; -@layer plone-components { +@layer plone-components.base { :root { /* These has to be mapped to global css custom properties based on the SG scales */ --quanta-icon-default-size-s: 18px; diff --git a/packages/types/src/config/Settings.d.ts b/packages/types/src/config/Settings.d.ts index 68419e2db2..61e87a8955 100644 --- a/packages/types/src/config/Settings.d.ts +++ b/packages/types/src/config/Settings.d.ts @@ -101,4 +101,5 @@ export interface SettingsConfig { includeSiteTitle: boolean; titleAndSiteTitleSeparator: string; }; + cssLayers: string[]; } diff --git a/packages/volto/package.json b/packages/volto/package.json index 64d9b1c45b..6ad031b51e 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -30,6 +30,9 @@ "coreAddons": { "volto-slate": { "package": "@plone/volto-slate" + }, + "cmsui": { + "package": "@plone/cmsui" } }, "main": "src/index.js", @@ -179,6 +182,7 @@ "dependencies": { "@loadable/component": "5.14.1", "@loadable/server": "5.14.0", + "@plone/cmsui": "workspace:*", "@plone/registry": "workspace:*", "@plone/scripts": "workspace:*", "@plone/volto-slate": "workspace:*", diff --git a/packages/volto/src/components/manage/Form/Form.jsx b/packages/volto/src/components/manage/Form/Form.jsx index d19b68a603..e5b358b3e0 100644 --- a/packages/volto/src/components/manage/Form/Form.jsx +++ b/packages/volto/src/components/manage/Form/Form.jsx @@ -5,7 +5,8 @@ import Icon from '@plone/volto/components/theme/Icon/Icon'; import Toast from '@plone/volto/components/manage/Toast/Toast'; -import { Field, BlocksForm } from '@plone/volto/components/manage/Form'; +import { BlocksForm } from '@plone/volto/components/manage/Form'; +import Field from '@plone/cmsui/components/Field/Field'; import BlocksToolbar from '@plone/volto/components/manage/Form/BlocksToolbar'; import UndoToolbar from '@plone/volto/components/manage/Form/UndoToolbar'; import { difference } from '@plone/volto/helpers/Utils/Utils'; diff --git a/packages/volto/src/components/manage/Form/InlineForm.jsx b/packages/volto/src/components/manage/Form/InlineForm.jsx index a92f6aaba2..6e132a9f74 100644 --- a/packages/volto/src/components/manage/Form/InlineForm.jsx +++ b/packages/volto/src/components/manage/Form/InlineForm.jsx @@ -14,7 +14,7 @@ import { arrayRange, } from '@plone/volto/helpers/Utils/Utils'; import Icon from '@plone/volto/components/theme/Icon/Icon'; -import { Field } from '@plone/volto/components/manage/Form'; +import Field from '@plone/cmsui/components/Field/Field'; import { applySchemaDefaults } from '@plone/volto/helpers/Blocks/Blocks'; import upSVG from '@plone/volto/icons/up-key.svg'; diff --git a/packages/volto/src/config/index.js b/packages/volto/src/config/index.js index 48ab6e0159..f4a497b546 100644 --- a/packages/volto/src/config/index.js +++ b/packages/volto/src/config/index.js @@ -177,6 +177,14 @@ let config = { includeSiteTitle: false, titleAndSiteTitleSeparator: '-', }, + cssLayers: [ + 'reset', + 'semanticUI', + 'plone-components', + 'layout', + 'addons', + 'theme', + ], }, experimental: { addBlockButton: { diff --git a/packages/volto/src/helpers/Html/Html.jsx b/packages/volto/src/helpers/Html/Html.jsx index 90237ca65f..096f485388 100644 --- a/packages/volto/src/helpers/Html/Html.jsx +++ b/packages/volto/src/helpers/Html/Html.jsx @@ -102,6 +102,12 @@ class Html extends Component { {head.meta.toComponent()} {head.link.toComponent()} {head.script.toComponent()} + + {config.settings.cssLayers && ( + // Load the CSS layers from config, if any + + )} + {head.style.toComponent()}