From 86dbd7a3746fd824e908b2839b2d208a0cffe4f4 Mon Sep 17 00:00:00 2001 From: Benjamin Marguin Date: Tue, 3 Oct 2023 10:42:34 +0200 Subject: [PATCH 1/2] Add ability to declare "embed" content to be displayed as iframe --- public/locales/en/translation.json | 12 +++ public/locales/fr/translation.json | 12 +++ .../RA/DataLayer/components/DataLayerForm.js | 16 +++ .../tabs/EmbedTab/EmbedConfigField.js | 35 ++++++ .../tabs/EmbedTab/EmbedItemInput.js | 101 ++++++++++++++++++ .../components/tabs/EmbedTab/index.js | 70 ++++++++++++ 6 files changed, 246 insertions(+) create mode 100644 src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js create mode 100644 src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js create mode 100644 src/modules/RA/DataLayer/components/tabs/EmbedTab/index.js diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index b71a4d03..e1061e8a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -542,6 +542,18 @@ "name": "Name", "compose": "Compose", "upload": "Upload" + }, + "embed": { + "tab": "Embed", + "no-embed": "To add an embed, click on", + "allow": "Enable", + "allow-display-data-embed": "Disable", + "all-fields": "Embeds", + "icon": "Icon", + "title": "Embed title", + "title-help": "", + "src": "Address", + "src-help": "Embed address (iframe)" } } }, diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 011fa295..9eab4626 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -594,6 +594,18 @@ "name": "Nom", "compose": "Composer", "upload": "Envoyer" + }, + "embed": { + "tab": "Inclusions", + "no-embed": "Pour activer les inclusions, cliquer sur", + "allow": "Activer", + "allow-display-data-embed": "Désactiver", + "all-fields": "Inclusions", + "icon": "Icône", + "title": "Titre de l'inclusion", + "title-help": "", + "src": "Adresse", + "src-help": "Adresse de la page à inclure (iframe)" } } }, diff --git a/src/modules/RA/DataLayer/components/DataLayerForm.js b/src/modules/RA/DataLayer/components/DataLayerForm.js index 82e89cb1..84a4cbec 100644 --- a/src/modules/RA/DataLayer/components/DataLayerForm.js +++ b/src/modules/RA/DataLayer/components/DataLayerForm.js @@ -13,6 +13,7 @@ import MinisheetTab from './tabs/MinisheetTab'; import FilterTab from './tabs/FilterTab'; import TableTab from './tabs/TableTab'; import StyleImageField from './StyleImageField'; +import EmbedTab from './tabs/EmbedTab'; const initialErrorState = { definition: false, @@ -22,6 +23,7 @@ const initialErrorState = { minisheet: false, filter: false, table: false, + embed: false, }; const inErrorReducer = (state, { type, payload }) => { @@ -91,6 +93,10 @@ const DataLayerForm = React.memo(props => { dispatch({ type: 'minisheet', payload: inError }); }, []); + const onEmbedErrorChange = React.useCallback(() => { + dispatch({ type: 'embed', paylaod: false }); + }, []); + const onTableErrorChange = React.useCallback(({ values: { fields = [], table_enable: tableEnable }, }) => { @@ -205,6 +211,16 @@ const DataLayerForm = React.memo(props => { + + + + ); }); diff --git a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js new file mode 100644 index 00000000..92f7ff09 --- /dev/null +++ b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { + useTranslate, + useInput, + ArrayInput, + SimpleFormIterator, +} from 'react-admin'; +import Typography from '@material-ui/core/Typography'; + +import EmbedItemInput from './EmbedItemInput'; + +const EmbedConfigField = ({ label, ...rest }) => { + const translate = useTranslate(); + const { + meta: { error }, + } = useInput(rest); + + return ( + <> + {translate(label)} + + {error && error.length > 0 && error.flatMap(err => + err && Object.entries(err).map(([key, value]) => ( + {key}: {translate(value)})))} + + + + + + + + ); +}; + +export default EmbedConfigField; diff --git a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js new file mode 100644 index 00000000..8c5ee41d --- /dev/null +++ b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js @@ -0,0 +1,101 @@ +import React from 'react'; +import { SelectInput, TextInput } from 'react-admin'; + +import { Icon } from '@blueprintjs/core'; + +const bpIcons = [ + 'chart', + 'curved-range-chart', + 'database', + 'diagram-tree', + 'doughnut-chart', + 'flow-branch', + 'flow-end', + 'flow-linear', + 'flow-review', + 'flow-review-branch', + 'flows', + 'form', + 'full-stacked-chart', + 'gantt-chart', + 'graph', + 'grid', + 'grouped-bar-chart', + 'heat-grid', + 'heatmap', + 'horizontal-bar-chart', + 'horizontal-bar-chart-asc', + 'horizontal-bar-chart-desc', + 'layout', + 'layout-auto', + 'layout-balloon', + 'layout-circle', + 'layout-grid', + 'layout-group-by', + 'layout-hierarchy', + 'layout-linear', + 'layout-skew-grid', + 'layout-sorted-clusters', + 'many-to-many', + 'many-to-one', + 'one-to-many', + 'one-to-one', + 'pie-chart', + 'polygon-filter', + 'regression-chart', + 'scatter-plot', + 'series-add', + 'series-configuration', + 'series-derived', + 'series-filtered', + 'series-search', + 'stacked-chart', + 'step-chart', + 'timeline-area-chart', + 'timeline-bar-chart', + 'timeline-line-chart', + 'trending-down', + 'trending-up', + 'vertical-bar-chart-asc', + 'vertical-bar-chart-desc', + 'waterfall-chart', +].sort(); + +const EmbedItemInput = ({ source }) => { + const iconChoices = React.useMemo( + () => bpIcons.map(bpIcon => ({ + id: bpIcon, + name: <> {bpIcon}, + })), + [], + ); + + return ( +
+ + + + + +
+ ); +}; + +export default EmbedItemInput; diff --git a/src/modules/RA/DataLayer/components/tabs/EmbedTab/index.js b/src/modules/RA/DataLayer/components/tabs/EmbedTab/index.js new file mode 100644 index 00000000..1b1268af --- /dev/null +++ b/src/modules/RA/DataLayer/components/tabs/EmbedTab/index.js @@ -0,0 +1,70 @@ +import React from 'react'; + +import { BooleanInput, useTranslate } from 'react-admin'; +import { useField } from 'react-final-form'; + +import Typography from '@material-ui/core/Typography'; +import Placeholder from '../../../../../../components/Placeholder'; + +import EmbedConfigField from './EmbedConfigField'; + +const validateEmbedFields = data => { + const valid = !data.some(({ label }) => label && !label.length); + if (!valid) { + return 'datalayer.form.embed.row-in-error'; + } + return undefined; +}; + +const EmbedConfigTabContent = props => { + const { input: { value: embedEnable, onChange: onEmbedEnableChange } } = useField('settings.embed_enable'); + const { input: { value: source } } = useField('source'); + const { input: { value: embedExportEnable } } = useField('embed_export_enable'); + const translate = useTranslate(); + + if (!source) { + return ( + + + {translate('datalayer.form.embed.no-source')} + + + ); + } + + // No embed yet + if (!embedEnable) { + return ( + +
+ {translate('datalayer.form.embed.no-embed')} + +
+
+ ); + } + + return ( + <> + + + + ); +}; + + +export default EmbedConfigTabContent; From 77eaa9162e1b3b09831990ea04cf45f513a7e743 Mon Sep 17 00:00:00 2001 From: Maxime Bouveron Date: Thu, 9 Nov 2023 17:19:02 +0100 Subject: [PATCH 2/2] EmberInput improvements --- public/locales/en/translation.json | 6 +- public/locales/fr/translation.json | 6 +- src/components/BPIcon.js | 71 ++++++++ .../tabs/EmbedTab/EmbedConfigField.js | 30 ++-- .../tabs/EmbedTab/EmbedItemInput.js | 170 +++++++++--------- 5 files changed, 191 insertions(+), 92 deletions(-) create mode 100644 src/components/BPIcon.js diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index e1061e8a..b152514d 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -553,7 +553,11 @@ "title": "Embed title", "title-help": "", "src": "Address", - "src-help": "Embed address (iframe)" + "src-help": "Embed address (iframe)", + "preview": "Preview", + "fullscreen": "Fullscreen", + "width": "Width (px)", + "height": "Height (px)" } } }, diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 9eab4626..766d2d10 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -605,7 +605,11 @@ "title": "Titre de l'inclusion", "title-help": "", "src": "Adresse", - "src-help": "Adresse de la page à inclure (iframe)" + "src-help": "Adresse de la page à inclure (iframe)", + "preview": "Prévisualiser", + "fullscreen": "Plein écran", + "width": "Largeur (px)", + "height": "Hauteur (px)" } } }, diff --git a/src/components/BPIcon.js b/src/components/BPIcon.js new file mode 100644 index 00000000..c1085f48 --- /dev/null +++ b/src/components/BPIcon.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { Icon } from '@blueprintjs/core'; + +export const graphIcons = [ + 'chart', + 'curved-range-chart', + 'database', + 'diagram-tree', + 'doughnut-chart', + 'flow-branch', + 'flow-end', + 'flow-linear', + 'flow-review', + 'flow-review-branch', + 'flows', + 'form', + 'full-stacked-chart', + 'gantt-chart', + 'graph', + 'grid', + 'grouped-bar-chart', + 'heat-grid', + 'heatmap', + 'horizontal-bar-chart', + 'horizontal-bar-chart-asc', + 'horizontal-bar-chart-desc', + 'layout', + 'layout-auto', + 'layout-balloon', + 'layout-circle', + 'layout-grid', + 'layout-group-by', + 'layout-hierarchy', + 'layout-linear', + 'layout-skew-grid', + 'layout-sorted-clusters', + 'many-to-many', + 'many-to-one', + 'one-to-many', + 'one-to-one', + 'pie-chart', + 'polygon-filter', + 'regression-chart', + 'scatter-plot', + 'series-add', + 'series-configuration', + 'series-derived', + 'series-filtered', + 'series-search', + 'stacked-chart', + 'step-chart', + 'timeline-area-chart', + 'timeline-bar-chart', + 'timeline-line-chart', + 'trending-down', + 'trending-up', + 'vertical-bar-chart-asc', + 'vertical-bar-chart-desc', + 'waterfall-chart', +].sort(); + +const BPIcon = ({ icon, displayIconName, style = {}, ...rest }) => + (displayIconName ? ( + <> + {icon} + + ) : ( + + )); + +export default BPIcon; diff --git a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js index 92f7ff09..39725468 100644 --- a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js +++ b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedConfigField.js @@ -17,17 +17,27 @@ const EmbedConfigField = ({ label, ...rest }) => { return ( <> - {translate(label)} + + {translate(label)} + - {error && error.length > 0 && error.flatMap(err => - err && Object.entries(err).map(([key, value]) => ( - {key}: {translate(value)})))} - - - - - - + {error?.length > 0 && + error.flatMap( + err => + err && + Object.entries(err).map(([key, value]) => ( + + {key}: {translate(value)} + + )), + )} +
+ + + + + +
); }; diff --git a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js index 8c5ee41d..288d8c93 100644 --- a/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js +++ b/src/modules/RA/DataLayer/components/tabs/EmbedTab/EmbedItemInput.js @@ -1,99 +1,109 @@ -import React from 'react'; -import { SelectInput, TextInput } from 'react-admin'; +import React, { useEffect } from 'react'; +import { BooleanInput, NumberInput, SelectInput, TextInput, useTranslate } from 'react-admin'; -import { Icon } from '@blueprintjs/core'; - -const bpIcons = [ - 'chart', - 'curved-range-chart', - 'database', - 'diagram-tree', - 'doughnut-chart', - 'flow-branch', - 'flow-end', - 'flow-linear', - 'flow-review', - 'flow-review-branch', - 'flows', - 'form', - 'full-stacked-chart', - 'gantt-chart', - 'graph', - 'grid', - 'grouped-bar-chart', - 'heat-grid', - 'heatmap', - 'horizontal-bar-chart', - 'horizontal-bar-chart-asc', - 'horizontal-bar-chart-desc', - 'layout', - 'layout-auto', - 'layout-balloon', - 'layout-circle', - 'layout-grid', - 'layout-group-by', - 'layout-hierarchy', - 'layout-linear', - 'layout-skew-grid', - 'layout-sorted-clusters', - 'many-to-many', - 'many-to-one', - 'one-to-many', - 'one-to-one', - 'pie-chart', - 'polygon-filter', - 'regression-chart', - 'scatter-plot', - 'series-add', - 'series-configuration', - 'series-derived', - 'series-filtered', - 'series-search', - 'stacked-chart', - 'step-chart', - 'timeline-area-chart', - 'timeline-bar-chart', - 'timeline-line-chart', - 'trending-down', - 'trending-up', - 'vertical-bar-chart-asc', - 'vertical-bar-chart-desc', - 'waterfall-chart', -].sort(); +import { + Accordion, + AccordionDetails, + AccordionSummary, + Typography, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { useField } from 'react-final-form'; +import Condition from '../../../../../../components/react-admin/Condition'; +import BPIcon, { graphIcons } from '../../../../../../components/BPIcon'; const EmbedItemInput = ({ source }) => { + const [previewExpanded, setPreviewExpanded] = React.useState(false); + const translate = useTranslate(); + const iconChoices = React.useMemo( - () => bpIcons.map(bpIcon => ({ - id: bpIcon, - name: <> {bpIcon}, - })), + () => + graphIcons.map(bpIcon => ({ + id: bpIcon, + name: , + })), [], ); - return ( -
- + const { + input: { value: url }, + } = useField(`${source}.src`); - + useEffect(() => { + setPreviewExpanded(false); + }, [url]); + + return ( +
+
+ , + }} + style={{ width: '5em', minWidth: '5em' }} + /> + +
+ val.match(/(^(https?:)?\/\/.)/)} + > + setPreviewExpanded(val)} + style={{ width: '40em', marginBottom: '2em' }} + > + }> + {translate('datalayer.form.embed.preview')} + + + {previewExpanded && ( +