node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var Symbols = require('@strapi/icons/symbols');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var Icons__namespace = /*#__PURE__*/_interopNamespaceDefault(Icons);
var Symbols__namespace = /*#__PURE__*/_interopNamespaceDefault(Symbols);
const ComponentIcon = ({ showBackground = true, icon = 'dashboard', ...props })=>{
const Icon = COMPONENT_ICONS[icon] || COMPONENT_ICONS.dashboard;
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
alignItems: "center",
background: showBackground ? 'neutral200' : undefined,
justifyContent: "center",
height: 8,
width: 8,
color: "neutral600",
borderRadius: showBackground ? '50%' : 0,
...props,
children: /*#__PURE__*/ jsxRuntime.jsx(Icon, {
height: "2rem",
width: "2rem"
})
});
};
const COMPONENT_ICONS = {
alien: Icons__namespace.Alien,
apps: Icons__namespace.GridNine,
archive: Icons__namespace.Archive,
arrowDown: Icons__namespace.ArrowDown,
arrowLeft: Icons__namespace.ArrowLeft,
arrowRight: Icons__namespace.ArrowRight,
arrowUp: Icons__namespace.ArrowUp,
attachment: Icons__namespace.Paperclip,
bell: Icons__namespace.Bell,
bold: Icons__namespace.Bold,
book: Icons__namespace.Book,
briefcase: Icons__namespace.Briefcase,
brush: Icons__namespace.PaintBrush,
bulletList: Icons__namespace.BulletList,
calendar: Icons__namespace.Calendar,
car: Icons__namespace.Car,
cast: Icons__namespace.Cast,
chartBubble: Icons__namespace.ChartBubble,
chartCircle: Icons__namespace.ChartCircle,
chartPie: Icons__namespace.ChartPie,
check: Icons__namespace.Check,
clock: Icons__namespace.Clock,
cloud: Icons__namespace.Cloud,
code: Icons__namespace.Code,
cog: Icons__namespace.Cog,
collapse: Icons__namespace.Collapse,
command: Icons__namespace.Command,
connector: Icons__namespace.Faders,
crop: Icons__namespace.Crop,
crown: Icons__namespace.Crown,
cup: Icons__namespace.Coffee,
cursor: Icons__namespace.Cursor,
dashboard: Icons__namespace.SquaresFour,
database: Icons__namespace.Database,
discuss: Icons__namespace.Discuss,
doctor: Icons__namespace.Stethoscope,
earth: Icons__namespace.Earth,
emotionHappy: Icons__namespace.EmotionHappy,
emotionUnhappy: Icons__namespace.EmotionUnhappy,
envelop: Icons__namespace.Mail,
exit: Icons__namespace.SignOut,
expand: Icons__namespace.Expand,
eye: Icons__namespace.Eye,
feather: Icons__namespace.Feather,
file: Icons__namespace.File,
fileError: Icons__namespace.FileError,
filePdf: Icons__namespace.FilePdf,
filter: Icons__namespace.Filter,
folder: Icons__namespace.Folder,
gate: Icons__namespace.CastleTurret,
gift: Icons__namespace.Gift,
globe: Icons__namespace.Globe,
grid: Icons__namespace.GridFour,
handHeart: Icons__namespace.HandHeart,
hashtag: Icons__namespace.Hashtag,
headphone: Icons__namespace.Headphones,
heart: Icons__namespace.Heart,
house: Icons__namespace.House,
information: Icons__namespace.Information,
italic: Icons__namespace.Italic,
key: Icons__namespace.Key,
landscape: Icons__namespace.Images,
layer: Icons__namespace.ListPlus,
layout: Icons__namespace.Layout,
lightbulb: Icons__namespace.Lightbulb,
link: Icons__namespace.Link,
lock: Icons__namespace.Lock,
magic: Icons__namespace.Magic,
manyToMany: Icons__namespace.ManyToMany,
manyToOne: Icons__namespace.ManyToOne,
manyWays: Icons__namespace.ManyWays,
medium: Symbols__namespace.Medium,
message: Icons__namespace.Message,
microphone: Icons__namespace.Microphone,
monitor: Icons__namespace.Monitor,
moon: Icons__namespace.Moon,
music: Icons__namespace.MusicNotes,
oneToMany: Icons__namespace.OneToMany,
oneToOne: Icons__namespace.OneToOne,
oneWay: Icons__namespace.OneWay,
paint: Icons__namespace.PaintBrush,
paintBrush: Icons__namespace.PaintBrush,
paperPlane: Icons__namespace.PaperPlane,
pencil: Icons__namespace.Pencil,
phone: Icons__namespace.Phone,
picture: Icons__namespace.Image,
pin: Icons__namespace.Pin,
pinMap: Icons__namespace.PinMap,
plane: Icons__namespace.Plane,
play: Icons__namespace.Play,
plus: Icons__namespace.Plus,
priceTag: Icons__namespace.PriceTag,
puzzle: Icons__namespace.PuzzlePiece,
question: Icons__namespace.Question,
quote: Icons__namespace.Quotes,
refresh: Icons__namespace.ArrowClockwise,
restaurant: Icons__namespace.Restaurant,
rocket: Icons__namespace.Rocket,
rotate: Icons__namespace.ArrowsCounterClockwise,
scissors: Icons__namespace.Scissors,
search: Icons__namespace.Search,
seed: Icons__namespace.Plant,
server: Icons__namespace.Server,
shield: Icons__namespace.Shield,
shirt: Icons__namespace.Shirt,
shoppingCart: Icons__namespace.ShoppingCart,
slideshow: Icons__namespace.PresentationChart,
stack: Icons__namespace.Stack,
star: Icons__namespace.Star,
store: Icons__namespace.Store,
strikeThrough: Icons__namespace.StrikeThrough,
sun: Icons__namespace.Sun,
television: Icons__namespace.Television,
thumbDown: Icons__namespace.ThumbDown,
thumbUp: Icons__namespace.ThumbUp,
train: Icons__namespace.Train,
twitter: Symbols__namespace.X,
typhoon: Icons__namespace.Typhoon,
underline: Icons__namespace.Underline,
user: Icons__namespace.User,
volumeMute: Icons__namespace.VolumeMute,
volumeUp: Icons__namespace.VolumeUp,
walk: Icons__namespace.Walk,
wheelchair: Icons__namespace.Wheelchair,
write: Icons__namespace.Feather
};
exports.COMPONENT_ICONS = COMPONENT_ICONS;
exports.ComponentIcon = ComponentIcon;
//# sourceMappingURL=ComponentIcon.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,153 @@
import { jsx } from 'react/jsx-runtime';
import 'react';
import { Flex } from '@strapi/design-system';
import * as Icons from '@strapi/icons';
import * as Symbols from '@strapi/icons/symbols';
const ComponentIcon = ({ showBackground = true, icon = 'dashboard', ...props })=>{
const Icon = COMPONENT_ICONS[icon] || COMPONENT_ICONS.dashboard;
return /*#__PURE__*/ jsx(Flex, {
alignItems: "center",
background: showBackground ? 'neutral200' : undefined,
justifyContent: "center",
height: 8,
width: 8,
color: "neutral600",
borderRadius: showBackground ? '50%' : 0,
...props,
children: /*#__PURE__*/ jsx(Icon, {
height: "2rem",
width: "2rem"
})
});
};
const COMPONENT_ICONS = {
alien: Icons.Alien,
apps: Icons.GridNine,
archive: Icons.Archive,
arrowDown: Icons.ArrowDown,
arrowLeft: Icons.ArrowLeft,
arrowRight: Icons.ArrowRight,
arrowUp: Icons.ArrowUp,
attachment: Icons.Paperclip,
bell: Icons.Bell,
bold: Icons.Bold,
book: Icons.Book,
briefcase: Icons.Briefcase,
brush: Icons.PaintBrush,
bulletList: Icons.BulletList,
calendar: Icons.Calendar,
car: Icons.Car,
cast: Icons.Cast,
chartBubble: Icons.ChartBubble,
chartCircle: Icons.ChartCircle,
chartPie: Icons.ChartPie,
check: Icons.Check,
clock: Icons.Clock,
cloud: Icons.Cloud,
code: Icons.Code,
cog: Icons.Cog,
collapse: Icons.Collapse,
command: Icons.Command,
connector: Icons.Faders,
crop: Icons.Crop,
crown: Icons.Crown,
cup: Icons.Coffee,
cursor: Icons.Cursor,
dashboard: Icons.SquaresFour,
database: Icons.Database,
discuss: Icons.Discuss,
doctor: Icons.Stethoscope,
earth: Icons.Earth,
emotionHappy: Icons.EmotionHappy,
emotionUnhappy: Icons.EmotionUnhappy,
envelop: Icons.Mail,
exit: Icons.SignOut,
expand: Icons.Expand,
eye: Icons.Eye,
feather: Icons.Feather,
file: Icons.File,
fileError: Icons.FileError,
filePdf: Icons.FilePdf,
filter: Icons.Filter,
folder: Icons.Folder,
gate: Icons.CastleTurret,
gift: Icons.Gift,
globe: Icons.Globe,
grid: Icons.GridFour,
handHeart: Icons.HandHeart,
hashtag: Icons.Hashtag,
headphone: Icons.Headphones,
heart: Icons.Heart,
house: Icons.House,
information: Icons.Information,
italic: Icons.Italic,
key: Icons.Key,
landscape: Icons.Images,
layer: Icons.ListPlus,
layout: Icons.Layout,
lightbulb: Icons.Lightbulb,
link: Icons.Link,
lock: Icons.Lock,
magic: Icons.Magic,
manyToMany: Icons.ManyToMany,
manyToOne: Icons.ManyToOne,
manyWays: Icons.ManyWays,
medium: Symbols.Medium,
message: Icons.Message,
microphone: Icons.Microphone,
monitor: Icons.Monitor,
moon: Icons.Moon,
music: Icons.MusicNotes,
oneToMany: Icons.OneToMany,
oneToOne: Icons.OneToOne,
oneWay: Icons.OneWay,
paint: Icons.PaintBrush,
paintBrush: Icons.PaintBrush,
paperPlane: Icons.PaperPlane,
pencil: Icons.Pencil,
phone: Icons.Phone,
picture: Icons.Image,
pin: Icons.Pin,
pinMap: Icons.PinMap,
plane: Icons.Plane,
play: Icons.Play,
plus: Icons.Plus,
priceTag: Icons.PriceTag,
puzzle: Icons.PuzzlePiece,
question: Icons.Question,
quote: Icons.Quotes,
refresh: Icons.ArrowClockwise,
restaurant: Icons.Restaurant,
rocket: Icons.Rocket,
rotate: Icons.ArrowsCounterClockwise,
scissors: Icons.Scissors,
search: Icons.Search,
seed: Icons.Plant,
server: Icons.Server,
shield: Icons.Shield,
shirt: Icons.Shirt,
shoppingCart: Icons.ShoppingCart,
slideshow: Icons.PresentationChart,
stack: Icons.Stack,
star: Icons.Star,
store: Icons.Store,
strikeThrough: Icons.StrikeThrough,
sun: Icons.Sun,
television: Icons.Television,
thumbDown: Icons.ThumbDown,
thumbUp: Icons.ThumbUp,
train: Icons.Train,
twitter: Symbols.X,
typhoon: Icons.Typhoon,
underline: Icons.Underline,
user: Icons.User,
volumeMute: Icons.VolumeMute,
volumeUp: Icons.VolumeUp,
walk: Icons.Walk,
wheelchair: Icons.Wheelchair,
write: Icons.Feather
};
export { COMPONENT_ICONS, ComponentIcon };
//# sourceMappingURL=ComponentIcon.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,259 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var yup = require('yup');
var attributes = require('../../constants/attributes.js');
var init = require('../../services/init.js');
var strings = require('../../utils/strings.js');
var translations = require('../../utils/translations.js');
var FieldTypeIcon = require('../FieldTypeIcon.js');
var Fields = require('./Fields.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
/* -------------------------------------------------------------------------------------------------
* Constants
* -----------------------------------------------------------------------------------------------*/ const FIELD_SCHEMA = yup__namespace.object().shape({
label: yup__namespace.string().required().nullable(),
description: yup__namespace.string(),
editable: yup__namespace.boolean(),
size: yup__namespace.number().required()
});
const EditFieldForm = ({ attribute, name, onClose })=>{
const { formatMessage } = reactIntl.useIntl();
const { toggleNotification } = strapiAdmin.useNotification();
const { value, onChange } = strapiAdmin.useField(name);
const { data: mainFieldOptions } = init.useGetInitialDataQuery(undefined, {
selectFromResult: (res)=>{
if (attribute?.type !== 'relation' || !res.data) {
return {
data: []
};
}
if ('targetModel' in attribute && typeof attribute.targetModel === 'string') {
const targetSchema = res.data.contentTypes.find((schema)=>schema.uid === attribute.targetModel);
if (targetSchema) {
return {
data: Object.entries(targetSchema.attributes).reduce((acc, [key, attribute])=>{
/**
* Create the list of attributes from the schema as to which can
* be our `mainField` and dictate the display name of the schema
* we're editing.
*/ if (!attributes.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD.includes(attribute.type)) {
acc.push({
label: key,
value: key
});
}
return acc;
}, [])
};
}
}
return {
data: []
};
},
skip: attribute?.type !== 'relation'
});
if (!value || value.name === Fields.TEMP_FIELD_NAME || !attribute) {
// This is very unlikely to happen, but it ensures the form is not opened without a value.
console.error("You've opened a field to edit without it being part of the form, this is likely a bug with Strapi. Please open an issue.");
toggleNotification({
message: formatMessage({
id: 'content-manager.containers.edit-settings.modal-form.error',
defaultMessage: 'An error occurred while trying to open the form.'
}),
type: 'danger'
});
return null;
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Content, {
children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Form, {
method: "PUT",
initialValues: value,
validationSchema: FIELD_SCHEMA,
onSubmit: (data)=>{
onChange(name, data);
onClose();
},
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 3,
children: [
/*#__PURE__*/ jsxRuntime.jsx(FieldTypeIcon.FieldTypeIcon, {
type: attribute.type
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Title, {
children: formatMessage({
id: 'content-manager.containers.edit-settings.modal-form.label',
defaultMessage: 'Edit {fieldName}'
}, {
fieldName: strings.capitalise(value.name)
})
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
children: [
{
name: 'label',
label: formatMessage({
id: translations.getTranslation('containers.edit-settings.modal-form.label'),
defaultMessage: 'Label'
}),
size: 6,
type: 'string'
},
{
name: 'description',
label: formatMessage({
id: translations.getTranslation('containers.edit-settings.modal-form.description'),
defaultMessage: 'Description'
}),
size: 6,
type: 'string'
},
{
name: 'placeholder',
label: formatMessage({
id: translations.getTranslation('containers.edit-settings.modal-form.placeholder'),
defaultMessage: 'Placeholder'
}),
size: 6,
type: 'string'
},
{
name: 'editable',
label: formatMessage({
id: translations.getTranslation('containers.edit-settings.modal-form.editable'),
defaultMessage: 'Editable'
}),
size: 6,
type: 'boolean'
},
{
name: 'mainField',
label: formatMessage({
id: translations.getTranslation('containers.edit-settings.modal-form.mainField'),
defaultMessage: 'Entry title'
}),
hint: formatMessage({
id: translations.getTranslation('containers.SettingPage.edit-settings.modal-form.mainField.hint'),
defaultMessage: 'Set the displayed field'
}),
size: 6,
options: mainFieldOptions,
type: 'enumeration'
},
{
name: 'size',
label: formatMessage({
id: translations.getTranslation('containers.ListSettingsView.modal-form.size'),
defaultMessage: 'Size'
}),
size: 6,
options: [
{
value: '4',
label: '33%'
},
{
value: '6',
label: '50%'
},
{
value: '8',
label: '66%'
},
{
value: '12',
label: '100%'
}
],
type: 'enumeration'
}
].filter(filterFieldsBasedOnAttributeType(attribute.type)).map(({ size, ...field })=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...field
})
}, field.name))
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Footer, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Close, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "tertiary",
children: formatMessage({
id: 'app.components.Button.cancel',
defaultMessage: 'Cancel'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
type: "submit",
children: formatMessage({
id: 'global.finish',
defaultMessage: 'Finish'
})
})
]
})
]
})
});
};
/**
* @internal
* @description not all edit fields have the same editable properties, it depends on the type
* e.g. a dynamic zone can only change it's label.
*/ const filterFieldsBasedOnAttributeType = (type)=>(field)=>{
switch(type){
case 'blocks':
case 'richtext':
return field.name !== 'size' && field.name !== 'mainField';
case 'boolean':
case 'media':
return field.name !== 'placeholder' && field.name !== 'mainField';
case 'component':
case 'dynamiczone':
return field.name === 'label' || field.name === 'editable';
case 'json':
return field.name !== 'placeholder' && field.name !== 'mainField' && field.name !== 'size';
case 'relation':
return true;
default:
return field.name !== 'mainField';
}
};
exports.EditFieldForm = EditFieldForm;
//# sourceMappingURL=EditFieldForm.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,238 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { useNotification, useField, Form, InputRenderer } from '@strapi/admin/strapi-admin';
import { Modal, Flex, Grid, Button } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import * as yup from 'yup';
import { ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD } from '../../constants/attributes.mjs';
import { useGetInitialDataQuery } from '../../services/init.mjs';
import { capitalise } from '../../utils/strings.mjs';
import { getTranslation } from '../../utils/translations.mjs';
import { FieldTypeIcon } from '../FieldTypeIcon.mjs';
import { TEMP_FIELD_NAME } from './Fields.mjs';
/* -------------------------------------------------------------------------------------------------
* Constants
* -----------------------------------------------------------------------------------------------*/ const FIELD_SCHEMA = yup.object().shape({
label: yup.string().required().nullable(),
description: yup.string(),
editable: yup.boolean(),
size: yup.number().required()
});
const EditFieldForm = ({ attribute, name, onClose })=>{
const { formatMessage } = useIntl();
const { toggleNotification } = useNotification();
const { value, onChange } = useField(name);
const { data: mainFieldOptions } = useGetInitialDataQuery(undefined, {
selectFromResult: (res)=>{
if (attribute?.type !== 'relation' || !res.data) {
return {
data: []
};
}
if ('targetModel' in attribute && typeof attribute.targetModel === 'string') {
const targetSchema = res.data.contentTypes.find((schema)=>schema.uid === attribute.targetModel);
if (targetSchema) {
return {
data: Object.entries(targetSchema.attributes).reduce((acc, [key, attribute])=>{
/**
* Create the list of attributes from the schema as to which can
* be our `mainField` and dictate the display name of the schema
* we're editing.
*/ if (!ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD.includes(attribute.type)) {
acc.push({
label: key,
value: key
});
}
return acc;
}, [])
};
}
}
return {
data: []
};
},
skip: attribute?.type !== 'relation'
});
if (!value || value.name === TEMP_FIELD_NAME || !attribute) {
// This is very unlikely to happen, but it ensures the form is not opened without a value.
console.error("You've opened a field to edit without it being part of the form, this is likely a bug with Strapi. Please open an issue.");
toggleNotification({
message: formatMessage({
id: 'content-manager.containers.edit-settings.modal-form.error',
defaultMessage: 'An error occurred while trying to open the form.'
}),
type: 'danger'
});
return null;
}
return /*#__PURE__*/ jsx(Modal.Content, {
children: /*#__PURE__*/ jsxs(Form, {
method: "PUT",
initialValues: value,
validationSchema: FIELD_SCHEMA,
onSubmit: (data)=>{
onChange(name, data);
onClose();
},
children: [
/*#__PURE__*/ jsx(Modal.Header, {
children: /*#__PURE__*/ jsxs(Flex, {
gap: 3,
children: [
/*#__PURE__*/ jsx(FieldTypeIcon, {
type: attribute.type
}),
/*#__PURE__*/ jsx(Modal.Title, {
children: formatMessage({
id: 'content-manager.containers.edit-settings.modal-form.label',
defaultMessage: 'Edit {fieldName}'
}, {
fieldName: capitalise(value.name)
})
})
]
})
}),
/*#__PURE__*/ jsx(Modal.Body, {
children: /*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
children: [
{
name: 'label',
label: formatMessage({
id: getTranslation('containers.edit-settings.modal-form.label'),
defaultMessage: 'Label'
}),
size: 6,
type: 'string'
},
{
name: 'description',
label: formatMessage({
id: getTranslation('containers.edit-settings.modal-form.description'),
defaultMessage: 'Description'
}),
size: 6,
type: 'string'
},
{
name: 'placeholder',
label: formatMessage({
id: getTranslation('containers.edit-settings.modal-form.placeholder'),
defaultMessage: 'Placeholder'
}),
size: 6,
type: 'string'
},
{
name: 'editable',
label: formatMessage({
id: getTranslation('containers.edit-settings.modal-form.editable'),
defaultMessage: 'Editable'
}),
size: 6,
type: 'boolean'
},
{
name: 'mainField',
label: formatMessage({
id: getTranslation('containers.edit-settings.modal-form.mainField'),
defaultMessage: 'Entry title'
}),
hint: formatMessage({
id: getTranslation('containers.SettingPage.edit-settings.modal-form.mainField.hint'),
defaultMessage: 'Set the displayed field'
}),
size: 6,
options: mainFieldOptions,
type: 'enumeration'
},
{
name: 'size',
label: formatMessage({
id: getTranslation('containers.ListSettingsView.modal-form.size'),
defaultMessage: 'Size'
}),
size: 6,
options: [
{
value: '4',
label: '33%'
},
{
value: '6',
label: '50%'
},
{
value: '8',
label: '66%'
},
{
value: '12',
label: '100%'
}
],
type: 'enumeration'
}
].filter(filterFieldsBasedOnAttributeType(attribute.type)).map(({ size, ...field })=>/*#__PURE__*/ jsx(Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(InputRenderer, {
...field
})
}, field.name))
})
}),
/*#__PURE__*/ jsxs(Modal.Footer, {
children: [
/*#__PURE__*/ jsx(Modal.Close, {
children: /*#__PURE__*/ jsx(Button, {
variant: "tertiary",
children: formatMessage({
id: 'app.components.Button.cancel',
defaultMessage: 'Cancel'
})
})
}),
/*#__PURE__*/ jsx(Button, {
type: "submit",
children: formatMessage({
id: 'global.finish',
defaultMessage: 'Finish'
})
})
]
})
]
})
});
};
/**
* @internal
* @description not all edit fields have the same editable properties, it depends on the type
* e.g. a dynamic zone can only change it's label.
*/ const filterFieldsBasedOnAttributeType = (type)=>(field)=>{
switch(type){
case 'blocks':
case 'richtext':
return field.name !== 'size' && field.name !== 'mainField';
case 'boolean':
case 'media':
return field.name !== 'placeholder' && field.name !== 'mainField';
case 'component':
case 'dynamiczone':
return field.name === 'label' || field.name === 'editable';
case 'json':
return field.name !== 'placeholder' && field.name !== 'mainField' && field.name !== 'size';
case 'relation':
return true;
default:
return field.name !== 'mainField';
}
};
export { EditFieldForm };
//# sourceMappingURL=EditFieldForm.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,534 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var fractionalIndexing = require('fractional-indexing');
var reactDndHtml5Backend = require('react-dnd-html5-backend');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var dragAndDrop = require('../../constants/dragAndDrop.js');
var useDragAndDrop = require('../../hooks/useDragAndDrop.js');
var translations = require('../../utils/translations.js');
var ComponentIcon = require('../ComponentIcon.js');
var EditFieldForm = require('./EditFieldForm.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const Fields = ({ attributes, fieldSizes, components, metadatas = {} })=>{
const { formatMessage } = reactIntl.useIntl();
const layout = strapiAdmin.useForm('Fields', (state)=>state.values.layout ?? []);
const onChange = strapiAdmin.useForm('Fields', (state)=>state.onChange);
const addFieldRow = strapiAdmin.useForm('Fields', (state)=>state.addFieldRow);
const removeFieldRow = strapiAdmin.useForm('Fields', (state)=>state.removeFieldRow);
const existingFields = layout.map((row)=>row.children.map((field)=>field.name)).flat();
/**
* Get the fields that are not already in the layout
* But also check that they are visible before we give users
* the option to display them. e.g. `id` is not visible.
*/ const remainingFields = Object.entries(metadatas).reduce((acc, current)=>{
const [name, { visible, ...field }] = current;
if (!existingFields.includes(name) && visible === true) {
const type = attributes[name]?.type;
const size = type ? fieldSizes[type] : 12;
acc.push({
...field,
label: field.label ?? name,
name,
size
});
}
return acc;
}, []);
const handleMoveField = ([newRowIndex, newFieldIndex], [currentRowIndex, currentFieldIndex])=>{
/**
* Because this view has the constraint that the sum of field sizes cannot be greater
* than 12, we don't use the form's method to move field rows, instead, we calculate
* the new layout and set the entire form.
*/ const newLayout = structuredClone(layout);
/**
* Remove field from the current layout space using splice so we have the item
*/ const [field] = newLayout[currentRowIndex].children.splice(currentFieldIndex, 1);
if (!field || field.name === TEMP_FIELD_NAME) {
return;
}
const newRow = newLayout[newRowIndex].children;
const [newFieldKey] = generateNKeysBetween(newRow, 1, currentFieldIndex, newFieldIndex);
/**
* Next we inject the field into it's new row at it's specified index, we then remove the spaces
* if they exist and recalculate into potentially two arrays ONLY if the sizing is now over 12,
* the row and the rest of the row that couldn't fit.
*
* for example, if i have a row of `[{size: 4}, {size: 6}]` and i add `{size: 8}` a index 0,
* the new array will look like `[{size: 8}, {size: 4}, {size: 6}]` which breaks the limit of 12,
* so instead we make two arrays for the new rows `[[{size: 8}, {size: 4}], [{size: 6}]]` which we
* then inject at the original row point with spacers included.
*/ newRow.splice(newFieldIndex, 0, {
...field,
__temp_key__: newFieldKey
});
if (newLayout[newRowIndex].children.reduce((acc, curr)=>acc + curr.size, 0) > 12) {
const recalculatedRows = chunkArray(newLayout[newRowIndex].children.filter((field)=>field.name !== TEMP_FIELD_NAME));
const rowKeys = generateNKeysBetween(newLayout, recalculatedRows.length, currentRowIndex, newRowIndex);
newLayout.splice(newRowIndex, 1, ...recalculatedRows.map((row, index)=>({
__temp_key__: rowKeys[index],
children: row
})));
}
/**
* Now we remove our spacers from the rows so we can understand what dead rows exist:
* - if there's only spacers left
* - there's nothing in the row, e.g. a size 12 field left it.
* These rows are then filtered out.
* After that, we recalculate the spacers for the rows that need them.
*/ const newLayoutWithSpacers = newLayout.map((row)=>({
...row,
children: row.children.filter((field)=>field.name !== TEMP_FIELD_NAME)
})).filter((row)=>row.children.length > 0).map((row)=>{
const totalSpaceTaken = row.children.reduce((acc, curr)=>acc + curr.size, 0);
if (totalSpaceTaken < 12) {
const [spacerKey] = fractionalIndexing.generateNKeysBetween(row.children.at(-1)?.__temp_key__, undefined, 1);
return {
...row,
children: [
...row.children,
{
name: TEMP_FIELD_NAME,
size: 12 - totalSpaceTaken,
__temp_key__: spacerKey
}
]
};
}
return row;
});
onChange('layout', newLayoutWithSpacers);
};
const handleRemoveField = (rowIndex, fieldIndex)=>()=>{
if (layout[rowIndex].children.length === 1) {
removeFieldRow(`layout`, rowIndex);
} else {
onChange(`layout.${rowIndex}.children`, [
...layout[rowIndex].children.slice(0, fieldIndex),
...layout[rowIndex].children.slice(fieldIndex + 1)
]);
}
};
const handleAddField = (field)=>()=>{
addFieldRow('layout', {
children: [
field
]
});
};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
paddingTop: 6,
direction: "column",
alignItems: "stretch",
gap: 4,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "flex-start",
direction: "column",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontWeight: "bold",
children: formatMessage({
id: translations.getTranslation('containers.list.displayedFields'),
defaultMessage: 'Displayed fields'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'containers.SettingPage.editSettings.description',
defaultMessage: 'Drag & drop the fields to build the layout'
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
padding: 4,
hasRadius: true,
borderStyle: "dashed",
borderWidth: "1px",
borderColor: "neutral300",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
layout.map((row, rowIndex)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 2,
children: row.children.map(({ size, ...field }, fieldIndex)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(Field, {
attribute: attributes[field.name],
components: components,
index: [
rowIndex,
fieldIndex
],
name: `layout.${rowIndex}.children.${fieldIndex}`,
onMoveField: handleMoveField,
onRemoveField: handleRemoveField(rowIndex, fieldIndex)
})
}, field.name))
}, row.__temp_key__)),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Menu.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Menu.Trigger, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icons.Plus, {}),
endIcon: null,
disabled: remainingFields.length === 0,
fullWidth: true,
variant: "secondary",
children: formatMessage({
id: translations.getTranslation('containers.SettingPage.add.field'),
defaultMessage: 'Insert another field'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Menu.Content, {
children: remainingFields.map((field)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Menu.Item, {
onSelect: handleAddField(field),
children: field.label
}, field.name))
})
]
})
]
})
})
]
});
};
/**
* @internal
* @description Small abstraction to solve within an array of fields where you can
* add a field to the beginning or start, move back and forth what it's index range
* should be when calculating it's new temp key
*/ const generateNKeysBetween = (field, count, currInd, newInd)=>{
const startKey = currInd > newInd ? field[newInd - 1]?.__temp_key__ : field[newInd]?.__temp_key__;
const endKey = currInd > newInd ? field[newInd]?.__temp_key__ : field[newInd + 1]?.__temp_key__;
return fractionalIndexing.generateNKeysBetween(startKey, endKey, count);
};
/**
* @internal
* @description chunks a row of layouts by the max size we allow, 12. It does not add the
* spacers again, that should be added separately.
*/ const chunkArray = (array)=>{
const result = [];
let temp = [];
array.reduce((acc, field)=>{
if (acc + field.size > 12) {
result.push(temp);
temp = [
field
];
return field.size;
} else {
temp.push(field);
return acc + field.size;
}
}, 0);
if (temp.length > 0) {
result.push(temp);
}
return result;
};
const TEMP_FIELD_NAME = '_TEMP_';
/**
* Displays a field in the layout with drag options, also
* opens a modal to edit the details of said field.
*/ const Field = ({ attribute, components, name, index, onMoveField, onRemoveField })=>{
const [isModalOpen, setIsModalOpen] = React__namespace.useState(false);
const { formatMessage } = reactIntl.useIntl();
const { value } = strapiAdmin.useField(name);
const [{ isDragging }, objectRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop.useDragAndDrop(true, {
dropSensitivity: 'immediate',
type: dragAndDrop.ItemTypes.EDIT_FIELD,
item: {
index,
label: value?.label,
name
},
index,
onMoveItem: onMoveField
});
React__namespace.useEffect(()=>{
dragPreviewRef(reactDndHtml5Backend.getEmptyImage(), {
captureDraggingState: false
});
}, [
dragPreviewRef
]);
const composedRefs = designSystem.useComposedRefs(dragRef, objectRef);
const handleRemoveField = (e)=>{
e.preventDefault();
e.stopPropagation();
onRemoveField(e);
};
const onEditFieldMeta = (e)=>{
e.preventDefault();
e.stopPropagation();
setIsModalOpen(true);
};
const tempRefs = designSystem.useComposedRefs(dropRef, objectRef);
if (!value) {
return null;
}
if (value.name === TEMP_FIELD_NAME) {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
tag: "span",
height: "100%",
style: {
opacity: 0
},
ref: tempRefs
});
}
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Root, {
open: isModalOpen,
onOpenChange: setIsModalOpen,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
borderColor: "neutral150",
background: "neutral100",
hasRadius: true,
style: {
opacity: isDragging ? 0.5 : 1
},
ref: dropRef,
gap: 3,
cursor: "pointer",
onClick: ()=>{
setIsModalOpen(true);
},
children: [
/*#__PURE__*/ jsxRuntime.jsx(DragButton, {
tag: "span",
withTooltip: false,
label: formatMessage({
id: translations.getTranslation('components.DraggableCard.move.field'),
defaultMessage: 'Move {item}'
}, {
item: value.label
}),
onClick: (e)=>e.stopPropagation(),
ref: composedRefs,
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Drag, {})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
grow: 1,
overflow: "hidden",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 3,
justifyContent: "space-between",
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
ellipsis: true,
fontWeight: "bold",
children: value.label
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
type: "button",
variant: "ghost",
background: "transparent",
onClick: onEditFieldMeta,
withTooltip: false,
label: formatMessage({
id: translations.getTranslation('components.DraggableCard.edit.field'),
defaultMessage: 'Edit {item}'
}, {
item: value.label
}),
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Pencil, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
type: "button",
variant: "ghost",
onClick: handleRemoveField,
background: "transparent",
withTooltip: false,
label: formatMessage({
id: translations.getTranslation('components.DraggableCard.delete.field'),
defaultMessage: 'Delete {item}'
}, {
item: value.label
}),
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Cross, {})
})
]
})
]
}),
attribute?.type === 'component' ? /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
paddingTop: 3,
paddingRight: 3,
paddingBottom: 3,
paddingLeft: 0,
alignItems: "flex-start",
direction: "column",
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
width: "100%",
children: components[attribute.component].layout.map((row)=>row.map(({ size, ...field })=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
alignItems: "center",
background: "neutral0",
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 3,
paddingRight: 3,
hasRadius: true,
borderColor: "neutral200",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
children: field.name
})
})
}, field.name)))
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
// used to stop the edit form from appearing when we click here.
onClick: (e)=>e.stopPropagation(),
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icons.Cog, {}),
tag: reactRouterDom.NavLink,
to: `../components/${attribute.component}/configurations/edit`,
children: formatMessage({
id: translations.getTranslation('components.FieldItem.linkToComponentLayout'),
defaultMessage: "Set the component's layout"
})
})
]
}) : null,
attribute?.type === 'dynamiczone' ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
paddingTop: 3,
paddingRight: 3,
paddingBottom: 3,
paddingLeft: 0,
alignItems: "flex-start",
gap: 2,
width: "100%",
children: attribute?.components.map((uid)=>/*#__PURE__*/ jsxRuntime.jsxs(ComponentLink, {
// used to stop the edit form from appearing when we click here.
onClick: (e)=>e.stopPropagation(),
to: `../components/${uid}/configurations/edit`,
children: [
/*#__PURE__*/ jsxRuntime.jsx(ComponentIcon.ComponentIcon, {
icon: components[uid].settings.icon
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontSize: 1,
textColor: "neutral600",
fontWeight: "bold",
children: components[uid].settings.displayName
})
]
}, uid))
}) : null
]
})
]
}),
value.name !== TEMP_FIELD_NAME && /*#__PURE__*/ jsxRuntime.jsx(EditFieldForm.EditFieldForm, {
attribute: attribute,
name: name,
onClose: ()=>setIsModalOpen(false)
})
]
});
};
const DragButton = styledComponents.styled(designSystem.IconButton)`
height: unset;
align-self: stretch;
display: flex;
align-items: center;
padding: 0;
border: none;
background-color: transparent;
border-radius: 0px;
border-right: 1px solid ${({ theme })=>theme.colors.neutral150};
cursor: all-scroll;
svg {
width: 1.2rem;
height: 1.2rem;
}
`;
const ComponentLink = styledComponents.styled(reactRouterDom.NavLink)`
display: flex;
flex-direction: column;
align-items: center;
gap: ${({ theme })=>theme.spaces[1]};
padding: ${(props)=>props.theme.spaces[2]};
border: 1px solid ${({ theme })=>theme.colors.neutral200};
background: ${({ theme })=>theme.colors.neutral0};
width: 14rem;
border-radius: ${({ theme })=>theme.borderRadius};
text-decoration: none;
&:focus,
&:hover {
${({ theme })=>`
background-color: ${theme.colors.primary100};
border-color: ${theme.colors.primary200};
${designSystem.Typography} {
color: ${theme.colors.primary600};
}
`}
/* > ComponentIcon */
> div:first-child {
background: ${({ theme })=>theme.colors.primary200};
color: ${({ theme })=>theme.colors.primary600};
svg {
path {
fill: ${({ theme })=>theme.colors.primary600};
}
}
}
}
`;
exports.Fields = Fields;
exports.TEMP_FIELD_NAME = TEMP_FIELD_NAME;
//# sourceMappingURL=Fields.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,512 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useForm, useField } from '@strapi/admin/strapi-admin';
import { IconButton, Typography, Flex, Box, Grid, Menu, useComposedRefs, Modal, Link } from '@strapi/design-system';
import { Plus, Drag, Pencil, Cross, Cog } from '@strapi/icons';
import { generateNKeysBetween as generateNKeysBetween$1 } from 'fractional-indexing';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
import { ItemTypes } from '../../constants/dragAndDrop.mjs';
import { useDragAndDrop } from '../../hooks/useDragAndDrop.mjs';
import { getTranslation } from '../../utils/translations.mjs';
import { ComponentIcon } from '../ComponentIcon.mjs';
import { EditFieldForm } from './EditFieldForm.mjs';
const Fields = ({ attributes, fieldSizes, components, metadatas = {} })=>{
const { formatMessage } = useIntl();
const layout = useForm('Fields', (state)=>state.values.layout ?? []);
const onChange = useForm('Fields', (state)=>state.onChange);
const addFieldRow = useForm('Fields', (state)=>state.addFieldRow);
const removeFieldRow = useForm('Fields', (state)=>state.removeFieldRow);
const existingFields = layout.map((row)=>row.children.map((field)=>field.name)).flat();
/**
* Get the fields that are not already in the layout
* But also check that they are visible before we give users
* the option to display them. e.g. `id` is not visible.
*/ const remainingFields = Object.entries(metadatas).reduce((acc, current)=>{
const [name, { visible, ...field }] = current;
if (!existingFields.includes(name) && visible === true) {
const type = attributes[name]?.type;
const size = type ? fieldSizes[type] : 12;
acc.push({
...field,
label: field.label ?? name,
name,
size
});
}
return acc;
}, []);
const handleMoveField = ([newRowIndex, newFieldIndex], [currentRowIndex, currentFieldIndex])=>{
/**
* Because this view has the constraint that the sum of field sizes cannot be greater
* than 12, we don't use the form's method to move field rows, instead, we calculate
* the new layout and set the entire form.
*/ const newLayout = structuredClone(layout);
/**
* Remove field from the current layout space using splice so we have the item
*/ const [field] = newLayout[currentRowIndex].children.splice(currentFieldIndex, 1);
if (!field || field.name === TEMP_FIELD_NAME) {
return;
}
const newRow = newLayout[newRowIndex].children;
const [newFieldKey] = generateNKeysBetween(newRow, 1, currentFieldIndex, newFieldIndex);
/**
* Next we inject the field into it's new row at it's specified index, we then remove the spaces
* if they exist and recalculate into potentially two arrays ONLY if the sizing is now over 12,
* the row and the rest of the row that couldn't fit.
*
* for example, if i have a row of `[{size: 4}, {size: 6}]` and i add `{size: 8}` a index 0,
* the new array will look like `[{size: 8}, {size: 4}, {size: 6}]` which breaks the limit of 12,
* so instead we make two arrays for the new rows `[[{size: 8}, {size: 4}], [{size: 6}]]` which we
* then inject at the original row point with spacers included.
*/ newRow.splice(newFieldIndex, 0, {
...field,
__temp_key__: newFieldKey
});
if (newLayout[newRowIndex].children.reduce((acc, curr)=>acc + curr.size, 0) > 12) {
const recalculatedRows = chunkArray(newLayout[newRowIndex].children.filter((field)=>field.name !== TEMP_FIELD_NAME));
const rowKeys = generateNKeysBetween(newLayout, recalculatedRows.length, currentRowIndex, newRowIndex);
newLayout.splice(newRowIndex, 1, ...recalculatedRows.map((row, index)=>({
__temp_key__: rowKeys[index],
children: row
})));
}
/**
* Now we remove our spacers from the rows so we can understand what dead rows exist:
* - if there's only spacers left
* - there's nothing in the row, e.g. a size 12 field left it.
* These rows are then filtered out.
* After that, we recalculate the spacers for the rows that need them.
*/ const newLayoutWithSpacers = newLayout.map((row)=>({
...row,
children: row.children.filter((field)=>field.name !== TEMP_FIELD_NAME)
})).filter((row)=>row.children.length > 0).map((row)=>{
const totalSpaceTaken = row.children.reduce((acc, curr)=>acc + curr.size, 0);
if (totalSpaceTaken < 12) {
const [spacerKey] = generateNKeysBetween$1(row.children.at(-1)?.__temp_key__, undefined, 1);
return {
...row,
children: [
...row.children,
{
name: TEMP_FIELD_NAME,
size: 12 - totalSpaceTaken,
__temp_key__: spacerKey
}
]
};
}
return row;
});
onChange('layout', newLayoutWithSpacers);
};
const handleRemoveField = (rowIndex, fieldIndex)=>()=>{
if (layout[rowIndex].children.length === 1) {
removeFieldRow(`layout`, rowIndex);
} else {
onChange(`layout.${rowIndex}.children`, [
...layout[rowIndex].children.slice(0, fieldIndex),
...layout[rowIndex].children.slice(fieldIndex + 1)
]);
}
};
const handleAddField = (field)=>()=>{
addFieldRow('layout', {
children: [
field
]
});
};
return /*#__PURE__*/ jsxs(Flex, {
paddingTop: 6,
direction: "column",
alignItems: "stretch",
gap: 4,
children: [
/*#__PURE__*/ jsxs(Flex, {
alignItems: "flex-start",
direction: "column",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsx(Typography, {
fontWeight: "bold",
children: formatMessage({
id: getTranslation('containers.list.displayedFields'),
defaultMessage: 'Displayed fields'
})
}),
/*#__PURE__*/ jsx(Typography, {
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'containers.SettingPage.editSettings.description',
defaultMessage: 'Drag & drop the fields to build the layout'
})
})
]
}),
/*#__PURE__*/ jsx(Box, {
padding: 4,
hasRadius: true,
borderStyle: "dashed",
borderWidth: "1px",
borderColor: "neutral300",
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
layout.map((row, rowIndex)=>/*#__PURE__*/ jsx(Grid.Root, {
gap: 2,
children: row.children.map(({ size, ...field }, fieldIndex)=>/*#__PURE__*/ jsx(Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(Field, {
attribute: attributes[field.name],
components: components,
index: [
rowIndex,
fieldIndex
],
name: `layout.${rowIndex}.children.${fieldIndex}`,
onMoveField: handleMoveField,
onRemoveField: handleRemoveField(rowIndex, fieldIndex)
})
}, field.name))
}, row.__temp_key__)),
/*#__PURE__*/ jsxs(Menu.Root, {
children: [
/*#__PURE__*/ jsx(Menu.Trigger, {
startIcon: /*#__PURE__*/ jsx(Plus, {}),
endIcon: null,
disabled: remainingFields.length === 0,
fullWidth: true,
variant: "secondary",
children: formatMessage({
id: getTranslation('containers.SettingPage.add.field'),
defaultMessage: 'Insert another field'
})
}),
/*#__PURE__*/ jsx(Menu.Content, {
children: remainingFields.map((field)=>/*#__PURE__*/ jsx(Menu.Item, {
onSelect: handleAddField(field),
children: field.label
}, field.name))
})
]
})
]
})
})
]
});
};
/**
* @internal
* @description Small abstraction to solve within an array of fields where you can
* add a field to the beginning or start, move back and forth what it's index range
* should be when calculating it's new temp key
*/ const generateNKeysBetween = (field, count, currInd, newInd)=>{
const startKey = currInd > newInd ? field[newInd - 1]?.__temp_key__ : field[newInd]?.__temp_key__;
const endKey = currInd > newInd ? field[newInd]?.__temp_key__ : field[newInd + 1]?.__temp_key__;
return generateNKeysBetween$1(startKey, endKey, count);
};
/**
* @internal
* @description chunks a row of layouts by the max size we allow, 12. It does not add the
* spacers again, that should be added separately.
*/ const chunkArray = (array)=>{
const result = [];
let temp = [];
array.reduce((acc, field)=>{
if (acc + field.size > 12) {
result.push(temp);
temp = [
field
];
return field.size;
} else {
temp.push(field);
return acc + field.size;
}
}, 0);
if (temp.length > 0) {
result.push(temp);
}
return result;
};
const TEMP_FIELD_NAME = '_TEMP_';
/**
* Displays a field in the layout with drag options, also
* opens a modal to edit the details of said field.
*/ const Field = ({ attribute, components, name, index, onMoveField, onRemoveField })=>{
const [isModalOpen, setIsModalOpen] = React.useState(false);
const { formatMessage } = useIntl();
const { value } = useField(name);
const [{ isDragging }, objectRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop(true, {
dropSensitivity: 'immediate',
type: ItemTypes.EDIT_FIELD,
item: {
index,
label: value?.label,
name
},
index,
onMoveItem: onMoveField
});
React.useEffect(()=>{
dragPreviewRef(getEmptyImage(), {
captureDraggingState: false
});
}, [
dragPreviewRef
]);
const composedRefs = useComposedRefs(dragRef, objectRef);
const handleRemoveField = (e)=>{
e.preventDefault();
e.stopPropagation();
onRemoveField(e);
};
const onEditFieldMeta = (e)=>{
e.preventDefault();
e.stopPropagation();
setIsModalOpen(true);
};
const tempRefs = useComposedRefs(dropRef, objectRef);
if (!value) {
return null;
}
if (value.name === TEMP_FIELD_NAME) {
return /*#__PURE__*/ jsx(Flex, {
tag: "span",
height: "100%",
style: {
opacity: 0
},
ref: tempRefs
});
}
return /*#__PURE__*/ jsxs(Modal.Root, {
open: isModalOpen,
onOpenChange: setIsModalOpen,
children: [
/*#__PURE__*/ jsxs(Flex, {
borderColor: "neutral150",
background: "neutral100",
hasRadius: true,
style: {
opacity: isDragging ? 0.5 : 1
},
ref: dropRef,
gap: 3,
cursor: "pointer",
onClick: ()=>{
setIsModalOpen(true);
},
children: [
/*#__PURE__*/ jsx(DragButton, {
tag: "span",
withTooltip: false,
label: formatMessage({
id: getTranslation('components.DraggableCard.move.field'),
defaultMessage: 'Move {item}'
}, {
item: value.label
}),
onClick: (e)=>e.stopPropagation(),
ref: composedRefs,
children: /*#__PURE__*/ jsx(Drag, {})
}),
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
grow: 1,
overflow: "hidden",
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 3,
justifyContent: "space-between",
width: "100%",
children: [
/*#__PURE__*/ jsx(Typography, {
ellipsis: true,
fontWeight: "bold",
children: value.label
}),
/*#__PURE__*/ jsxs(Flex, {
children: [
/*#__PURE__*/ jsx(IconButton, {
type: "button",
variant: "ghost",
background: "transparent",
onClick: onEditFieldMeta,
withTooltip: false,
label: formatMessage({
id: getTranslation('components.DraggableCard.edit.field'),
defaultMessage: 'Edit {item}'
}, {
item: value.label
}),
children: /*#__PURE__*/ jsx(Pencil, {})
}),
/*#__PURE__*/ jsx(IconButton, {
type: "button",
variant: "ghost",
onClick: handleRemoveField,
background: "transparent",
withTooltip: false,
label: formatMessage({
id: getTranslation('components.DraggableCard.delete.field'),
defaultMessage: 'Delete {item}'
}, {
item: value.label
}),
children: /*#__PURE__*/ jsx(Cross, {})
})
]
})
]
}),
attribute?.type === 'component' ? /*#__PURE__*/ jsxs(Flex, {
paddingTop: 3,
paddingRight: 3,
paddingBottom: 3,
paddingLeft: 0,
alignItems: "flex-start",
direction: "column",
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
width: "100%",
children: components[attribute.component].layout.map((row)=>row.map(({ size, ...field })=>/*#__PURE__*/ jsx(Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(Flex, {
alignItems: "center",
background: "neutral0",
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 3,
paddingRight: 3,
hasRadius: true,
borderColor: "neutral200",
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
children: field.name
})
})
}, field.name)))
}),
/*#__PURE__*/ jsx(Link, {
// used to stop the edit form from appearing when we click here.
onClick: (e)=>e.stopPropagation(),
startIcon: /*#__PURE__*/ jsx(Cog, {}),
tag: NavLink,
to: `../components/${attribute.component}/configurations/edit`,
children: formatMessage({
id: getTranslation('components.FieldItem.linkToComponentLayout'),
defaultMessage: "Set the component's layout"
})
})
]
}) : null,
attribute?.type === 'dynamiczone' ? /*#__PURE__*/ jsx(Flex, {
paddingTop: 3,
paddingRight: 3,
paddingBottom: 3,
paddingLeft: 0,
alignItems: "flex-start",
gap: 2,
width: "100%",
children: attribute?.components.map((uid)=>/*#__PURE__*/ jsxs(ComponentLink, {
// used to stop the edit form from appearing when we click here.
onClick: (e)=>e.stopPropagation(),
to: `../components/${uid}/configurations/edit`,
children: [
/*#__PURE__*/ jsx(ComponentIcon, {
icon: components[uid].settings.icon
}),
/*#__PURE__*/ jsx(Typography, {
fontSize: 1,
textColor: "neutral600",
fontWeight: "bold",
children: components[uid].settings.displayName
})
]
}, uid))
}) : null
]
})
]
}),
value.name !== TEMP_FIELD_NAME && /*#__PURE__*/ jsx(EditFieldForm, {
attribute: attribute,
name: name,
onClose: ()=>setIsModalOpen(false)
})
]
});
};
const DragButton = styled(IconButton)`
height: unset;
align-self: stretch;
display: flex;
align-items: center;
padding: 0;
border: none;
background-color: transparent;
border-radius: 0px;
border-right: 1px solid ${({ theme })=>theme.colors.neutral150};
cursor: all-scroll;
svg {
width: 1.2rem;
height: 1.2rem;
}
`;
const ComponentLink = styled(NavLink)`
display: flex;
flex-direction: column;
align-items: center;
gap: ${({ theme })=>theme.spaces[1]};
padding: ${(props)=>props.theme.spaces[2]};
border: 1px solid ${({ theme })=>theme.colors.neutral200};
background: ${({ theme })=>theme.colors.neutral0};
width: 14rem;
border-radius: ${({ theme })=>theme.borderRadius};
text-decoration: none;
&:focus,
&:hover {
${({ theme })=>`
background-color: ${theme.colors.primary100};
border-color: ${theme.colors.primary200};
${Typography} {
color: ${theme.colors.primary600};
}
`}
/* > ComponentIcon */
> div:first-child {
background: ${({ theme })=>theme.colors.primary200};
color: ${({ theme })=>theme.colors.primary600};
svg {
path {
fill: ${({ theme })=>theme.colors.primary600};
}
}
}
}
`;
export { Fields, TEMP_FIELD_NAME };
//# sourceMappingURL=Fields.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,253 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var fractionalIndexing = require('fractional-indexing');
var pipe = require('lodash/fp/pipe');
var reactIntl = require('react-intl');
var attributes = require('../../constants/attributes.js');
var strings = require('../../utils/strings.js');
var translations = require('../../utils/translations.js');
var Fields = require('./Fields.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const ConfigurationForm = ({ attributes: attributes$1, fieldSizes, layout: editLayout, onSubmit })=>{
const { components, settings, layout, metadatas } = editLayout;
const { formatMessage } = reactIntl.useIntl();
const initialValues = React__namespace.useMemo(()=>{
const transformations = pipe(flattenPanels, replaceMainFieldWithNameOnly, extractMetadata, addTmpSpaceToLayout, addTmpKeysToLayout);
return {
layout: transformations(layout),
settings
};
}, [
layout,
settings
]);
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Root, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Main, {
children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Form, {
initialValues: initialValues,
onSubmit: onSubmit,
method: "PUT",
children: [
/*#__PURE__*/ jsxRuntime.jsx(Header, {
name: settings.displayName ?? ''
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Content, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "stretch",
background: "neutral0",
direction: "column",
gap: 6,
hasRadius: true,
shadow: "tableShadow",
paddingTop: 6,
paddingBottom: 6,
paddingLeft: 7,
paddingRight: 7,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "delta",
tag: "h2",
children: formatMessage({
id: translations.getTranslation('containers.SettingPage.settings'),
defaultMessage: 'Settings'
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Grid.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
type: "enumeration",
label: formatMessage({
id: translations.getTranslation('containers.SettingPage.editSettings.entry.title'),
defaultMessage: 'Entry title'
}),
hint: formatMessage({
id: translations.getTranslation('containers.SettingPage.editSettings.entry.title.description'),
defaultMessage: 'Set the display field of your entry'
}),
name: "settings.mainField",
options: Object.entries(attributes$1).reduce((acc, [key, attribute])=>{
if (!attribute) {
return acc;
}
/**
* Create the list of attributes from the schema as to which can
* be our `mainField` and dictate the display name of the schema
* we're editing.
*/ if (!attributes.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD.includes(attribute.type)) {
acc.push({
label: key,
value: key
});
}
return acc;
}, [])
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
paddingTop: 6,
paddingBottom: 6,
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Divider, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "delta",
tag: "h3",
children: formatMessage({
id: translations.getTranslation('containers.SettingPage.view'),
defaultMessage: 'View'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(Fields.Fields, {
attributes: attributes$1,
components: components,
fieldSizes: fieldSizes,
metadatas: metadatas
})
})
]
})
]
})
})
]
})
})
});
};
/**
* @internal
* @description Panels don't exist in the layout, so we flatten by one.
*/ const flattenPanels = (layout)=>layout.flat(1);
/**
* @internal
* @description We don't need the mainField object in the layout, we only need the name.
*/ const replaceMainFieldWithNameOnly = (layout)=>layout.map((row)=>row.map((field)=>({
...field,
mainField: field.mainField?.name
})));
/**
* @internal
* @description We extract the metadata values from the field layout, because these are editable by the user.
*/ const extractMetadata = (layout)=>{
return layout.map((row)=>row.map(({ label, disabled, hint, placeholder, size, name, mainField })=>({
label,
editable: !disabled,
description: hint,
mainField,
placeholder,
size,
name,
__temp_key__: ''
})));
};
/**
* @internal
* @description Each row of the layout has a max size of 12 (based on bootstrap grid system)
* So in order to offer a better drop zone we add the _TEMP_ div to complete the remaining substract (12 - existing)
*/ const addTmpSpaceToLayout = (layout)=>[
...layout.map((row)=>{
const totalSpaceTaken = row.reduce((acc, field)=>acc + field.size, 0);
if (totalSpaceTaken < 12) {
return [
...row,
{
name: Fields.TEMP_FIELD_NAME,
size: 12 - totalSpaceTaken,
__temp_key__: ''
}
];
}
return row;
})
];
/**
* @internal
* @description At this point of the transformations we have Field[][], but each row for the form should have a __temp_key__
* applied. This means we need to change it so `Field` is nested under the children property.
*/ const addTmpKeysToLayout = (layout)=>{
const keys = fractionalIndexing.generateNKeysBetween(undefined, undefined, layout.length);
return layout.map((row, rowIndex)=>{
const fieldKeys = fractionalIndexing.generateNKeysBetween(undefined, undefined, row.length);
return {
__temp_key__: keys[rowIndex],
children: row.map((field, fieldIndex)=>{
return {
...field,
__temp_key__: fieldKeys[fieldIndex]
};
})
};
});
};
const Header = ({ name })=>{
const { formatMessage } = reactIntl.useIntl();
const modified = strapiAdmin.useForm('Header', (state)=>state.modified);
const isSubmitting = strapiAdmin.useForm('Header', (state)=>state.isSubmitting);
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Header, {
title: formatMessage({
id: translations.getTranslation('components.SettingsViewWrapper.pluginHeader.title'),
defaultMessage: `Configure the view - {name}`
}, {
name: strings.capitalise(name)
}),
subtitle: formatMessage({
id: translations.getTranslation('components.SettingsViewWrapper.pluginHeader.description.edit-settings'),
defaultMessage: 'Customize how the edit view will look like.'
}),
navigationAction: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
primaryAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
disabled: !modified,
loading: isSubmitting,
type: "submit",
children: formatMessage({
id: 'global.save',
defaultMessage: 'Save'
})
})
});
};
exports.ConfigurationForm = ConfigurationForm;
//# sourceMappingURL=Form.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,232 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import * as React from 'react';
import { Layouts, Form, InputRenderer, useForm, BackButton } from '@strapi/admin/strapi-admin';
import { Main, Flex, Typography, Grid, Divider, Button } from '@strapi/design-system';
import { generateNKeysBetween } from 'fractional-indexing';
import pipe from 'lodash/fp/pipe';
import { useIntl } from 'react-intl';
import { ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD } from '../../constants/attributes.mjs';
import { capitalise } from '../../utils/strings.mjs';
import { getTranslation } from '../../utils/translations.mjs';
import { Fields, TEMP_FIELD_NAME } from './Fields.mjs';
const ConfigurationForm = ({ attributes, fieldSizes, layout: editLayout, onSubmit })=>{
const { components, settings, layout, metadatas } = editLayout;
const { formatMessage } = useIntl();
const initialValues = React.useMemo(()=>{
const transformations = pipe(flattenPanels, replaceMainFieldWithNameOnly, extractMetadata, addTmpSpaceToLayout, addTmpKeysToLayout);
return {
layout: transformations(layout),
settings
};
}, [
layout,
settings
]);
return /*#__PURE__*/ jsx(Layouts.Root, {
children: /*#__PURE__*/ jsx(Main, {
children: /*#__PURE__*/ jsxs(Form, {
initialValues: initialValues,
onSubmit: onSubmit,
method: "PUT",
children: [
/*#__PURE__*/ jsx(Header, {
name: settings.displayName ?? ''
}),
/*#__PURE__*/ jsx(Layouts.Content, {
children: /*#__PURE__*/ jsxs(Flex, {
alignItems: "stretch",
background: "neutral0",
direction: "column",
gap: 6,
hasRadius: true,
shadow: "tableShadow",
paddingTop: 6,
paddingBottom: 6,
paddingLeft: 7,
paddingRight: 7,
children: [
/*#__PURE__*/ jsx(Typography, {
variant: "delta",
tag: "h2",
children: formatMessage({
id: getTranslation('containers.SettingPage.settings'),
defaultMessage: 'Settings'
})
}),
/*#__PURE__*/ jsxs(Grid.Root, {
children: [
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(InputRenderer, {
type: "enumeration",
label: formatMessage({
id: getTranslation('containers.SettingPage.editSettings.entry.title'),
defaultMessage: 'Entry title'
}),
hint: formatMessage({
id: getTranslation('containers.SettingPage.editSettings.entry.title.description'),
defaultMessage: 'Set the display field of your entry'
}),
name: "settings.mainField",
options: Object.entries(attributes).reduce((acc, [key, attribute])=>{
if (!attribute) {
return acc;
}
/**
* Create the list of attributes from the schema as to which can
* be our `mainField` and dictate the display name of the schema
* we're editing.
*/ if (!ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD.includes(attribute.type)) {
acc.push({
label: key,
value: key
});
}
return acc;
}, [])
})
}),
/*#__PURE__*/ jsx(Grid.Item, {
paddingTop: 6,
paddingBottom: 6,
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(Divider, {})
}),
/*#__PURE__*/ jsx(Grid.Item, {
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(Typography, {
variant: "delta",
tag: "h3",
children: formatMessage({
id: getTranslation('containers.SettingPage.view'),
defaultMessage: 'View'
})
})
}),
/*#__PURE__*/ jsx(Grid.Item, {
col: 12,
s: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(Fields, {
attributes: attributes,
components: components,
fieldSizes: fieldSizes,
metadatas: metadatas
})
})
]
})
]
})
})
]
})
})
});
};
/**
* @internal
* @description Panels don't exist in the layout, so we flatten by one.
*/ const flattenPanels = (layout)=>layout.flat(1);
/**
* @internal
* @description We don't need the mainField object in the layout, we only need the name.
*/ const replaceMainFieldWithNameOnly = (layout)=>layout.map((row)=>row.map((field)=>({
...field,
mainField: field.mainField?.name
})));
/**
* @internal
* @description We extract the metadata values from the field layout, because these are editable by the user.
*/ const extractMetadata = (layout)=>{
return layout.map((row)=>row.map(({ label, disabled, hint, placeholder, size, name, mainField })=>({
label,
editable: !disabled,
description: hint,
mainField,
placeholder,
size,
name,
__temp_key__: ''
})));
};
/**
* @internal
* @description Each row of the layout has a max size of 12 (based on bootstrap grid system)
* So in order to offer a better drop zone we add the _TEMP_ div to complete the remaining substract (12 - existing)
*/ const addTmpSpaceToLayout = (layout)=>[
...layout.map((row)=>{
const totalSpaceTaken = row.reduce((acc, field)=>acc + field.size, 0);
if (totalSpaceTaken < 12) {
return [
...row,
{
name: TEMP_FIELD_NAME,
size: 12 - totalSpaceTaken,
__temp_key__: ''
}
];
}
return row;
})
];
/**
* @internal
* @description At this point of the transformations we have Field[][], but each row for the form should have a __temp_key__
* applied. This means we need to change it so `Field` is nested under the children property.
*/ const addTmpKeysToLayout = (layout)=>{
const keys = generateNKeysBetween(undefined, undefined, layout.length);
return layout.map((row, rowIndex)=>{
const fieldKeys = generateNKeysBetween(undefined, undefined, row.length);
return {
__temp_key__: keys[rowIndex],
children: row.map((field, fieldIndex)=>{
return {
...field,
__temp_key__: fieldKeys[fieldIndex]
};
})
};
});
};
const Header = ({ name })=>{
const { formatMessage } = useIntl();
const modified = useForm('Header', (state)=>state.modified);
const isSubmitting = useForm('Header', (state)=>state.isSubmitting);
return /*#__PURE__*/ jsx(Layouts.Header, {
title: formatMessage({
id: getTranslation('components.SettingsViewWrapper.pluginHeader.title'),
defaultMessage: `Configure the view - {name}`
}, {
name: capitalise(name)
}),
subtitle: formatMessage({
id: getTranslation('components.SettingsViewWrapper.pluginHeader.description.edit-settings'),
defaultMessage: 'Customize how the edit view will look like.'
}),
navigationAction: /*#__PURE__*/ jsx(BackButton, {}),
primaryAction: /*#__PURE__*/ jsx(Button, {
disabled: !modified,
loading: isSubmitting,
type: "submit",
children: formatMessage({
id: 'global.save',
defaultMessage: 'Save'
})
})
});
};
export { ConfigurationForm };
//# sourceMappingURL=Form.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var designSystem = require('@strapi/design-system');
var reactDnd = require('react-dnd');
function getStyle(initialOffset, currentOffset, mouseOffset) {
if (!initialOffset || !currentOffset || !mouseOffset) {
return {
display: 'none'
};
}
const { x, y } = mouseOffset;
return {
transform: `translate(${x}px, ${y}px)`
};
}
const DragLayer = ({ renderItem })=>{
const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = reactDnd.useDragLayer((monitor)=>({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging(),
mouseOffset: monitor.getClientOffset()
}));
if (!isDragging) {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
height: "100%",
left: 0,
position: "fixed",
pointerEvents: "none",
top: 0,
zIndex: 100,
width: "100%",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
style: getStyle(initialOffset, currentOffset, mouseOffset),
children: renderItem({
type: itemType,
item
})
})
});
};
exports.DragLayer = DragLayer;
//# sourceMappingURL=DragLayer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DragLayer.js","sources":["../../../admin/src/components/DragLayer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Box } from '@strapi/design-system';\nimport { DragLayerMonitor, XYCoord, useDragLayer } from 'react-dnd';\n\nfunction getStyle(\n initialOffset: XYCoord | null,\n currentOffset: XYCoord | null,\n mouseOffset: XYCoord | null\n) {\n if (!initialOffset || !currentOffset || !mouseOffset) {\n return { display: 'none' };\n }\n\n const { x, y } = mouseOffset;\n\n return {\n transform: `translate(${x}px, ${y}px)`,\n };\n}\n\nexport interface DragLayerProps {\n renderItem: (item: {\n /**\n * TODO: it'd be great if we could make this a union where the type infers the item.\n */\n item: any;\n type: ReturnType<DragLayerMonitor['getItemType']>;\n }) => React.ReactNode;\n}\n\nconst DragLayer = ({ renderItem }: DragLayerProps) => {\n const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer(\n (monitor) => ({\n item: monitor.getItem(),\n itemType: monitor.getItemType(),\n initialOffset: monitor.getInitialSourceClientOffset(),\n currentOffset: monitor.getSourceClientOffset(),\n isDragging: monitor.isDragging(),\n mouseOffset: monitor.getClientOffset(),\n })\n );\n\n if (!isDragging) {\n return null;\n }\n\n return (\n <Box\n height=\"100%\"\n left={0}\n position=\"fixed\"\n pointerEvents=\"none\"\n top={0}\n zIndex={100}\n width=\"100%\"\n >\n <Box style={getStyle(initialOffset, currentOffset, mouseOffset)}>\n {renderItem({ type: itemType, item })}\n </Box>\n </Box>\n );\n};\n\nexport { DragLayer };\n"],"names":["getStyle","initialOffset","currentOffset","mouseOffset","display","x","y","transform","DragLayer","renderItem","itemType","isDragging","item","useDragLayer","monitor","getItem","getItemType","getInitialSourceClientOffset","getSourceClientOffset","getClientOffset","_jsx","Box","height","left","position","pointerEvents","top","zIndex","width","style","type"],"mappings":";;;;;;;AAKA,SAASA,QACPC,CAAAA,aAA6B,EAC7BC,aAA6B,EAC7BC,WAA2B,EAAA;AAE3B,IAAA,IAAI,CAACF,aAAAA,IAAiB,CAACC,aAAAA,IAAiB,CAACC,WAAa,EAAA;QACpD,OAAO;YAAEC,OAAS,EAAA;AAAO,SAAA;AAC3B;AAEA,IAAA,MAAM,EAAEC,CAAC,EAAEC,CAAC,EAAE,GAAGH,WAAAA;IAEjB,OAAO;QACLI,SAAW,EAAA,CAAC,UAAU,EAAEF,CAAAA,CAAE,IAAI,EAAEC,CAAAA,CAAE,GAAG;AACvC,KAAA;AACF;AAYA,MAAME,SAAY,GAAA,CAAC,EAAEC,UAAU,EAAkB,GAAA;AAC/C,IAAA,MAAM,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,IAAI,EAAEX,aAAa,EAAEC,aAAa,EAAEC,WAAW,EAAE,GAAGU,qBAChF,CAAA,CAACC,WAAa;AACZF,YAAAA,IAAAA,EAAME,QAAQC,OAAO,EAAA;AACrBL,YAAAA,QAAAA,EAAUI,QAAQE,WAAW,EAAA;AAC7Bf,YAAAA,aAAAA,EAAea,QAAQG,4BAA4B,EAAA;AACnDf,YAAAA,aAAAA,EAAeY,QAAQI,qBAAqB,EAAA;AAC5CP,YAAAA,UAAAA,EAAYG,QAAQH,UAAU,EAAA;AAC9BR,YAAAA,WAAAA,EAAaW,QAAQK,eAAe;SACtC,CAAA,CAAA;AAGF,IAAA,IAAI,CAACR,UAAY,EAAA;QACf,OAAO,IAAA;AACT;AAEA,IAAA,qBACES,cAACC,CAAAA,gBAAAA,EAAAA;QACCC,MAAO,EAAA,MAAA;QACPC,IAAM,EAAA,CAAA;QACNC,QAAS,EAAA,OAAA;QACTC,aAAc,EAAA,MAAA;QACdC,GAAK,EAAA,CAAA;QACLC,MAAQ,EAAA,GAAA;QACRC,KAAM,EAAA,MAAA;AAEN,QAAA,QAAA,gBAAAR,cAACC,CAAAA,gBAAAA,EAAAA;YAAIQ,KAAO7B,EAAAA,QAAAA,CAASC,eAAeC,aAAeC,EAAAA,WAAAA,CAAAA;sBAChDM,UAAW,CAAA;gBAAEqB,IAAMpB,EAAAA,QAAAA;AAAUE,gBAAAA;AAAK,aAAA;;;AAI3C;;;;"}

View File

@@ -0,0 +1,48 @@
import { jsx } from 'react/jsx-runtime';
import 'react';
import { Box } from '@strapi/design-system';
import { useDragLayer } from 'react-dnd';
function getStyle(initialOffset, currentOffset, mouseOffset) {
if (!initialOffset || !currentOffset || !mouseOffset) {
return {
display: 'none'
};
}
const { x, y } = mouseOffset;
return {
transform: `translate(${x}px, ${y}px)`
};
}
const DragLayer = ({ renderItem })=>{
const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer((monitor)=>({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging(),
mouseOffset: monitor.getClientOffset()
}));
if (!isDragging) {
return null;
}
return /*#__PURE__*/ jsx(Box, {
height: "100%",
left: 0,
position: "fixed",
pointerEvents: "none",
top: 0,
zIndex: 100,
width: "100%",
children: /*#__PURE__*/ jsx(Box, {
style: getStyle(initialOffset, currentOffset, mouseOffset),
children: renderItem({
type: itemType,
item
})
})
});
};
export { DragLayer };
//# sourceMappingURL=DragLayer.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DragLayer.mjs","sources":["../../../admin/src/components/DragLayer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Box } from '@strapi/design-system';\nimport { DragLayerMonitor, XYCoord, useDragLayer } from 'react-dnd';\n\nfunction getStyle(\n initialOffset: XYCoord | null,\n currentOffset: XYCoord | null,\n mouseOffset: XYCoord | null\n) {\n if (!initialOffset || !currentOffset || !mouseOffset) {\n return { display: 'none' };\n }\n\n const { x, y } = mouseOffset;\n\n return {\n transform: `translate(${x}px, ${y}px)`,\n };\n}\n\nexport interface DragLayerProps {\n renderItem: (item: {\n /**\n * TODO: it'd be great if we could make this a union where the type infers the item.\n */\n item: any;\n type: ReturnType<DragLayerMonitor['getItemType']>;\n }) => React.ReactNode;\n}\n\nconst DragLayer = ({ renderItem }: DragLayerProps) => {\n const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer(\n (monitor) => ({\n item: monitor.getItem(),\n itemType: monitor.getItemType(),\n initialOffset: monitor.getInitialSourceClientOffset(),\n currentOffset: monitor.getSourceClientOffset(),\n isDragging: monitor.isDragging(),\n mouseOffset: monitor.getClientOffset(),\n })\n );\n\n if (!isDragging) {\n return null;\n }\n\n return (\n <Box\n height=\"100%\"\n left={0}\n position=\"fixed\"\n pointerEvents=\"none\"\n top={0}\n zIndex={100}\n width=\"100%\"\n >\n <Box style={getStyle(initialOffset, currentOffset, mouseOffset)}>\n {renderItem({ type: itemType, item })}\n </Box>\n </Box>\n );\n};\n\nexport { DragLayer };\n"],"names":["getStyle","initialOffset","currentOffset","mouseOffset","display","x","y","transform","DragLayer","renderItem","itemType","isDragging","item","useDragLayer","monitor","getItem","getItemType","getInitialSourceClientOffset","getSourceClientOffset","getClientOffset","_jsx","Box","height","left","position","pointerEvents","top","zIndex","width","style","type"],"mappings":";;;;;AAKA,SAASA,QACPC,CAAAA,aAA6B,EAC7BC,aAA6B,EAC7BC,WAA2B,EAAA;AAE3B,IAAA,IAAI,CAACF,aAAAA,IAAiB,CAACC,aAAAA,IAAiB,CAACC,WAAa,EAAA;QACpD,OAAO;YAAEC,OAAS,EAAA;AAAO,SAAA;AAC3B;AAEA,IAAA,MAAM,EAAEC,CAAC,EAAEC,CAAC,EAAE,GAAGH,WAAAA;IAEjB,OAAO;QACLI,SAAW,EAAA,CAAC,UAAU,EAAEF,CAAAA,CAAE,IAAI,EAAEC,CAAAA,CAAE,GAAG;AACvC,KAAA;AACF;AAYA,MAAME,SAAY,GAAA,CAAC,EAAEC,UAAU,EAAkB,GAAA;AAC/C,IAAA,MAAM,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,IAAI,EAAEX,aAAa,EAAEC,aAAa,EAAEC,WAAW,EAAE,GAAGU,YAChF,CAAA,CAACC,WAAa;AACZF,YAAAA,IAAAA,EAAME,QAAQC,OAAO,EAAA;AACrBL,YAAAA,QAAAA,EAAUI,QAAQE,WAAW,EAAA;AAC7Bf,YAAAA,aAAAA,EAAea,QAAQG,4BAA4B,EAAA;AACnDf,YAAAA,aAAAA,EAAeY,QAAQI,qBAAqB,EAAA;AAC5CP,YAAAA,UAAAA,EAAYG,QAAQH,UAAU,EAAA;AAC9BR,YAAAA,WAAAA,EAAaW,QAAQK,eAAe;SACtC,CAAA,CAAA;AAGF,IAAA,IAAI,CAACR,UAAY,EAAA;QACf,OAAO,IAAA;AACT;AAEA,IAAA,qBACES,GAACC,CAAAA,GAAAA,EAAAA;QACCC,MAAO,EAAA,MAAA;QACPC,IAAM,EAAA,CAAA;QACNC,QAAS,EAAA,OAAA;QACTC,aAAc,EAAA,MAAA;QACdC,GAAK,EAAA,CAAA;QACLC,MAAQ,EAAA,GAAA;QACRC,KAAM,EAAA,MAAA;AAEN,QAAA,QAAA,gBAAAR,GAACC,CAAAA,GAAAA,EAAAA;YAAIQ,KAAO7B,EAAAA,QAAAA,CAASC,eAAeC,aAAeC,EAAAA,WAAAA,CAAAA;sBAChDM,UAAW,CAAA;gBAAEqB,IAAMpB,EAAAA,QAAAA;AAAUE,gBAAAA;AAAK,aAAA;;;AAI3C;;;;"}

View File

@@ -0,0 +1,82 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var styledComponents = require('styled-components');
const CardDragPreview = ({ label, isSibling = false })=>{
return /*#__PURE__*/ jsxRuntime.jsxs(FieldContainer, {
background: isSibling ? 'neutral100' : 'primary100',
display: "inline-flex",
gap: 3,
hasRadius: true,
justifyContent: "space-between",
$isSibling: isSibling,
"max-height": `3.2rem`,
maxWidth: "min-content",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 3,
children: [
/*#__PURE__*/ jsxRuntime.jsx(DragButton, {
alignItems: "center",
cursor: "all-scroll",
padding: 3,
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Drag, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: isSibling ? undefined : 'primary600',
fontWeight: "bold",
ellipsis: true,
maxWidth: "7.2rem",
children: label
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(ActionBox, {
alignItems: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Pencil, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(ActionBox, {
alignItems: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Cross, {})
})
]
})
]
});
};
const ActionBox = styledComponents.styled(designSystem.Flex)`
height: ${({ theme })=>theme.spaces[7]};
&:last-child {
padding: 0 ${({ theme })=>theme.spaces[3]};
}
`;
const DragButton = styledComponents.styled(ActionBox)`
border-right: 1px solid ${({ theme })=>theme.colors.primary200};
svg {
width: 1.2rem;
height: 1.2rem;
}
`;
const FieldContainer = styledComponents.styled(designSystem.Flex)`
border: 1px solid
${({ theme, $isSibling })=>$isSibling ? theme.colors.neutral150 : theme.colors.primary200};
svg {
width: 1rem;
height: 1rem;
path {
fill: ${({ theme, $isSibling })=>$isSibling ? undefined : theme.colors.primary600};
}
}
`;
exports.CardDragPreview = CardDragPreview;
//# sourceMappingURL=CardDragPreview.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CardDragPreview.js","sources":["../../../../admin/src/components/DragPreviews/CardDragPreview.tsx"],"sourcesContent":["import { Flex, FlexComponent, Typography } from '@strapi/design-system';\nimport { Cross, Drag, Pencil } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\ninterface CardDragPreviewProps {\n label: string;\n isSibling?: boolean;\n}\n\nconst CardDragPreview = ({ label, isSibling = false }: CardDragPreviewProps) => {\n return (\n <FieldContainer\n background={isSibling ? 'neutral100' : 'primary100'}\n display=\"inline-flex\"\n gap={3}\n hasRadius\n justifyContent=\"space-between\"\n $isSibling={isSibling}\n max-height={`3.2rem`}\n maxWidth=\"min-content\"\n >\n <Flex gap={3}>\n <DragButton alignItems=\"center\" cursor=\"all-scroll\" padding={3}>\n <Drag />\n </DragButton>\n\n <Typography\n textColor={isSibling ? undefined : 'primary600'}\n fontWeight=\"bold\"\n ellipsis\n maxWidth=\"7.2rem\"\n >\n {label}\n </Typography>\n </Flex>\n\n <Flex>\n <ActionBox alignItems=\"center\">\n <Pencil />\n </ActionBox>\n\n <ActionBox alignItems=\"center\">\n <Cross />\n </ActionBox>\n </Flex>\n </FieldContainer>\n );\n};\n\nconst ActionBox = styled<FlexComponent>(Flex)`\n height: ${({ theme }) => theme.spaces[7]};\n\n &:last-child {\n padding: 0 ${({ theme }) => theme.spaces[3]};\n }\n`;\n\nconst DragButton = styled(ActionBox)`\n border-right: 1px solid ${({ theme }) => theme.colors.primary200};\n\n svg {\n width: 1.2rem;\n height: 1.2rem;\n }\n`;\n\nconst FieldContainer = styled<FlexComponent>(Flex)<{ $isSibling: boolean }>`\n border: 1px solid\n ${({ theme, $isSibling }) => ($isSibling ? theme.colors.neutral150 : theme.colors.primary200)};\n\n svg {\n width: 1rem;\n height: 1rem;\n\n path {\n fill: ${({ theme, $isSibling }) => ($isSibling ? undefined : theme.colors.primary600)};\n }\n }\n`;\n\nexport { CardDragPreview };\nexport type { CardDragPreviewProps };\n"],"names":["CardDragPreview","label","isSibling","_jsxs","FieldContainer","background","display","gap","hasRadius","justifyContent","$isSibling","max-height","maxWidth","Flex","_jsx","DragButton","alignItems","cursor","padding","Drag","Typography","textColor","undefined","fontWeight","ellipsis","ActionBox","Pencil","Cross","styled","theme","spaces","colors","primary200","neutral150","primary600"],"mappings":";;;;;;;AASA,MAAMA,kBAAkB,CAAC,EAAEC,KAAK,EAAEC,SAAAA,GAAY,KAAK,EAAwB,GAAA;AACzE,IAAA,qBACEC,eAACC,CAAAA,cAAAA,EAAAA;AACCC,QAAAA,UAAAA,EAAYH,YAAY,YAAe,GAAA,YAAA;QACvCI,OAAQ,EAAA,aAAA;QACRC,GAAK,EAAA,CAAA;QACLC,SAAS,EAAA,IAAA;QACTC,cAAe,EAAA,eAAA;QACfC,UAAYR,EAAAA,SAAAA;QACZS,YAAY,EAAA,CAAC,MAAM,CAAC;QACpBC,QAAS,EAAA,aAAA;;0BAETT,eAACU,CAAAA,iBAAAA,EAAAA;gBAAKN,GAAK,EAAA,CAAA;;kCACTO,cAACC,CAAAA,UAAAA,EAAAA;wBAAWC,UAAW,EAAA,QAAA;wBAASC,MAAO,EAAA,YAAA;wBAAaC,OAAS,EAAA,CAAA;AAC3D,wBAAA,QAAA,gBAAAJ,cAACK,CAAAA,UAAAA,EAAAA,EAAAA;;kCAGHL,cAACM,CAAAA,uBAAAA,EAAAA;AACCC,wBAAAA,SAAAA,EAAWnB,YAAYoB,SAAY,GAAA,YAAA;wBACnCC,UAAW,EAAA,MAAA;wBACXC,QAAQ,EAAA,IAAA;wBACRZ,QAAS,EAAA,QAAA;AAERX,wBAAAA,QAAAA,EAAAA;;;;0BAILE,eAACU,CAAAA,iBAAAA,EAAAA;;kCACCC,cAACW,CAAAA,SAAAA,EAAAA;wBAAUT,UAAW,EAAA,QAAA;AACpB,wBAAA,QAAA,gBAAAF,cAACY,CAAAA,YAAAA,EAAAA,EAAAA;;kCAGHZ,cAACW,CAAAA,SAAAA,EAAAA;wBAAUT,UAAW,EAAA,QAAA;AACpB,wBAAA,QAAA,gBAAAF,cAACa,CAAAA,WAAAA,EAAAA,EAAAA;;;;;;AAKX;AAEA,MAAMF,SAAAA,GAAYG,uBAAsBf,CAAAA,iBAAAA,CAAK;UACnC,EAAE,CAAC,EAAEgB,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAAC,CAAA,CAAE,CAAC;;;eAG5B,EAAE,CAAC,EAAED,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAAC,CAAA,CAAE,CAAC;;AAEhD,CAAC;AAED,MAAMf,UAAAA,GAAaa,uBAAOH,CAAAA,SAAAA,CAAU;0BACV,EAAE,CAAC,EAAEI,KAAK,EAAE,GAAKA,KAAME,CAAAA,MAAM,CAACC,UAAU,CAAC;;;;;;AAMnE,CAAC;AAED,MAAM5B,cAAAA,GAAiBwB,uBAAsBf,CAAAA,iBAAAA,CAA8B;;AAEvE,IAAA,EAAE,CAAC,EAAEgB,KAAK,EAAEnB,UAAU,EAAE,GAAMA,UAAAA,GAAamB,KAAME,CAAAA,MAAM,CAACE,UAAU,GAAGJ,MAAME,MAAM,CAACC,UAAU,CAAE;;;;;;;AAOtF,YAAA,EAAE,CAAC,EAAEH,KAAK,EAAEnB,UAAU,EAAE,GAAMA,UAAAA,GAAaY,SAAYO,GAAAA,KAAAA,CAAME,MAAM,CAACG,UAAU,CAAE;;;AAG5F,CAAC;;;;"}

View File

@@ -0,0 +1,80 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { Flex, Typography } from '@strapi/design-system';
import { Drag, Pencil, Cross } from '@strapi/icons';
import { styled } from 'styled-components';
const CardDragPreview = ({ label, isSibling = false })=>{
return /*#__PURE__*/ jsxs(FieldContainer, {
background: isSibling ? 'neutral100' : 'primary100',
display: "inline-flex",
gap: 3,
hasRadius: true,
justifyContent: "space-between",
$isSibling: isSibling,
"max-height": `3.2rem`,
maxWidth: "min-content",
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 3,
children: [
/*#__PURE__*/ jsx(DragButton, {
alignItems: "center",
cursor: "all-scroll",
padding: 3,
children: /*#__PURE__*/ jsx(Drag, {})
}),
/*#__PURE__*/ jsx(Typography, {
textColor: isSibling ? undefined : 'primary600',
fontWeight: "bold",
ellipsis: true,
maxWidth: "7.2rem",
children: label
})
]
}),
/*#__PURE__*/ jsxs(Flex, {
children: [
/*#__PURE__*/ jsx(ActionBox, {
alignItems: "center",
children: /*#__PURE__*/ jsx(Pencil, {})
}),
/*#__PURE__*/ jsx(ActionBox, {
alignItems: "center",
children: /*#__PURE__*/ jsx(Cross, {})
})
]
})
]
});
};
const ActionBox = styled(Flex)`
height: ${({ theme })=>theme.spaces[7]};
&:last-child {
padding: 0 ${({ theme })=>theme.spaces[3]};
}
`;
const DragButton = styled(ActionBox)`
border-right: 1px solid ${({ theme })=>theme.colors.primary200};
svg {
width: 1.2rem;
height: 1.2rem;
}
`;
const FieldContainer = styled(Flex)`
border: 1px solid
${({ theme, $isSibling })=>$isSibling ? theme.colors.neutral150 : theme.colors.primary200};
svg {
width: 1rem;
height: 1rem;
path {
fill: ${({ theme, $isSibling })=>$isSibling ? undefined : theme.colors.primary600};
}
}
`;
export { CardDragPreview };
//# sourceMappingURL=CardDragPreview.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CardDragPreview.mjs","sources":["../../../../admin/src/components/DragPreviews/CardDragPreview.tsx"],"sourcesContent":["import { Flex, FlexComponent, Typography } from '@strapi/design-system';\nimport { Cross, Drag, Pencil } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\ninterface CardDragPreviewProps {\n label: string;\n isSibling?: boolean;\n}\n\nconst CardDragPreview = ({ label, isSibling = false }: CardDragPreviewProps) => {\n return (\n <FieldContainer\n background={isSibling ? 'neutral100' : 'primary100'}\n display=\"inline-flex\"\n gap={3}\n hasRadius\n justifyContent=\"space-between\"\n $isSibling={isSibling}\n max-height={`3.2rem`}\n maxWidth=\"min-content\"\n >\n <Flex gap={3}>\n <DragButton alignItems=\"center\" cursor=\"all-scroll\" padding={3}>\n <Drag />\n </DragButton>\n\n <Typography\n textColor={isSibling ? undefined : 'primary600'}\n fontWeight=\"bold\"\n ellipsis\n maxWidth=\"7.2rem\"\n >\n {label}\n </Typography>\n </Flex>\n\n <Flex>\n <ActionBox alignItems=\"center\">\n <Pencil />\n </ActionBox>\n\n <ActionBox alignItems=\"center\">\n <Cross />\n </ActionBox>\n </Flex>\n </FieldContainer>\n );\n};\n\nconst ActionBox = styled<FlexComponent>(Flex)`\n height: ${({ theme }) => theme.spaces[7]};\n\n &:last-child {\n padding: 0 ${({ theme }) => theme.spaces[3]};\n }\n`;\n\nconst DragButton = styled(ActionBox)`\n border-right: 1px solid ${({ theme }) => theme.colors.primary200};\n\n svg {\n width: 1.2rem;\n height: 1.2rem;\n }\n`;\n\nconst FieldContainer = styled<FlexComponent>(Flex)<{ $isSibling: boolean }>`\n border: 1px solid\n ${({ theme, $isSibling }) => ($isSibling ? theme.colors.neutral150 : theme.colors.primary200)};\n\n svg {\n width: 1rem;\n height: 1rem;\n\n path {\n fill: ${({ theme, $isSibling }) => ($isSibling ? undefined : theme.colors.primary600)};\n }\n }\n`;\n\nexport { CardDragPreview };\nexport type { CardDragPreviewProps };\n"],"names":["CardDragPreview","label","isSibling","_jsxs","FieldContainer","background","display","gap","hasRadius","justifyContent","$isSibling","max-height","maxWidth","Flex","_jsx","DragButton","alignItems","cursor","padding","Drag","Typography","textColor","undefined","fontWeight","ellipsis","ActionBox","Pencil","Cross","styled","theme","spaces","colors","primary200","neutral150","primary600"],"mappings":";;;;;AASA,MAAMA,kBAAkB,CAAC,EAAEC,KAAK,EAAEC,SAAAA,GAAY,KAAK,EAAwB,GAAA;AACzE,IAAA,qBACEC,IAACC,CAAAA,cAAAA,EAAAA;AACCC,QAAAA,UAAAA,EAAYH,YAAY,YAAe,GAAA,YAAA;QACvCI,OAAQ,EAAA,aAAA;QACRC,GAAK,EAAA,CAAA;QACLC,SAAS,EAAA,IAAA;QACTC,cAAe,EAAA,eAAA;QACfC,UAAYR,EAAAA,SAAAA;QACZS,YAAY,EAAA,CAAC,MAAM,CAAC;QACpBC,QAAS,EAAA,aAAA;;0BAETT,IAACU,CAAAA,IAAAA,EAAAA;gBAAKN,GAAK,EAAA,CAAA;;kCACTO,GAACC,CAAAA,UAAAA,EAAAA;wBAAWC,UAAW,EAAA,QAAA;wBAASC,MAAO,EAAA,YAAA;wBAAaC,OAAS,EAAA,CAAA;AAC3D,wBAAA,QAAA,gBAAAJ,GAACK,CAAAA,IAAAA,EAAAA,EAAAA;;kCAGHL,GAACM,CAAAA,UAAAA,EAAAA;AACCC,wBAAAA,SAAAA,EAAWnB,YAAYoB,SAAY,GAAA,YAAA;wBACnCC,UAAW,EAAA,MAAA;wBACXC,QAAQ,EAAA,IAAA;wBACRZ,QAAS,EAAA,QAAA;AAERX,wBAAAA,QAAAA,EAAAA;;;;0BAILE,IAACU,CAAAA,IAAAA,EAAAA;;kCACCC,GAACW,CAAAA,SAAAA,EAAAA;wBAAUT,UAAW,EAAA,QAAA;AACpB,wBAAA,QAAA,gBAAAF,GAACY,CAAAA,MAAAA,EAAAA,EAAAA;;kCAGHZ,GAACW,CAAAA,SAAAA,EAAAA;wBAAUT,UAAW,EAAA,QAAA;AACpB,wBAAA,QAAA,gBAAAF,GAACa,CAAAA,KAAAA,EAAAA,EAAAA;;;;;;AAKX;AAEA,MAAMF,SAAAA,GAAYG,MAAsBf,CAAAA,IAAAA,CAAK;UACnC,EAAE,CAAC,EAAEgB,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAAC,CAAA,CAAE,CAAC;;;eAG5B,EAAE,CAAC,EAAED,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAAC,CAAA,CAAE,CAAC;;AAEhD,CAAC;AAED,MAAMf,UAAAA,GAAaa,MAAOH,CAAAA,SAAAA,CAAU;0BACV,EAAE,CAAC,EAAEI,KAAK,EAAE,GAAKA,KAAME,CAAAA,MAAM,CAACC,UAAU,CAAC;;;;;;AAMnE,CAAC;AAED,MAAM5B,cAAAA,GAAiBwB,MAAsBf,CAAAA,IAAAA,CAA8B;;AAEvE,IAAA,EAAE,CAAC,EAAEgB,KAAK,EAAEnB,UAAU,EAAE,GAAMA,UAAAA,GAAamB,KAAME,CAAAA,MAAM,CAACE,UAAU,GAAGJ,MAAME,MAAM,CAACC,UAAU,CAAE;;;;;;;AAOtF,YAAA,EAAE,CAAC,EAAEH,KAAK,EAAEnB,UAAU,EAAE,GAAMA,UAAAA,GAAaY,SAAYO,GAAAA,KAAAA,CAAME,MAAM,CAACG,UAAU,CAAE;;;AAG5F,CAAC;;;;"}

View File

@@ -0,0 +1,83 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var styledComponents = require('styled-components');
const ComponentDragPreview = ({ displayedValue })=>{
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
background: "neutral0",
borderColor: "neutral200",
justifyContent: "space-between",
gap: 3,
padding: 3,
width: "30rem",
children: [
/*#__PURE__*/ jsxRuntime.jsx(ToggleButton, {
type: "button",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 6,
children: [
/*#__PURE__*/ jsxRuntime.jsx(DropdownIconWrapper, {
alignItems: "center",
justifyContent: "center",
background: "neutral200",
height: "3.2rem",
width: "3.2rem",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.CaretDown, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
maxWidth: "15rem",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral700",
ellipsis: true,
children: displayedValue
})
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Trash, {})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Drag, {})
})
]
})
]
});
};
const DropdownIconWrapper = styledComponents.styled(designSystem.Flex)`
border-radius: 50%;
svg {
height: 0.6rem;
width: 1.1rem;
> path {
fill: ${({ theme })=>theme.colors.neutral600};
}
}
`;
// TODO: we shouldn't have to reset a whole button
const ToggleButton = styledComponents.styled.button`
border: none;
background: transparent;
display: block;
width: 100%;
text-align: unset;
padding: 0;
`;
exports.ComponentDragPreview = ComponentDragPreview;
//# sourceMappingURL=ComponentDragPreview.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ComponentDragPreview.js","sources":["../../../../admin/src/components/DragPreviews/ComponentDragPreview.tsx"],"sourcesContent":["import { Flex, FlexComponent, IconButton, Typography } from '@strapi/design-system';\nimport { CaretDown, Drag, Trash } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\ninterface ComponentDragPreviewProps {\n displayedValue: string;\n}\n\nconst ComponentDragPreview = ({ displayedValue }: ComponentDragPreviewProps) => {\n return (\n <Flex\n background=\"neutral0\"\n borderColor=\"neutral200\"\n justifyContent=\"space-between\"\n gap={3}\n padding={3}\n width=\"30rem\"\n >\n <ToggleButton type=\"button\">\n <Flex gap={6}>\n <DropdownIconWrapper\n alignItems=\"center\"\n justifyContent=\"center\"\n background=\"neutral200\"\n height=\"3.2rem\"\n width=\"3.2rem\"\n >\n <CaretDown />\n </DropdownIconWrapper>\n\n <Flex maxWidth=\"15rem\">\n <Typography textColor=\"neutral700\" ellipsis>\n {displayedValue}\n </Typography>\n </Flex>\n </Flex>\n </ToggleButton>\n\n <Flex gap={2}>\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Trash />\n </IconButton>\n\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Drag />\n </IconButton>\n </Flex>\n </Flex>\n );\n};\n\nconst DropdownIconWrapper = styled<FlexComponent>(Flex)`\n border-radius: 50%;\n\n svg {\n height: 0.6rem;\n width: 1.1rem;\n > path {\n fill: ${({ theme }) => theme.colors.neutral600};\n }\n }\n`;\n\n// TODO: we shouldn't have to reset a whole button\nconst ToggleButton = styled.button`\n border: none;\n background: transparent;\n display: block;\n width: 100%;\n text-align: unset;\n padding: 0;\n`;\n\nexport { ComponentDragPreview };\nexport type { ComponentDragPreviewProps };\n"],"names":["ComponentDragPreview","displayedValue","_jsxs","Flex","background","borderColor","justifyContent","gap","padding","width","_jsx","ToggleButton","type","DropdownIconWrapper","alignItems","height","CaretDown","maxWidth","Typography","textColor","ellipsis","IconButton","withTooltip","label","variant","Trash","Drag","styled","theme","colors","neutral600","button"],"mappings":";;;;;;;AAQA,MAAMA,oBAAuB,GAAA,CAAC,EAAEC,cAAc,EAA6B,GAAA;AACzE,IAAA,qBACEC,eAACC,CAAAA,iBAAAA,EAAAA;QACCC,UAAW,EAAA,UAAA;QACXC,WAAY,EAAA,YAAA;QACZC,cAAe,EAAA,eAAA;QACfC,GAAK,EAAA,CAAA;QACLC,OAAS,EAAA,CAAA;QACTC,KAAM,EAAA,OAAA;;0BAENC,cAACC,CAAAA,YAAAA,EAAAA;gBAAaC,IAAK,EAAA,QAAA;AACjB,gBAAA,QAAA,gBAAAV,eAACC,CAAAA,iBAAAA,EAAAA;oBAAKI,GAAK,EAAA,CAAA;;sCACTG,cAACG,CAAAA,mBAAAA,EAAAA;4BACCC,UAAW,EAAA,QAAA;4BACXR,cAAe,EAAA,QAAA;4BACfF,UAAW,EAAA,YAAA;4BACXW,MAAO,EAAA,QAAA;4BACPN,KAAM,EAAA,QAAA;AAEN,4BAAA,QAAA,gBAAAC,cAACM,CAAAA,eAAAA,EAAAA,EAAAA;;sCAGHN,cAACP,CAAAA,iBAAAA,EAAAA;4BAAKc,QAAS,EAAA,OAAA;AACb,4BAAA,QAAA,gBAAAP,cAACQ,CAAAA,uBAAAA,EAAAA;gCAAWC,SAAU,EAAA,YAAA;gCAAaC,QAAQ,EAAA,IAAA;AACxCnB,gCAAAA,QAAAA,EAAAA;;;;;;0BAMTC,eAACC,CAAAA,iBAAAA,EAAAA;gBAAKI,GAAK,EAAA,CAAA;;kCACTG,cAACW,CAAAA,uBAAAA,EAAAA;wBAAWC,WAAa,EAAA,KAAA;wBAAOC,KAAM,EAAA,EAAA;wBAAGC,OAAQ,EAAA,OAAA;AAC/C,wBAAA,QAAA,gBAAAd,cAACe,CAAAA,WAAAA,EAAAA,EAAAA;;kCAGHf,cAACW,CAAAA,uBAAAA,EAAAA;wBAAWC,WAAa,EAAA,KAAA;wBAAOC,KAAM,EAAA,EAAA;wBAAGC,OAAQ,EAAA,OAAA;AAC/C,wBAAA,QAAA,gBAAAd,cAACgB,CAAAA,UAAAA,EAAAA,EAAAA;;;;;;AAKX;AAEA,MAAMb,mBAAAA,GAAsBc,uBAAsBxB,CAAAA,iBAAAA,CAAK;;;;;;;YAO3C,EAAE,CAAC,EAAEyB,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACC,UAAU,CAAC;;;AAGrD,CAAC;AAED;AACA,MAAMnB,YAAAA,GAAegB,uBAAOI,CAAAA,MAAM;;;;;;;AAOlC,CAAC;;;;"}

View File

@@ -0,0 +1,81 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { Flex, Typography, IconButton } from '@strapi/design-system';
import { CaretDown, Trash, Drag } from '@strapi/icons';
import { styled } from 'styled-components';
const ComponentDragPreview = ({ displayedValue })=>{
return /*#__PURE__*/ jsxs(Flex, {
background: "neutral0",
borderColor: "neutral200",
justifyContent: "space-between",
gap: 3,
padding: 3,
width: "30rem",
children: [
/*#__PURE__*/ jsx(ToggleButton, {
type: "button",
children: /*#__PURE__*/ jsxs(Flex, {
gap: 6,
children: [
/*#__PURE__*/ jsx(DropdownIconWrapper, {
alignItems: "center",
justifyContent: "center",
background: "neutral200",
height: "3.2rem",
width: "3.2rem",
children: /*#__PURE__*/ jsx(CaretDown, {})
}),
/*#__PURE__*/ jsx(Flex, {
maxWidth: "15rem",
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral700",
ellipsis: true,
children: displayedValue
})
})
]
})
}),
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsx(Trash, {})
}),
/*#__PURE__*/ jsx(IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsx(Drag, {})
})
]
})
]
});
};
const DropdownIconWrapper = styled(Flex)`
border-radius: 50%;
svg {
height: 0.6rem;
width: 1.1rem;
> path {
fill: ${({ theme })=>theme.colors.neutral600};
}
}
`;
// TODO: we shouldn't have to reset a whole button
const ToggleButton = styled.button`
border: none;
background: transparent;
display: block;
width: 100%;
text-align: unset;
padding: 0;
`;
export { ComponentDragPreview };
//# sourceMappingURL=ComponentDragPreview.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ComponentDragPreview.mjs","sources":["../../../../admin/src/components/DragPreviews/ComponentDragPreview.tsx"],"sourcesContent":["import { Flex, FlexComponent, IconButton, Typography } from '@strapi/design-system';\nimport { CaretDown, Drag, Trash } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\ninterface ComponentDragPreviewProps {\n displayedValue: string;\n}\n\nconst ComponentDragPreview = ({ displayedValue }: ComponentDragPreviewProps) => {\n return (\n <Flex\n background=\"neutral0\"\n borderColor=\"neutral200\"\n justifyContent=\"space-between\"\n gap={3}\n padding={3}\n width=\"30rem\"\n >\n <ToggleButton type=\"button\">\n <Flex gap={6}>\n <DropdownIconWrapper\n alignItems=\"center\"\n justifyContent=\"center\"\n background=\"neutral200\"\n height=\"3.2rem\"\n width=\"3.2rem\"\n >\n <CaretDown />\n </DropdownIconWrapper>\n\n <Flex maxWidth=\"15rem\">\n <Typography textColor=\"neutral700\" ellipsis>\n {displayedValue}\n </Typography>\n </Flex>\n </Flex>\n </ToggleButton>\n\n <Flex gap={2}>\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Trash />\n </IconButton>\n\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Drag />\n </IconButton>\n </Flex>\n </Flex>\n );\n};\n\nconst DropdownIconWrapper = styled<FlexComponent>(Flex)`\n border-radius: 50%;\n\n svg {\n height: 0.6rem;\n width: 1.1rem;\n > path {\n fill: ${({ theme }) => theme.colors.neutral600};\n }\n }\n`;\n\n// TODO: we shouldn't have to reset a whole button\nconst ToggleButton = styled.button`\n border: none;\n background: transparent;\n display: block;\n width: 100%;\n text-align: unset;\n padding: 0;\n`;\n\nexport { ComponentDragPreview };\nexport type { ComponentDragPreviewProps };\n"],"names":["ComponentDragPreview","displayedValue","_jsxs","Flex","background","borderColor","justifyContent","gap","padding","width","_jsx","ToggleButton","type","DropdownIconWrapper","alignItems","height","CaretDown","maxWidth","Typography","textColor","ellipsis","IconButton","withTooltip","label","variant","Trash","Drag","styled","theme","colors","neutral600","button"],"mappings":";;;;;AAQA,MAAMA,oBAAuB,GAAA,CAAC,EAAEC,cAAc,EAA6B,GAAA;AACzE,IAAA,qBACEC,IAACC,CAAAA,IAAAA,EAAAA;QACCC,UAAW,EAAA,UAAA;QACXC,WAAY,EAAA,YAAA;QACZC,cAAe,EAAA,eAAA;QACfC,GAAK,EAAA,CAAA;QACLC,OAAS,EAAA,CAAA;QACTC,KAAM,EAAA,OAAA;;0BAENC,GAACC,CAAAA,YAAAA,EAAAA;gBAAaC,IAAK,EAAA,QAAA;AACjB,gBAAA,QAAA,gBAAAV,IAACC,CAAAA,IAAAA,EAAAA;oBAAKI,GAAK,EAAA,CAAA;;sCACTG,GAACG,CAAAA,mBAAAA,EAAAA;4BACCC,UAAW,EAAA,QAAA;4BACXR,cAAe,EAAA,QAAA;4BACfF,UAAW,EAAA,YAAA;4BACXW,MAAO,EAAA,QAAA;4BACPN,KAAM,EAAA,QAAA;AAEN,4BAAA,QAAA,gBAAAC,GAACM,CAAAA,SAAAA,EAAAA,EAAAA;;sCAGHN,GAACP,CAAAA,IAAAA,EAAAA;4BAAKc,QAAS,EAAA,OAAA;AACb,4BAAA,QAAA,gBAAAP,GAACQ,CAAAA,UAAAA,EAAAA;gCAAWC,SAAU,EAAA,YAAA;gCAAaC,QAAQ,EAAA,IAAA;AACxCnB,gCAAAA,QAAAA,EAAAA;;;;;;0BAMTC,IAACC,CAAAA,IAAAA,EAAAA;gBAAKI,GAAK,EAAA,CAAA;;kCACTG,GAACW,CAAAA,UAAAA,EAAAA;wBAAWC,WAAa,EAAA,KAAA;wBAAOC,KAAM,EAAA,EAAA;wBAAGC,OAAQ,EAAA,OAAA;AAC/C,wBAAA,QAAA,gBAAAd,GAACe,CAAAA,KAAAA,EAAAA,EAAAA;;kCAGHf,GAACW,CAAAA,UAAAA,EAAAA;wBAAWC,WAAa,EAAA,KAAA;wBAAOC,KAAM,EAAA,EAAA;wBAAGC,OAAQ,EAAA,OAAA;AAC/C,wBAAA,QAAA,gBAAAd,GAACgB,CAAAA,IAAAA,EAAAA,EAAAA;;;;;;AAKX;AAEA,MAAMb,mBAAAA,GAAsBc,MAAsBxB,CAAAA,IAAAA,CAAK;;;;;;;YAO3C,EAAE,CAAC,EAAEyB,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACC,UAAU,CAAC;;;AAGrD,CAAC;AAED;AACA,MAAMnB,YAAAA,GAAegB,MAAOI,CAAAA,MAAM;;;;;;;AAOlC,CAAC;;;;"}

View File

@@ -0,0 +1,73 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var DocumentStatus = require('../../pages/EditView/components/DocumentStatus.js');
var Relations = require('../../pages/EditView/components/FormInputs/Relations/Relations.js');
const RelationDragPreview = ({ status, displayedValue, width })=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
style: {
width
},
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 2,
paddingRight: 4,
hasRadius: true,
borderWidth: 1,
background: "neutral0",
borderColor: "neutral200",
justifyContent: "space-between",
gap: 4,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(Relations.FlexWrapper, {
gap: 1,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Drag, {})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
width: "100%",
minWidth: 0,
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(Relations.LinkEllipsis, {
href: "",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "primary600",
ellipsis: true,
children: displayedValue
})
})
}),
status ? /*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: status
}) : null
]
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(Relations.DisconnectButton, {
type: "button",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Cross, {
width: "12px"
})
})
]
})
});
};
exports.RelationDragPreview = RelationDragPreview;
//# sourceMappingURL=RelationDragPreview.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelationDragPreview.js","sources":["../../../../admin/src/components/DragPreviews/RelationDragPreview.tsx"],"sourcesContent":["import { Box, Flex, IconButton, Typography } from '@strapi/design-system';\nimport { Cross, Drag } from '@strapi/icons';\n\nimport { DocumentStatus } from '../../pages/EditView/components/DocumentStatus';\nimport {\n DisconnectButton,\n LinkEllipsis,\n FlexWrapper,\n} from '../../pages/EditView/components/FormInputs/Relations/Relations';\n\nimport type { Data } from '@strapi/types';\n\ninterface RelationDragPreviewProps {\n status?: string;\n displayedValue: string;\n id: Data.ID;\n index: number;\n width: number;\n}\n\nconst RelationDragPreview = ({ status, displayedValue, width }: RelationDragPreviewProps) => {\n return (\n <Box style={{ width }}>\n <Flex\n paddingTop={2}\n paddingBottom={2}\n paddingLeft={2}\n paddingRight={4}\n hasRadius\n borderWidth={1}\n background=\"neutral0\"\n borderColor=\"neutral200\"\n justifyContent=\"space-between\"\n gap={4}\n >\n <FlexWrapper gap={1}>\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Drag />\n </IconButton>\n <Flex width=\"100%\" minWidth={0} justifyContent=\"space-between\">\n <Box minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>\n <LinkEllipsis href=\"\">\n <Typography textColor=\"primary600\" ellipsis>\n {displayedValue}\n </Typography>\n </LinkEllipsis>\n </Box>\n {status ? <DocumentStatus status={status} /> : null}\n </Flex>\n </FlexWrapper>\n <DisconnectButton type=\"button\">\n <Cross width=\"12px\" />\n </DisconnectButton>\n </Flex>\n </Box>\n );\n};\n\nexport { RelationDragPreview };\nexport type { RelationDragPreviewProps };\n"],"names":["RelationDragPreview","status","displayedValue","width","_jsx","Box","style","_jsxs","Flex","paddingTop","paddingBottom","paddingLeft","paddingRight","hasRadius","borderWidth","background","borderColor","justifyContent","gap","FlexWrapper","IconButton","withTooltip","label","variant","Drag","minWidth","LinkEllipsis","href","Typography","textColor","ellipsis","DocumentStatus","DisconnectButton","type","Cross"],"mappings":";;;;;;;;AAoBMA,MAAAA,mBAAAA,GAAsB,CAAC,EAAEC,MAAM,EAAEC,cAAc,EAAEC,KAAK,EAA4B,GAAA;AACtF,IAAA,qBACEC,cAACC,CAAAA,gBAAAA,EAAAA;QAAIC,KAAO,EAAA;AAAEH,YAAAA;AAAM,SAAA;AAClB,QAAA,QAAA,gBAAAI,eAACC,CAAAA,iBAAAA,EAAAA;YACCC,UAAY,EAAA,CAAA;YACZC,aAAe,EAAA,CAAA;YACfC,WAAa,EAAA,CAAA;YACbC,YAAc,EAAA,CAAA;YACdC,SAAS,EAAA,IAAA;YACTC,WAAa,EAAA,CAAA;YACbC,UAAW,EAAA,UAAA;YACXC,WAAY,EAAA,YAAA;YACZC,cAAe,EAAA,eAAA;YACfC,GAAK,EAAA,CAAA;;8BAELX,eAACY,CAAAA,qBAAAA,EAAAA;oBAAYD,GAAK,EAAA,CAAA;;sCAChBd,cAACgB,CAAAA,uBAAAA,EAAAA;4BAAWC,WAAa,EAAA,KAAA;4BAAOC,KAAM,EAAA,EAAA;4BAAGC,OAAQ,EAAA,OAAA;AAC/C,4BAAA,QAAA,gBAAAnB,cAACoB,CAAAA,UAAAA,EAAAA,EAAAA;;sCAEHjB,eAACC,CAAAA,iBAAAA,EAAAA;4BAAKL,KAAM,EAAA,MAAA;4BAAOsB,QAAU,EAAA,CAAA;4BAAGR,cAAe,EAAA,eAAA;;8CAC7Cb,cAACC,CAAAA,gBAAAA,EAAAA;oCAAIoB,QAAU,EAAA,CAAA;oCAAGhB,UAAY,EAAA,CAAA;oCAAGC,aAAe,EAAA,CAAA;oCAAGE,YAAc,EAAA,CAAA;AAC/D,oCAAA,QAAA,gBAAAR,cAACsB,CAAAA,sBAAAA,EAAAA;wCAAaC,IAAK,EAAA,EAAA;AACjB,wCAAA,QAAA,gBAAAvB,cAACwB,CAAAA,uBAAAA,EAAAA;4CAAWC,SAAU,EAAA,YAAA;4CAAaC,QAAQ,EAAA,IAAA;AACxC5B,4CAAAA,QAAAA,EAAAA;;;;AAIND,gCAAAA,MAAAA,iBAASG,cAAC2B,CAAAA,6BAAAA,EAAAA;oCAAe9B,MAAQA,EAAAA;AAAa,iCAAA,CAAA,GAAA;;;;;8BAGnDG,cAAC4B,CAAAA,0BAAAA,EAAAA;oBAAiBC,IAAK,EAAA,QAAA;AACrB,oBAAA,QAAA,gBAAA7B,cAAC8B,CAAAA,WAAAA,EAAAA;wBAAM/B,KAAM,EAAA;;;;;;AAKvB;;;;"}

View File

@@ -0,0 +1,71 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { Box, Flex, IconButton, Typography } from '@strapi/design-system';
import { Drag, Cross } from '@strapi/icons';
import { DocumentStatus } from '../../pages/EditView/components/DocumentStatus.mjs';
import { FlexWrapper, LinkEllipsis, DisconnectButton } from '../../pages/EditView/components/FormInputs/Relations/Relations.mjs';
const RelationDragPreview = ({ status, displayedValue, width })=>{
return /*#__PURE__*/ jsx(Box, {
style: {
width
},
children: /*#__PURE__*/ jsxs(Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 2,
paddingRight: 4,
hasRadius: true,
borderWidth: 1,
background: "neutral0",
borderColor: "neutral200",
justifyContent: "space-between",
gap: 4,
children: [
/*#__PURE__*/ jsxs(FlexWrapper, {
gap: 1,
children: [
/*#__PURE__*/ jsx(IconButton, {
withTooltip: false,
label: "",
variant: "ghost",
children: /*#__PURE__*/ jsx(Drag, {})
}),
/*#__PURE__*/ jsxs(Flex, {
width: "100%",
minWidth: 0,
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsx(Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsx(LinkEllipsis, {
href: "",
children: /*#__PURE__*/ jsx(Typography, {
textColor: "primary600",
ellipsis: true,
children: displayedValue
})
})
}),
status ? /*#__PURE__*/ jsx(DocumentStatus, {
status: status
}) : null
]
})
]
}),
/*#__PURE__*/ jsx(DisconnectButton, {
type: "button",
children: /*#__PURE__*/ jsx(Cross, {
width: "12px"
})
})
]
})
});
};
export { RelationDragPreview };
//# sourceMappingURL=RelationDragPreview.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelationDragPreview.mjs","sources":["../../../../admin/src/components/DragPreviews/RelationDragPreview.tsx"],"sourcesContent":["import { Box, Flex, IconButton, Typography } from '@strapi/design-system';\nimport { Cross, Drag } from '@strapi/icons';\n\nimport { DocumentStatus } from '../../pages/EditView/components/DocumentStatus';\nimport {\n DisconnectButton,\n LinkEllipsis,\n FlexWrapper,\n} from '../../pages/EditView/components/FormInputs/Relations/Relations';\n\nimport type { Data } from '@strapi/types';\n\ninterface RelationDragPreviewProps {\n status?: string;\n displayedValue: string;\n id: Data.ID;\n index: number;\n width: number;\n}\n\nconst RelationDragPreview = ({ status, displayedValue, width }: RelationDragPreviewProps) => {\n return (\n <Box style={{ width }}>\n <Flex\n paddingTop={2}\n paddingBottom={2}\n paddingLeft={2}\n paddingRight={4}\n hasRadius\n borderWidth={1}\n background=\"neutral0\"\n borderColor=\"neutral200\"\n justifyContent=\"space-between\"\n gap={4}\n >\n <FlexWrapper gap={1}>\n <IconButton withTooltip={false} label=\"\" variant=\"ghost\">\n <Drag />\n </IconButton>\n <Flex width=\"100%\" minWidth={0} justifyContent=\"space-between\">\n <Box minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>\n <LinkEllipsis href=\"\">\n <Typography textColor=\"primary600\" ellipsis>\n {displayedValue}\n </Typography>\n </LinkEllipsis>\n </Box>\n {status ? <DocumentStatus status={status} /> : null}\n </Flex>\n </FlexWrapper>\n <DisconnectButton type=\"button\">\n <Cross width=\"12px\" />\n </DisconnectButton>\n </Flex>\n </Box>\n );\n};\n\nexport { RelationDragPreview };\nexport type { RelationDragPreviewProps };\n"],"names":["RelationDragPreview","status","displayedValue","width","_jsx","Box","style","_jsxs","Flex","paddingTop","paddingBottom","paddingLeft","paddingRight","hasRadius","borderWidth","background","borderColor","justifyContent","gap","FlexWrapper","IconButton","withTooltip","label","variant","Drag","minWidth","LinkEllipsis","href","Typography","textColor","ellipsis","DocumentStatus","DisconnectButton","type","Cross"],"mappings":";;;;;;AAoBMA,MAAAA,mBAAAA,GAAsB,CAAC,EAAEC,MAAM,EAAEC,cAAc,EAAEC,KAAK,EAA4B,GAAA;AACtF,IAAA,qBACEC,GAACC,CAAAA,GAAAA,EAAAA;QAAIC,KAAO,EAAA;AAAEH,YAAAA;AAAM,SAAA;AAClB,QAAA,QAAA,gBAAAI,IAACC,CAAAA,IAAAA,EAAAA;YACCC,UAAY,EAAA,CAAA;YACZC,aAAe,EAAA,CAAA;YACfC,WAAa,EAAA,CAAA;YACbC,YAAc,EAAA,CAAA;YACdC,SAAS,EAAA,IAAA;YACTC,WAAa,EAAA,CAAA;YACbC,UAAW,EAAA,UAAA;YACXC,WAAY,EAAA,YAAA;YACZC,cAAe,EAAA,eAAA;YACfC,GAAK,EAAA,CAAA;;8BAELX,IAACY,CAAAA,WAAAA,EAAAA;oBAAYD,GAAK,EAAA,CAAA;;sCAChBd,GAACgB,CAAAA,UAAAA,EAAAA;4BAAWC,WAAa,EAAA,KAAA;4BAAOC,KAAM,EAAA,EAAA;4BAAGC,OAAQ,EAAA,OAAA;AAC/C,4BAAA,QAAA,gBAAAnB,GAACoB,CAAAA,IAAAA,EAAAA,EAAAA;;sCAEHjB,IAACC,CAAAA,IAAAA,EAAAA;4BAAKL,KAAM,EAAA,MAAA;4BAAOsB,QAAU,EAAA,CAAA;4BAAGR,cAAe,EAAA,eAAA;;8CAC7Cb,GAACC,CAAAA,GAAAA,EAAAA;oCAAIoB,QAAU,EAAA,CAAA;oCAAGhB,UAAY,EAAA,CAAA;oCAAGC,aAAe,EAAA,CAAA;oCAAGE,YAAc,EAAA,CAAA;AAC/D,oCAAA,QAAA,gBAAAR,GAACsB,CAAAA,YAAAA,EAAAA;wCAAaC,IAAK,EAAA,EAAA;AACjB,wCAAA,QAAA,gBAAAvB,GAACwB,CAAAA,UAAAA,EAAAA;4CAAWC,SAAU,EAAA,YAAA;4CAAaC,QAAQ,EAAA,IAAA;AACxC5B,4CAAAA,QAAAA,EAAAA;;;;AAIND,gCAAAA,MAAAA,iBAASG,GAAC2B,CAAAA,cAAAA,EAAAA;oCAAe9B,MAAQA,EAAAA;AAAa,iCAAA,CAAA,GAAA;;;;;8BAGnDG,GAAC4B,CAAAA,gBAAAA,EAAAA;oBAAiBC,IAAK,EAAA,QAAA;AACrB,oBAAA,QAAA,gBAAA7B,GAAC8B,CAAAA,KAAAA,EAAAA;wBAAM/B,KAAM,EAAA;;;;;;AAKvB;;;;"}

View File

@@ -0,0 +1,58 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var Symbols = require('@strapi/icons/symbols');
const iconByTypes = {
biginteger: /*#__PURE__*/ jsxRuntime.jsx(Symbols.NumberField, {}),
boolean: /*#__PURE__*/ jsxRuntime.jsx(Symbols.BooleanField, {}),
date: /*#__PURE__*/ jsxRuntime.jsx(Symbols.DateField, {}),
datetime: /*#__PURE__*/ jsxRuntime.jsx(Symbols.DateField, {}),
decimal: /*#__PURE__*/ jsxRuntime.jsx(Symbols.NumberField, {}),
email: /*#__PURE__*/ jsxRuntime.jsx(Symbols.EmailField, {}),
enumeration: /*#__PURE__*/ jsxRuntime.jsx(Symbols.EnumerationField, {}),
float: /*#__PURE__*/ jsxRuntime.jsx(Symbols.NumberField, {}),
integer: /*#__PURE__*/ jsxRuntime.jsx(Symbols.NumberField, {}),
media: /*#__PURE__*/ jsxRuntime.jsx(Symbols.MediaField, {}),
password: /*#__PURE__*/ jsxRuntime.jsx(Symbols.PasswordField, {}),
relation: /*#__PURE__*/ jsxRuntime.jsx(Symbols.RelationField, {}),
string: /*#__PURE__*/ jsxRuntime.jsx(Symbols.TextField, {}),
text: /*#__PURE__*/ jsxRuntime.jsx(Symbols.TextField, {}),
richtext: /*#__PURE__*/ jsxRuntime.jsx(Symbols.TextField, {}),
time: /*#__PURE__*/ jsxRuntime.jsx(Symbols.DateField, {}),
timestamp: /*#__PURE__*/ jsxRuntime.jsx(Symbols.DateField, {}),
json: /*#__PURE__*/ jsxRuntime.jsx(Symbols.JsonField, {}),
uid: /*#__PURE__*/ jsxRuntime.jsx(Symbols.UidField, {}),
component: /*#__PURE__*/ jsxRuntime.jsx(Symbols.ComponentField, {}),
dynamiczone: /*#__PURE__*/ jsxRuntime.jsx(Symbols.DynamicZoneField, {}),
blocks: /*#__PURE__*/ jsxRuntime.jsx(Symbols.BlocksField, {})
};
const FieldTypeIcon = ({ type, customFieldUid })=>{
const getCustomField = strapiAdmin.useStrapiApp('FieldTypeIcon', (state)=>state.customFields.get);
if (!type) {
return null;
}
let Compo = iconByTypes[type];
if (customFieldUid) {
const customField = getCustomField(customFieldUid);
const CustomFieldIcon = customField?.icon;
if (CustomFieldIcon) {
Compo = /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
marginRight: 3,
width: 7,
height: 6,
children: /*#__PURE__*/ jsxRuntime.jsx(CustomFieldIcon, {})
});
}
}
if (!iconByTypes[type]) {
return null;
}
return Compo;
};
exports.FieldTypeIcon = FieldTypeIcon;
//# sourceMappingURL=FieldTypeIcon.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FieldTypeIcon.js","sources":["../../../admin/src/components/FieldTypeIcon.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useStrapiApp } from '@strapi/admin/strapi-admin';\nimport { Box } from '@strapi/design-system';\nimport {\n BlocksField,\n BooleanField,\n ComponentField,\n DateField,\n DynamicZoneField,\n EmailField,\n EnumerationField,\n JsonField,\n MediaField,\n NumberField,\n PasswordField,\n RelationField,\n TextField,\n UidField,\n} from '@strapi/icons/symbols';\n\nimport type { Schema } from '@strapi/types';\n\nconst iconByTypes: Record<Schema.Attribute.Kind, React.ReactElement> = {\n biginteger: <NumberField />,\n boolean: <BooleanField />,\n date: <DateField />,\n datetime: <DateField />,\n decimal: <NumberField />,\n email: <EmailField />,\n enumeration: <EnumerationField />,\n float: <NumberField />,\n integer: <NumberField />,\n media: <MediaField />,\n password: <PasswordField />,\n relation: <RelationField />,\n string: <TextField />,\n text: <TextField />,\n richtext: <TextField />,\n time: <DateField />,\n timestamp: <DateField />,\n json: <JsonField />,\n uid: <UidField />,\n component: <ComponentField />,\n dynamiczone: <DynamicZoneField />,\n blocks: <BlocksField />,\n};\n\ninterface FieldTypeIconProps {\n type?: keyof typeof iconByTypes;\n customFieldUid?: string;\n}\n\nconst FieldTypeIcon = ({ type, customFieldUid }: FieldTypeIconProps) => {\n const getCustomField = useStrapiApp('FieldTypeIcon', (state) => state.customFields.get);\n\n if (!type) {\n return null;\n }\n\n let Compo = iconByTypes[type];\n\n if (customFieldUid) {\n const customField = getCustomField(customFieldUid);\n const CustomFieldIcon = customField?.icon;\n\n if (CustomFieldIcon) {\n Compo = (\n <Box marginRight={3} width={7} height={6}>\n <CustomFieldIcon />\n </Box>\n );\n }\n }\n\n if (!iconByTypes[type]) {\n return null;\n }\n\n return Compo;\n};\n\nexport { FieldTypeIcon };\n"],"names":["iconByTypes","biginteger","_jsx","NumberField","boolean","BooleanField","date","DateField","datetime","decimal","email","EmailField","enumeration","EnumerationField","float","integer","media","MediaField","password","PasswordField","relation","RelationField","string","TextField","text","richtext","time","timestamp","json","JsonField","uid","UidField","component","ComponentField","dynamiczone","DynamicZoneField","blocks","BlocksField","FieldTypeIcon","type","customFieldUid","getCustomField","useStrapiApp","state","customFields","get","Compo","customField","CustomFieldIcon","icon","Box","marginRight","width","height"],"mappings":";;;;;;;;AAuBA,MAAMA,WAAiE,GAAA;AACrEC,IAAAA,UAAAA,gBAAYC,cAACC,CAAAA,mBAAAA,EAAAA,EAAAA,CAAAA;AACbC,IAAAA,OAAAA,gBAASF,cAACG,CAAAA,oBAAAA,EAAAA,EAAAA,CAAAA;AACVC,IAAAA,IAAAA,gBAAMJ,cAACK,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACPC,IAAAA,QAAAA,gBAAUN,cAACK,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACXE,IAAAA,OAAAA,gBAASP,cAACC,CAAAA,mBAAAA,EAAAA,EAAAA,CAAAA;AACVO,IAAAA,KAAAA,gBAAOR,cAACS,CAAAA,kBAAAA,EAAAA,EAAAA,CAAAA;AACRC,IAAAA,WAAAA,gBAAaV,cAACW,CAAAA,wBAAAA,EAAAA,EAAAA,CAAAA;AACdC,IAAAA,KAAAA,gBAAOZ,cAACC,CAAAA,mBAAAA,EAAAA,EAAAA,CAAAA;AACRY,IAAAA,OAAAA,gBAASb,cAACC,CAAAA,mBAAAA,EAAAA,EAAAA,CAAAA;AACVa,IAAAA,KAAAA,gBAAOd,cAACe,CAAAA,kBAAAA,EAAAA,EAAAA,CAAAA;AACRC,IAAAA,QAAAA,gBAAUhB,cAACiB,CAAAA,qBAAAA,EAAAA,EAAAA,CAAAA;AACXC,IAAAA,QAAAA,gBAAUlB,cAACmB,CAAAA,qBAAAA,EAAAA,EAAAA,CAAAA;AACXC,IAAAA,MAAAA,gBAAQpB,cAACqB,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACTC,IAAAA,IAAAA,gBAAMtB,cAACqB,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACPE,IAAAA,QAAAA,gBAAUvB,cAACqB,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACXG,IAAAA,IAAAA,gBAAMxB,cAACK,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACPoB,IAAAA,SAAAA,gBAAWzB,cAACK,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACZqB,IAAAA,IAAAA,gBAAM1B,cAAC2B,CAAAA,iBAAAA,EAAAA,EAAAA,CAAAA;AACPC,IAAAA,GAAAA,gBAAK5B,cAAC6B,CAAAA,gBAAAA,EAAAA,EAAAA,CAAAA;AACNC,IAAAA,SAAAA,gBAAW9B,cAAC+B,CAAAA,sBAAAA,EAAAA,EAAAA,CAAAA;AACZC,IAAAA,WAAAA,gBAAahC,cAACiC,CAAAA,wBAAAA,EAAAA,EAAAA,CAAAA;AACdC,IAAAA,MAAAA,gBAAQlC,cAACmC,CAAAA,mBAAAA,EAAAA,EAAAA;AACX,CAAA;AAOA,MAAMC,gBAAgB,CAAC,EAAEC,IAAI,EAAEC,cAAc,EAAsB,GAAA;IACjE,MAAMC,cAAAA,GAAiBC,yBAAa,eAAiB,EAAA,CAACC,QAAUA,KAAMC,CAAAA,YAAY,CAACC,GAAG,CAAA;AAEtF,IAAA,IAAI,CAACN,IAAM,EAAA;QACT,OAAO,IAAA;AACT;IAEA,IAAIO,KAAAA,GAAQ9C,WAAW,CAACuC,IAAK,CAAA;AAE7B,IAAA,IAAIC,cAAgB,EAAA;AAClB,QAAA,MAAMO,cAAcN,cAAeD,CAAAA,cAAAA,CAAAA;AACnC,QAAA,MAAMQ,kBAAkBD,WAAaE,EAAAA,IAAAA;AAErC,QAAA,IAAID,eAAiB,EAAA;AACnBF,YAAAA,KAAAA,iBACE5C,cAACgD,CAAAA,gBAAAA,EAAAA;gBAAIC,WAAa,EAAA,CAAA;gBAAGC,KAAO,EAAA,CAAA;gBAAGC,MAAQ,EAAA,CAAA;AACrC,gBAAA,QAAA,gBAAAnD,cAAC8C,CAAAA,eAAAA,EAAAA,EAAAA;;AAGP;AACF;AAEA,IAAA,IAAI,CAAChD,WAAW,CAACuC,IAAAA,CAAK,EAAE;QACtB,OAAO,IAAA;AACT;IAEA,OAAOO,KAAAA;AACT;;;;"}

View File

@@ -0,0 +1,56 @@
import { jsx } from 'react/jsx-runtime';
import 'react';
import { useStrapiApp } from '@strapi/admin/strapi-admin';
import { Box } from '@strapi/design-system';
import { NumberField, BooleanField, DateField, EmailField, EnumerationField, MediaField, PasswordField, RelationField, TextField, JsonField, UidField, ComponentField, DynamicZoneField, BlocksField } from '@strapi/icons/symbols';
const iconByTypes = {
biginteger: /*#__PURE__*/ jsx(NumberField, {}),
boolean: /*#__PURE__*/ jsx(BooleanField, {}),
date: /*#__PURE__*/ jsx(DateField, {}),
datetime: /*#__PURE__*/ jsx(DateField, {}),
decimal: /*#__PURE__*/ jsx(NumberField, {}),
email: /*#__PURE__*/ jsx(EmailField, {}),
enumeration: /*#__PURE__*/ jsx(EnumerationField, {}),
float: /*#__PURE__*/ jsx(NumberField, {}),
integer: /*#__PURE__*/ jsx(NumberField, {}),
media: /*#__PURE__*/ jsx(MediaField, {}),
password: /*#__PURE__*/ jsx(PasswordField, {}),
relation: /*#__PURE__*/ jsx(RelationField, {}),
string: /*#__PURE__*/ jsx(TextField, {}),
text: /*#__PURE__*/ jsx(TextField, {}),
richtext: /*#__PURE__*/ jsx(TextField, {}),
time: /*#__PURE__*/ jsx(DateField, {}),
timestamp: /*#__PURE__*/ jsx(DateField, {}),
json: /*#__PURE__*/ jsx(JsonField, {}),
uid: /*#__PURE__*/ jsx(UidField, {}),
component: /*#__PURE__*/ jsx(ComponentField, {}),
dynamiczone: /*#__PURE__*/ jsx(DynamicZoneField, {}),
blocks: /*#__PURE__*/ jsx(BlocksField, {})
};
const FieldTypeIcon = ({ type, customFieldUid })=>{
const getCustomField = useStrapiApp('FieldTypeIcon', (state)=>state.customFields.get);
if (!type) {
return null;
}
let Compo = iconByTypes[type];
if (customFieldUid) {
const customField = getCustomField(customFieldUid);
const CustomFieldIcon = customField?.icon;
if (CustomFieldIcon) {
Compo = /*#__PURE__*/ jsx(Box, {
marginRight: 3,
width: 7,
height: 6,
children: /*#__PURE__*/ jsx(CustomFieldIcon, {})
});
}
}
if (!iconByTypes[type]) {
return null;
}
return Compo;
};
export { FieldTypeIcon };
//# sourceMappingURL=FieldTypeIcon.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FieldTypeIcon.mjs","sources":["../../../admin/src/components/FieldTypeIcon.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useStrapiApp } from '@strapi/admin/strapi-admin';\nimport { Box } from '@strapi/design-system';\nimport {\n BlocksField,\n BooleanField,\n ComponentField,\n DateField,\n DynamicZoneField,\n EmailField,\n EnumerationField,\n JsonField,\n MediaField,\n NumberField,\n PasswordField,\n RelationField,\n TextField,\n UidField,\n} from '@strapi/icons/symbols';\n\nimport type { Schema } from '@strapi/types';\n\nconst iconByTypes: Record<Schema.Attribute.Kind, React.ReactElement> = {\n biginteger: <NumberField />,\n boolean: <BooleanField />,\n date: <DateField />,\n datetime: <DateField />,\n decimal: <NumberField />,\n email: <EmailField />,\n enumeration: <EnumerationField />,\n float: <NumberField />,\n integer: <NumberField />,\n media: <MediaField />,\n password: <PasswordField />,\n relation: <RelationField />,\n string: <TextField />,\n text: <TextField />,\n richtext: <TextField />,\n time: <DateField />,\n timestamp: <DateField />,\n json: <JsonField />,\n uid: <UidField />,\n component: <ComponentField />,\n dynamiczone: <DynamicZoneField />,\n blocks: <BlocksField />,\n};\n\ninterface FieldTypeIconProps {\n type?: keyof typeof iconByTypes;\n customFieldUid?: string;\n}\n\nconst FieldTypeIcon = ({ type, customFieldUid }: FieldTypeIconProps) => {\n const getCustomField = useStrapiApp('FieldTypeIcon', (state) => state.customFields.get);\n\n if (!type) {\n return null;\n }\n\n let Compo = iconByTypes[type];\n\n if (customFieldUid) {\n const customField = getCustomField(customFieldUid);\n const CustomFieldIcon = customField?.icon;\n\n if (CustomFieldIcon) {\n Compo = (\n <Box marginRight={3} width={7} height={6}>\n <CustomFieldIcon />\n </Box>\n );\n }\n }\n\n if (!iconByTypes[type]) {\n return null;\n }\n\n return Compo;\n};\n\nexport { FieldTypeIcon };\n"],"names":["iconByTypes","biginteger","_jsx","NumberField","boolean","BooleanField","date","DateField","datetime","decimal","email","EmailField","enumeration","EnumerationField","float","integer","media","MediaField","password","PasswordField","relation","RelationField","string","TextField","text","richtext","time","timestamp","json","JsonField","uid","UidField","component","ComponentField","dynamiczone","DynamicZoneField","blocks","BlocksField","FieldTypeIcon","type","customFieldUid","getCustomField","useStrapiApp","state","customFields","get","Compo","customField","CustomFieldIcon","icon","Box","marginRight","width","height"],"mappings":";;;;;;AAuBA,MAAMA,WAAiE,GAAA;AACrEC,IAAAA,UAAAA,gBAAYC,GAACC,CAAAA,WAAAA,EAAAA,EAAAA,CAAAA;AACbC,IAAAA,OAAAA,gBAASF,GAACG,CAAAA,YAAAA,EAAAA,EAAAA,CAAAA;AACVC,IAAAA,IAAAA,gBAAMJ,GAACK,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACPC,IAAAA,QAAAA,gBAAUN,GAACK,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACXE,IAAAA,OAAAA,gBAASP,GAACC,CAAAA,WAAAA,EAAAA,EAAAA,CAAAA;AACVO,IAAAA,KAAAA,gBAAOR,GAACS,CAAAA,UAAAA,EAAAA,EAAAA,CAAAA;AACRC,IAAAA,WAAAA,gBAAaV,GAACW,CAAAA,gBAAAA,EAAAA,EAAAA,CAAAA;AACdC,IAAAA,KAAAA,gBAAOZ,GAACC,CAAAA,WAAAA,EAAAA,EAAAA,CAAAA;AACRY,IAAAA,OAAAA,gBAASb,GAACC,CAAAA,WAAAA,EAAAA,EAAAA,CAAAA;AACVa,IAAAA,KAAAA,gBAAOd,GAACe,CAAAA,UAAAA,EAAAA,EAAAA,CAAAA;AACRC,IAAAA,QAAAA,gBAAUhB,GAACiB,CAAAA,aAAAA,EAAAA,EAAAA,CAAAA;AACXC,IAAAA,QAAAA,gBAAUlB,GAACmB,CAAAA,aAAAA,EAAAA,EAAAA,CAAAA;AACXC,IAAAA,MAAAA,gBAAQpB,GAACqB,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACTC,IAAAA,IAAAA,gBAAMtB,GAACqB,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACPE,IAAAA,QAAAA,gBAAUvB,GAACqB,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACXG,IAAAA,IAAAA,gBAAMxB,GAACK,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACPoB,IAAAA,SAAAA,gBAAWzB,GAACK,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACZqB,IAAAA,IAAAA,gBAAM1B,GAAC2B,CAAAA,SAAAA,EAAAA,EAAAA,CAAAA;AACPC,IAAAA,GAAAA,gBAAK5B,GAAC6B,CAAAA,QAAAA,EAAAA,EAAAA,CAAAA;AACNC,IAAAA,SAAAA,gBAAW9B,GAAC+B,CAAAA,cAAAA,EAAAA,EAAAA,CAAAA;AACZC,IAAAA,WAAAA,gBAAahC,GAACiC,CAAAA,gBAAAA,EAAAA,EAAAA,CAAAA;AACdC,IAAAA,MAAAA,gBAAQlC,GAACmC,CAAAA,WAAAA,EAAAA,EAAAA;AACX,CAAA;AAOA,MAAMC,gBAAgB,CAAC,EAAEC,IAAI,EAAEC,cAAc,EAAsB,GAAA;IACjE,MAAMC,cAAAA,GAAiBC,aAAa,eAAiB,EAAA,CAACC,QAAUA,KAAMC,CAAAA,YAAY,CAACC,GAAG,CAAA;AAEtF,IAAA,IAAI,CAACN,IAAM,EAAA;QACT,OAAO,IAAA;AACT;IAEA,IAAIO,KAAAA,GAAQ9C,WAAW,CAACuC,IAAK,CAAA;AAE7B,IAAA,IAAIC,cAAgB,EAAA;AAClB,QAAA,MAAMO,cAAcN,cAAeD,CAAAA,cAAAA,CAAAA;AACnC,QAAA,MAAMQ,kBAAkBD,WAAaE,EAAAA,IAAAA;AAErC,QAAA,IAAID,eAAiB,EAAA;AACnBF,YAAAA,KAAAA,iBACE5C,GAACgD,CAAAA,GAAAA,EAAAA;gBAAIC,WAAa,EAAA,CAAA;gBAAGC,KAAO,EAAA,CAAA;gBAAGC,MAAQ,EAAA,CAAA;AACrC,gBAAA,QAAA,gBAAAnD,GAAC8C,CAAAA,eAAAA,EAAAA,EAAAA;;AAGP;AACF;AAEA,IAAA,IAAI,CAAChD,WAAW,CAACuC,IAAAA,CAAK,EAAE;QACtB,OAAO,IAAA;AACT;IAEA,OAAOO,KAAAA;AACT;;;;"}

View File

@@ -0,0 +1,44 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var plugin = require('../constants/plugin.js');
const INJECTION_ZONES = {
editView: {
informations: [],
'right-links': []
},
listView: {
actions: [],
deleteModalAdditionalInfos: [],
publishModalAdditionalInfos: [],
unpublishModalAdditionalInfos: []
},
preview: {
actions: []
}
};
/**
* You can't know what this component props will be because it's generic and used everywhere
* e.g. content-manager edit view, we just send the slug but we might not in the listView,
* therefore, people should type it themselves on the components they render.
*/ const InjectionZone = ({ area, ...props })=>{
const components = useInjectionZone(area);
return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
children: components.map((component)=>/*#__PURE__*/ jsxRuntime.jsx(component.Component, {
...props
}, component.name))
});
};
const useInjectionZone = (area)=>{
const getPlugin = strapiAdmin.useStrapiApp('useInjectionZone', (state)=>state.getPlugin);
const contentManagerPlugin = getPlugin(plugin.PLUGIN_ID);
const [page, position] = area.split('.');
return contentManagerPlugin.getInjectedComponents(page, position);
};
exports.INJECTION_ZONES = INJECTION_ZONES;
exports.InjectionZone = InjectionZone;
exports.useInjectionZone = useInjectionZone;
//# sourceMappingURL=InjectionZone.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"InjectionZone.js","sources":["../../../admin/src/components/InjectionZone.tsx"],"sourcesContent":["import { useStrapiApp, InjectionZoneComponent } from '@strapi/admin/strapi-admin';\n\nimport { PLUGIN_ID } from '../constants/plugin';\n\nconst INJECTION_ZONES = {\n editView: { informations: [], 'right-links': [] },\n listView: {\n actions: [],\n deleteModalAdditionalInfos: [],\n publishModalAdditionalInfos: [],\n unpublishModalAdditionalInfos: [],\n },\n preview: {\n actions: [],\n },\n} satisfies InjectionZones;\n\ninterface InjectionZones {\n editView: {\n informations: InjectionZoneComponent[];\n 'right-links': InjectionZoneComponent[];\n };\n listView: {\n actions: InjectionZoneComponent[];\n deleteModalAdditionalInfos: InjectionZoneComponent[];\n publishModalAdditionalInfos: InjectionZoneComponent[];\n unpublishModalAdditionalInfos: InjectionZoneComponent[];\n };\n preview: {\n actions: InjectionZoneComponent[];\n };\n}\n\ntype InjectionZoneArea =\n | 'editView.informations'\n | 'editView.right-links'\n | 'listView.actions'\n | 'listView.unpublishModalAdditionalInfos'\n | 'listView.deleteModalAdditionalInfos'\n | 'listView.publishModalAdditionalInfos'\n | 'listView.deleteModalAdditionalInfos'\n | 'preview.actions';\n\ntype InjectionZoneModule = InjectionZoneArea extends `${infer Word}.${string}` ? Word : never;\ntype InjectionZoneContainer = InjectionZoneArea extends `${string}.${infer Word}.${string}`\n ? Word\n : never;\ntype InjectionZoneBlock = InjectionZoneArea extends `${string}.${string}.${infer Word}`\n ? Word\n : never;\n\n/**\n * You can't know what this component props will be because it's generic and used everywhere\n * e.g. content-manager edit view, we just send the slug but we might not in the listView,\n * therefore, people should type it themselves on the components they render.\n */\nconst InjectionZone = ({ area, ...props }: { area: InjectionZoneArea; [key: string]: unknown }) => {\n const components = useInjectionZone(area);\n\n return (\n <>\n {components.map((component) => (\n <component.Component key={component.name} {...props} />\n ))}\n </>\n );\n};\n\nexport const useInjectionZone = (area: InjectionZoneArea) => {\n const getPlugin = useStrapiApp('useInjectionZone', (state) => state.getPlugin);\n const contentManagerPlugin = getPlugin(PLUGIN_ID);\n const [page, position] = area.split('.') as [InjectionZoneContainer, InjectionZoneBlock];\n\n return contentManagerPlugin.getInjectedComponents(page, position);\n};\n\nexport { InjectionZone, INJECTION_ZONES };\n\nexport type {\n InjectionZoneArea,\n InjectionZoneComponent,\n InjectionZones,\n InjectionZoneModule,\n InjectionZoneContainer,\n InjectionZoneBlock,\n};\n"],"names":["INJECTION_ZONES","editView","informations","listView","actions","deleteModalAdditionalInfos","publishModalAdditionalInfos","unpublishModalAdditionalInfos","preview","InjectionZone","area","props","components","useInjectionZone","_jsx","_Fragment","map","component","Component","name","getPlugin","useStrapiApp","state","contentManagerPlugin","PLUGIN_ID","page","position","split","getInjectedComponents"],"mappings":";;;;;;AAIA,MAAMA,eAAkB,GAAA;IACtBC,QAAU,EAAA;AAAEC,QAAAA,YAAAA,EAAc,EAAE;AAAE,QAAA,aAAA,EAAe;AAAG,KAAA;IAChDC,QAAU,EAAA;AACRC,QAAAA,OAAAA,EAAS,EAAE;AACXC,QAAAA,0BAAAA,EAA4B,EAAE;AAC9BC,QAAAA,2BAAAA,EAA6B,EAAE;AAC/BC,QAAAA,6BAAAA,EAA+B;AACjC,KAAA;IACAC,OAAS,EAAA;AACPJ,QAAAA,OAAAA,EAAS;AACX;AACF;AAoCA;;;;AAIC,UACKK,aAAgB,GAAA,CAAC,EAAEC,IAAI,EAAE,GAAGC,KAA4D,EAAA,GAAA;AAC5F,IAAA,MAAMC,aAAaC,gBAAiBH,CAAAA,IAAAA,CAAAA;IAEpC,qBACEI,cAAA,CAAAC,mBAAA,EAAA;AACGH,QAAAA,QAAAA,EAAAA,UAAAA,CAAWI,GAAG,CAAC,CAACC,SACf,iBAAAH,cAAA,CAACG,UAAUC,SAAS,EAAA;AAAuB,gBAAA,GAAGP;AAApBM,aAAAA,EAAAA,SAAAA,CAAUE,IAAI,CAAA;;AAIhD;AAEO,MAAMN,mBAAmB,CAACH,IAAAA,GAAAA;AAC/B,IAAA,MAAMU,YAAYC,wBAAa,CAAA,kBAAA,EAAoB,CAACC,KAAAA,GAAUA,MAAMF,SAAS,CAAA;AAC7E,IAAA,MAAMG,uBAAuBH,SAAUI,CAAAA,gBAAAA,CAAAA;AACvC,IAAA,MAAM,CAACC,IAAMC,EAAAA,QAAAA,CAAS,GAAGhB,IAAAA,CAAKiB,KAAK,CAAC,GAAA,CAAA;IAEpC,OAAOJ,oBAAAA,CAAqBK,qBAAqB,CAACH,IAAMC,EAAAA,QAAAA,CAAAA;AAC1D;;;;;;"}

View File

@@ -0,0 +1,40 @@
import { jsx, Fragment } from 'react/jsx-runtime';
import { useStrapiApp } from '@strapi/admin/strapi-admin';
import { PLUGIN_ID } from '../constants/plugin.mjs';
const INJECTION_ZONES = {
editView: {
informations: [],
'right-links': []
},
listView: {
actions: [],
deleteModalAdditionalInfos: [],
publishModalAdditionalInfos: [],
unpublishModalAdditionalInfos: []
},
preview: {
actions: []
}
};
/**
* You can't know what this component props will be because it's generic and used everywhere
* e.g. content-manager edit view, we just send the slug but we might not in the listView,
* therefore, people should type it themselves on the components they render.
*/ const InjectionZone = ({ area, ...props })=>{
const components = useInjectionZone(area);
return /*#__PURE__*/ jsx(Fragment, {
children: components.map((component)=>/*#__PURE__*/ jsx(component.Component, {
...props
}, component.name))
});
};
const useInjectionZone = (area)=>{
const getPlugin = useStrapiApp('useInjectionZone', (state)=>state.getPlugin);
const contentManagerPlugin = getPlugin(PLUGIN_ID);
const [page, position] = area.split('.');
return contentManagerPlugin.getInjectedComponents(page, position);
};
export { INJECTION_ZONES, InjectionZone, useInjectionZone };
//# sourceMappingURL=InjectionZone.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"InjectionZone.mjs","sources":["../../../admin/src/components/InjectionZone.tsx"],"sourcesContent":["import { useStrapiApp, InjectionZoneComponent } from '@strapi/admin/strapi-admin';\n\nimport { PLUGIN_ID } from '../constants/plugin';\n\nconst INJECTION_ZONES = {\n editView: { informations: [], 'right-links': [] },\n listView: {\n actions: [],\n deleteModalAdditionalInfos: [],\n publishModalAdditionalInfos: [],\n unpublishModalAdditionalInfos: [],\n },\n preview: {\n actions: [],\n },\n} satisfies InjectionZones;\n\ninterface InjectionZones {\n editView: {\n informations: InjectionZoneComponent[];\n 'right-links': InjectionZoneComponent[];\n };\n listView: {\n actions: InjectionZoneComponent[];\n deleteModalAdditionalInfos: InjectionZoneComponent[];\n publishModalAdditionalInfos: InjectionZoneComponent[];\n unpublishModalAdditionalInfos: InjectionZoneComponent[];\n };\n preview: {\n actions: InjectionZoneComponent[];\n };\n}\n\ntype InjectionZoneArea =\n | 'editView.informations'\n | 'editView.right-links'\n | 'listView.actions'\n | 'listView.unpublishModalAdditionalInfos'\n | 'listView.deleteModalAdditionalInfos'\n | 'listView.publishModalAdditionalInfos'\n | 'listView.deleteModalAdditionalInfos'\n | 'preview.actions';\n\ntype InjectionZoneModule = InjectionZoneArea extends `${infer Word}.${string}` ? Word : never;\ntype InjectionZoneContainer = InjectionZoneArea extends `${string}.${infer Word}.${string}`\n ? Word\n : never;\ntype InjectionZoneBlock = InjectionZoneArea extends `${string}.${string}.${infer Word}`\n ? Word\n : never;\n\n/**\n * You can't know what this component props will be because it's generic and used everywhere\n * e.g. content-manager edit view, we just send the slug but we might not in the listView,\n * therefore, people should type it themselves on the components they render.\n */\nconst InjectionZone = ({ area, ...props }: { area: InjectionZoneArea; [key: string]: unknown }) => {\n const components = useInjectionZone(area);\n\n return (\n <>\n {components.map((component) => (\n <component.Component key={component.name} {...props} />\n ))}\n </>\n );\n};\n\nexport const useInjectionZone = (area: InjectionZoneArea) => {\n const getPlugin = useStrapiApp('useInjectionZone', (state) => state.getPlugin);\n const contentManagerPlugin = getPlugin(PLUGIN_ID);\n const [page, position] = area.split('.') as [InjectionZoneContainer, InjectionZoneBlock];\n\n return contentManagerPlugin.getInjectedComponents(page, position);\n};\n\nexport { InjectionZone, INJECTION_ZONES };\n\nexport type {\n InjectionZoneArea,\n InjectionZoneComponent,\n InjectionZones,\n InjectionZoneModule,\n InjectionZoneContainer,\n InjectionZoneBlock,\n};\n"],"names":["INJECTION_ZONES","editView","informations","listView","actions","deleteModalAdditionalInfos","publishModalAdditionalInfos","unpublishModalAdditionalInfos","preview","InjectionZone","area","props","components","useInjectionZone","_jsx","_Fragment","map","component","Component","name","getPlugin","useStrapiApp","state","contentManagerPlugin","PLUGIN_ID","page","position","split","getInjectedComponents"],"mappings":";;;;AAIA,MAAMA,eAAkB,GAAA;IACtBC,QAAU,EAAA;AAAEC,QAAAA,YAAAA,EAAc,EAAE;AAAE,QAAA,aAAA,EAAe;AAAG,KAAA;IAChDC,QAAU,EAAA;AACRC,QAAAA,OAAAA,EAAS,EAAE;AACXC,QAAAA,0BAAAA,EAA4B,EAAE;AAC9BC,QAAAA,2BAAAA,EAA6B,EAAE;AAC/BC,QAAAA,6BAAAA,EAA+B;AACjC,KAAA;IACAC,OAAS,EAAA;AACPJ,QAAAA,OAAAA,EAAS;AACX;AACF;AAoCA;;;;AAIC,UACKK,aAAgB,GAAA,CAAC,EAAEC,IAAI,EAAE,GAAGC,KAA4D,EAAA,GAAA;AAC5F,IAAA,MAAMC,aAAaC,gBAAiBH,CAAAA,IAAAA,CAAAA;IAEpC,qBACEI,GAAA,CAAAC,QAAA,EAAA;AACGH,QAAAA,QAAAA,EAAAA,UAAAA,CAAWI,GAAG,CAAC,CAACC,SACf,iBAAAH,GAAA,CAACG,UAAUC,SAAS,EAAA;AAAuB,gBAAA,GAAGP;AAApBM,aAAAA,EAAAA,SAAAA,CAAUE,IAAI,CAAA;;AAIhD;AAEO,MAAMN,mBAAmB,CAACH,IAAAA,GAAAA;AAC/B,IAAA,MAAMU,YAAYC,YAAa,CAAA,kBAAA,EAAoB,CAACC,KAAAA,GAAUA,MAAMF,SAAS,CAAA;AAC7E,IAAA,MAAMG,uBAAuBH,SAAUI,CAAAA,SAAAA,CAAAA;AACvC,IAAA,MAAM,CAACC,IAAMC,EAAAA,QAAAA,CAAS,GAAGhB,IAAAA,CAAKiB,KAAK,CAAC,GAAA,CAAA;IAEpC,OAAOJ,oBAAAA,CAAqBK,qBAAqB,CAACH,IAAMC,EAAAA,QAAAA,CAAAA;AAC1D;;;;"}

View File

@@ -0,0 +1,172 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var useContentTypeSchema = require('../hooks/useContentTypeSchema.js');
var hooks = require('../modules/hooks.js');
var translations = require('../utils/translations.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const SubNavLinkCustom = styledComponents.styled(designSystem.SubNavLink)`
div {
width: inherit;
span:nth-child(2) {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: inherit;
}
}
`;
const LeftMenu = ()=>{
const [search, setSearch] = React__namespace.useState('');
const [{ query }] = strapiAdmin.useQueryParams();
const { formatMessage, locale } = reactIntl.useIntl();
const collectionTypeLinks = hooks.useTypedSelector((state)=>state['content-manager'].app.collectionTypeLinks);
const singleTypeLinks = hooks.useTypedSelector((state)=>state['content-manager'].app.singleTypeLinks);
const { schemas } = useContentTypeSchema.useContentTypeSchema();
const { startsWith } = designSystem.useFilter(locale, {
sensitivity: 'base'
});
const formatter = designSystem.useCollator(locale, {
sensitivity: 'base'
});
const menu = React__namespace.useMemo(()=>[
{
id: 'collectionTypes',
title: formatMessage({
id: translations.getTranslation('components.LeftMenu.collection-types'),
defaultMessage: 'Collection Types'
}),
searchable: true,
links: collectionTypeLinks
},
{
id: 'singleTypes',
title: formatMessage({
id: translations.getTranslation('components.LeftMenu.single-types'),
defaultMessage: 'Single Types'
}),
searchable: true,
links: singleTypeLinks
}
].map((section)=>({
...section,
links: section.links/**
* Filter by the search value
*/ .filter((link)=>startsWith(link.title, search))/**
* Sort correctly using the language
*/ .sort((a, b)=>formatter.compare(a.title, b.title))/**
* Apply the formated strings to the links from react-intl
*/ .map((link)=>{
return {
...link,
title: formatMessage({
id: link.title,
defaultMessage: link.title
})
};
})
})), [
collectionTypeLinks,
search,
singleTypeLinks,
startsWith,
formatMessage,
formatter
]);
const handleClear = ()=>{
setSearch('');
};
const handleChangeSearch = ({ target: { value } })=>{
setSearch(value);
};
const label = formatMessage({
id: translations.getTranslation('header.name'),
defaultMessage: 'Content Manager'
});
const getPluginsParamsForLink = (link)=>{
const schema = schemas.find((schema)=>schema.uid === link.uid);
const isI18nEnabled = Boolean(schema?.pluginOptions?.i18n?.localized);
// The search params have the i18n plugin
if (query.plugins && 'i18n' in query.plugins) {
// Prepare removal of i18n from the plugins search params
const { i18n, ...restPlugins } = query.plugins;
// i18n is not enabled, remove it from the plugins search params
if (!isI18nEnabled) {
return restPlugins;
}
// i18n is enabled, put the plugins search params back together
return {
i18n,
...restPlugins
};
}
return query.plugins;
};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.SubNav, {
"aria-label": label,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SubNavHeader, {
label: label,
searchable: true,
value: search,
onChange: handleChangeSearch,
onClear: handleClear,
searchLabel: formatMessage({
id: 'content-manager.components.LeftMenu.Search.label',
defaultMessage: 'Search for a content type'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SubNavSections, {
children: menu.map((section)=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SubNavSection, {
label: section.title,
badgeLabel: section.links.length.toString(),
children: section.links.map((link)=>{
return /*#__PURE__*/ jsxRuntime.jsx(SubNavLinkCustom, {
tag: reactRouterDom.NavLink,
to: {
pathname: link.to,
search: qs.stringify({
...qs.parse(link.search ?? ''),
plugins: getPluginsParamsForLink(link)
})
},
width: "100%",
children: link.title
}, link.uid);
})
}, section.id);
})
})
]
});
};
exports.LeftMenu = LeftMenu;
//# sourceMappingURL=LeftMenu.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,151 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useQueryParams } from '@strapi/admin/strapi-admin';
import { SubNavLink, useFilter, useCollator, SubNav, SubNavHeader, SubNavSections, SubNavSection } from '@strapi/design-system';
import { stringify, parse } from 'qs';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
import { useContentTypeSchema } from '../hooks/useContentTypeSchema.mjs';
import { useTypedSelector } from '../modules/hooks.mjs';
import { getTranslation } from '../utils/translations.mjs';
const SubNavLinkCustom = styled(SubNavLink)`
div {
width: inherit;
span:nth-child(2) {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: inherit;
}
}
`;
const LeftMenu = ()=>{
const [search, setSearch] = React.useState('');
const [{ query }] = useQueryParams();
const { formatMessage, locale } = useIntl();
const collectionTypeLinks = useTypedSelector((state)=>state['content-manager'].app.collectionTypeLinks);
const singleTypeLinks = useTypedSelector((state)=>state['content-manager'].app.singleTypeLinks);
const { schemas } = useContentTypeSchema();
const { startsWith } = useFilter(locale, {
sensitivity: 'base'
});
const formatter = useCollator(locale, {
sensitivity: 'base'
});
const menu = React.useMemo(()=>[
{
id: 'collectionTypes',
title: formatMessage({
id: getTranslation('components.LeftMenu.collection-types'),
defaultMessage: 'Collection Types'
}),
searchable: true,
links: collectionTypeLinks
},
{
id: 'singleTypes',
title: formatMessage({
id: getTranslation('components.LeftMenu.single-types'),
defaultMessage: 'Single Types'
}),
searchable: true,
links: singleTypeLinks
}
].map((section)=>({
...section,
links: section.links/**
* Filter by the search value
*/ .filter((link)=>startsWith(link.title, search))/**
* Sort correctly using the language
*/ .sort((a, b)=>formatter.compare(a.title, b.title))/**
* Apply the formated strings to the links from react-intl
*/ .map((link)=>{
return {
...link,
title: formatMessage({
id: link.title,
defaultMessage: link.title
})
};
})
})), [
collectionTypeLinks,
search,
singleTypeLinks,
startsWith,
formatMessage,
formatter
]);
const handleClear = ()=>{
setSearch('');
};
const handleChangeSearch = ({ target: { value } })=>{
setSearch(value);
};
const label = formatMessage({
id: getTranslation('header.name'),
defaultMessage: 'Content Manager'
});
const getPluginsParamsForLink = (link)=>{
const schema = schemas.find((schema)=>schema.uid === link.uid);
const isI18nEnabled = Boolean(schema?.pluginOptions?.i18n?.localized);
// The search params have the i18n plugin
if (query.plugins && 'i18n' in query.plugins) {
// Prepare removal of i18n from the plugins search params
const { i18n, ...restPlugins } = query.plugins;
// i18n is not enabled, remove it from the plugins search params
if (!isI18nEnabled) {
return restPlugins;
}
// i18n is enabled, put the plugins search params back together
return {
i18n,
...restPlugins
};
}
return query.plugins;
};
return /*#__PURE__*/ jsxs(SubNav, {
"aria-label": label,
children: [
/*#__PURE__*/ jsx(SubNavHeader, {
label: label,
searchable: true,
value: search,
onChange: handleChangeSearch,
onClear: handleClear,
searchLabel: formatMessage({
id: 'content-manager.components.LeftMenu.Search.label',
defaultMessage: 'Search for a content type'
})
}),
/*#__PURE__*/ jsx(SubNavSections, {
children: menu.map((section)=>{
return /*#__PURE__*/ jsx(SubNavSection, {
label: section.title,
badgeLabel: section.links.length.toString(),
children: section.links.map((link)=>{
return /*#__PURE__*/ jsx(SubNavLinkCustom, {
tag: NavLink,
to: {
pathname: link.to,
search: stringify({
...parse(link.search ?? ''),
plugins: getPluginsParamsForLink(link)
})
},
width: "100%",
children: link.title
}, link.uid);
})
}, section.id);
})
})
]
});
};
export { LeftMenu };
//# sourceMappingURL=LeftMenu.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var dateFns = require('date-fns');
var reactIntl = require('react-intl');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const intervals = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds'
];
/**
* Displays the relative time between a given timestamp and the current time.
* You can display a custom message for given time intervals by passing an array of custom intervals.
*
* @example
* ```jsx
* <caption>Display "last hour" if the timestamp is less than an hour ago</caption>
* <RelativeTime
* timestamp={new Date('2021-01-01')}
* customIntervals={[
* { unit: 'hours', threshold: 1, text: 'last hour' },
* ]}
* ```
*/ const RelativeTime = /*#__PURE__*/ React__namespace.forwardRef(({ timestamp, customIntervals = [], ...restProps }, forwardedRef)=>{
const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
/**
* TODO: make this auto-update, like a clock.
*/ const interval = dateFns.intervalToDuration({
start: timestamp,
end: Date.now()
});
const unit = intervals.find((intervalUnit)=>{
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
}) ?? 'seconds';
const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
// Display custom text if interval is less than the threshold
const customInterval = customIntervals.find((custom)=>interval[custom.unit] < custom.threshold);
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, {
numeric: 'auto'
});
return /*#__PURE__*/ jsxRuntime.jsx("time", {
ref: forwardedRef,
dateTime: timestamp.toISOString(),
role: "time",
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
...restProps,
children: displayText
});
});
exports.RelativeTime = RelativeTime;
//# sourceMappingURL=RelativeTime.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelativeTime.js","sources":["../../../admin/src/components/RelativeTime.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Duration, intervalToDuration, isPast } from 'date-fns';\nimport { useIntl } from 'react-intl';\n\nconst intervals: Array<keyof Duration> = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];\n\ninterface CustomInterval {\n unit: keyof Duration;\n text: string;\n threshold: number;\n}\n\ninterface RelativeTimeProps extends React.ComponentPropsWithoutRef<'time'> {\n timestamp: Date;\n customIntervals?: CustomInterval[];\n}\n\n/**\n * Displays the relative time between a given timestamp and the current time.\n * You can display a custom message for given time intervals by passing an array of custom intervals.\n *\n * @example\n * ```jsx\n * <caption>Display \"last hour\" if the timestamp is less than an hour ago</caption>\n * <RelativeTime\n * timestamp={new Date('2021-01-01')}\n * customIntervals={[\n * { unit: 'hours', threshold: 1, text: 'last hour' },\n * ]}\n * ```\n */\nconst RelativeTime = React.forwardRef<HTMLTimeElement, RelativeTimeProps>(\n ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {\n const { formatRelativeTime, formatDate, formatTime } = useIntl();\n\n /**\n * TODO: make this auto-update, like a clock.\n */\n const interval = intervalToDuration({\n start: timestamp,\n end: Date.now(),\n // see https://github.com/date-fns/date-fns/issues/2891 No idea why it's all partial it returns it every time.\n }) as Required<Duration>;\n\n const unit =\n intervals.find((intervalUnit) => {\n return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);\n }) ?? 'seconds';\n\n const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];\n\n // Display custom text if interval is less than the threshold\n const customInterval = customIntervals.find(\n (custom) => interval[custom.unit] < custom.threshold\n );\n\n const displayText = customInterval\n ? customInterval.text\n : formatRelativeTime(relativeTime, unit, { numeric: 'auto' });\n\n return (\n <time\n ref={forwardedRef}\n dateTime={timestamp.toISOString()}\n role=\"time\"\n title={`${formatDate(timestamp)} ${formatTime(timestamp)}`}\n {...restProps}\n >\n {displayText}\n </time>\n );\n }\n);\n\nexport { RelativeTime };\nexport type { CustomInterval, RelativeTimeProps };\n"],"names":["intervals","RelativeTime","React","forwardRef","timestamp","customIntervals","restProps","forwardedRef","formatRelativeTime","formatDate","formatTime","useIntl","interval","intervalToDuration","start","end","Date","now","unit","find","intervalUnit","Object","keys","includes","relativeTime","isPast","customInterval","custom","threshold","displayText","text","numeric","_jsx","time","ref","dateTime","toISOString","role","title"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,MAAMA,SAAmC,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,OAAA;AAAS,IAAA,SAAA;AAAW,IAAA;AAAU,CAAA;AAanG;;;;;;;;;;;;;AAaC,IACKC,MAAAA,YAAAA,iBAAeC,gBAAMC,CAAAA,UAAU,CACnC,CAAC,EAAEC,SAAS,EAAEC,eAAkB,GAAA,EAAE,EAAE,GAAGC,WAAW,EAAEC,YAAAA,GAAAA;AAClD,IAAA,MAAM,EAAEC,kBAAkB,EAAEC,UAAU,EAAEC,UAAU,EAAE,GAAGC,iBAAAA,EAAAA;AAEvD;;QAGA,MAAMC,WAAWC,0BAAmB,CAAA;QAClCC,KAAOV,EAAAA,SAAAA;AACPW,QAAAA,GAAAA,EAAKC,KAAKC,GAAG;AAEf,KAAA,CAAA;AAEA,IAAA,MAAMC,IACJlB,GAAAA,SAAAA,CAAUmB,IAAI,CAAC,CAACC,YAAAA,GAAAA;QACd,OAAOR,QAAQ,CAACQ,YAAAA,CAAa,GAAG,CAAA,IAAKC,OAAOC,IAAI,CAACV,QAAUW,CAAAA,CAAAA,QAAQ,CAACH,YAAAA,CAAAA;KAChE,CAAA,IAAA,SAAA;IAER,MAAMI,YAAAA,GAAeC,cAAOrB,CAAAA,SAAAA,CAAAA,GAAa,CAACQ,QAAQ,CAACM,IAAK,CAAA,GAAGN,QAAQ,CAACM,IAAK,CAAA;;AAGzE,IAAA,MAAMQ,cAAiBrB,GAAAA,eAAAA,CAAgBc,IAAI,CACzC,CAACQ,MAAAA,GAAWf,QAAQ,CAACe,MAAOT,CAAAA,IAAI,CAAC,GAAGS,OAAOC,SAAS,CAAA;AAGtD,IAAA,MAAMC,cAAcH,cAChBA,GAAAA,cAAAA,CAAeI,IAAI,GACnBtB,kBAAAA,CAAmBgB,cAAcN,IAAM,EAAA;QAAEa,OAAS,EAAA;AAAO,KAAA,CAAA;AAE7D,IAAA,qBACEC,cAACC,CAAAA,MAAAA,EAAAA;QACCC,GAAK3B,EAAAA,YAAAA;AACL4B,QAAAA,QAAAA,EAAU/B,UAAUgC,WAAW,EAAA;QAC/BC,IAAK,EAAA,MAAA;QACLC,KAAO,EAAA,CAAC,EAAE7B,UAAWL,CAAAA,SAAAA,CAAAA,CAAW,CAAC,EAAEM,UAAAA,CAAWN,WAAW,CAAC;AACzD,QAAA,GAAGE,SAAS;AAEZuB,QAAAA,QAAAA,EAAAA;;AAGP,CAAA;;;;"}

View File

@@ -0,0 +1,55 @@
import { jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { intervalToDuration, isPast } from 'date-fns';
import { useIntl } from 'react-intl';
const intervals = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds'
];
/**
* Displays the relative time between a given timestamp and the current time.
* You can display a custom message for given time intervals by passing an array of custom intervals.
*
* @example
* ```jsx
* <caption>Display "last hour" if the timestamp is less than an hour ago</caption>
* <RelativeTime
* timestamp={new Date('2021-01-01')}
* customIntervals={[
* { unit: 'hours', threshold: 1, text: 'last hour' },
* ]}
* ```
*/ const RelativeTime = /*#__PURE__*/ React.forwardRef(({ timestamp, customIntervals = [], ...restProps }, forwardedRef)=>{
const { formatRelativeTime, formatDate, formatTime } = useIntl();
/**
* TODO: make this auto-update, like a clock.
*/ const interval = intervalToDuration({
start: timestamp,
end: Date.now()
});
const unit = intervals.find((intervalUnit)=>{
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
}) ?? 'seconds';
const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
// Display custom text if interval is less than the threshold
const customInterval = customIntervals.find((custom)=>interval[custom.unit] < custom.threshold);
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, {
numeric: 'auto'
});
return /*#__PURE__*/ jsx("time", {
ref: forwardedRef,
dateTime: timestamp.toISOString(),
role: "time",
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
...restProps,
children: displayText
});
});
export { RelativeTime };
//# sourceMappingURL=RelativeTime.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelativeTime.mjs","sources":["../../../admin/src/components/RelativeTime.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Duration, intervalToDuration, isPast } from 'date-fns';\nimport { useIntl } from 'react-intl';\n\nconst intervals: Array<keyof Duration> = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];\n\ninterface CustomInterval {\n unit: keyof Duration;\n text: string;\n threshold: number;\n}\n\ninterface RelativeTimeProps extends React.ComponentPropsWithoutRef<'time'> {\n timestamp: Date;\n customIntervals?: CustomInterval[];\n}\n\n/**\n * Displays the relative time between a given timestamp and the current time.\n * You can display a custom message for given time intervals by passing an array of custom intervals.\n *\n * @example\n * ```jsx\n * <caption>Display \"last hour\" if the timestamp is less than an hour ago</caption>\n * <RelativeTime\n * timestamp={new Date('2021-01-01')}\n * customIntervals={[\n * { unit: 'hours', threshold: 1, text: 'last hour' },\n * ]}\n * ```\n */\nconst RelativeTime = React.forwardRef<HTMLTimeElement, RelativeTimeProps>(\n ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {\n const { formatRelativeTime, formatDate, formatTime } = useIntl();\n\n /**\n * TODO: make this auto-update, like a clock.\n */\n const interval = intervalToDuration({\n start: timestamp,\n end: Date.now(),\n // see https://github.com/date-fns/date-fns/issues/2891 No idea why it's all partial it returns it every time.\n }) as Required<Duration>;\n\n const unit =\n intervals.find((intervalUnit) => {\n return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);\n }) ?? 'seconds';\n\n const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];\n\n // Display custom text if interval is less than the threshold\n const customInterval = customIntervals.find(\n (custom) => interval[custom.unit] < custom.threshold\n );\n\n const displayText = customInterval\n ? customInterval.text\n : formatRelativeTime(relativeTime, unit, { numeric: 'auto' });\n\n return (\n <time\n ref={forwardedRef}\n dateTime={timestamp.toISOString()}\n role=\"time\"\n title={`${formatDate(timestamp)} ${formatTime(timestamp)}`}\n {...restProps}\n >\n {displayText}\n </time>\n );\n }\n);\n\nexport { RelativeTime };\nexport type { CustomInterval, RelativeTimeProps };\n"],"names":["intervals","RelativeTime","React","forwardRef","timestamp","customIntervals","restProps","forwardedRef","formatRelativeTime","formatDate","formatTime","useIntl","interval","intervalToDuration","start","end","Date","now","unit","find","intervalUnit","Object","keys","includes","relativeTime","isPast","customInterval","custom","threshold","displayText","text","numeric","_jsx","time","ref","dateTime","toISOString","role","title"],"mappings":";;;;;AAKA,MAAMA,SAAmC,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,OAAA;AAAS,IAAA,SAAA;AAAW,IAAA;AAAU,CAAA;AAanG;;;;;;;;;;;;;AAaC,IACKC,MAAAA,YAAAA,iBAAeC,KAAMC,CAAAA,UAAU,CACnC,CAAC,EAAEC,SAAS,EAAEC,eAAkB,GAAA,EAAE,EAAE,GAAGC,WAAW,EAAEC,YAAAA,GAAAA;AAClD,IAAA,MAAM,EAAEC,kBAAkB,EAAEC,UAAU,EAAEC,UAAU,EAAE,GAAGC,OAAAA,EAAAA;AAEvD;;QAGA,MAAMC,WAAWC,kBAAmB,CAAA;QAClCC,KAAOV,EAAAA,SAAAA;AACPW,QAAAA,GAAAA,EAAKC,KAAKC,GAAG;AAEf,KAAA,CAAA;AAEA,IAAA,MAAMC,IACJlB,GAAAA,SAAAA,CAAUmB,IAAI,CAAC,CAACC,YAAAA,GAAAA;QACd,OAAOR,QAAQ,CAACQ,YAAAA,CAAa,GAAG,CAAA,IAAKC,OAAOC,IAAI,CAACV,QAAUW,CAAAA,CAAAA,QAAQ,CAACH,YAAAA,CAAAA;KAChE,CAAA,IAAA,SAAA;IAER,MAAMI,YAAAA,GAAeC,MAAOrB,CAAAA,SAAAA,CAAAA,GAAa,CAACQ,QAAQ,CAACM,IAAK,CAAA,GAAGN,QAAQ,CAACM,IAAK,CAAA;;AAGzE,IAAA,MAAMQ,cAAiBrB,GAAAA,eAAAA,CAAgBc,IAAI,CACzC,CAACQ,MAAAA,GAAWf,QAAQ,CAACe,MAAOT,CAAAA,IAAI,CAAC,GAAGS,OAAOC,SAAS,CAAA;AAGtD,IAAA,MAAMC,cAAcH,cAChBA,GAAAA,cAAAA,CAAeI,IAAI,GACnBtB,kBAAAA,CAAmBgB,cAAcN,IAAM,EAAA;QAAEa,OAAS,EAAA;AAAO,KAAA,CAAA;AAE7D,IAAA,qBACEC,GAACC,CAAAA,MAAAA,EAAAA;QACCC,GAAK3B,EAAAA,YAAAA;AACL4B,QAAAA,QAAAA,EAAU/B,UAAUgC,WAAW,EAAA;QAC/BC,IAAK,EAAA,MAAA;QACLC,KAAO,EAAA,CAAC,EAAE7B,UAAWL,CAAAA,SAAAA,CAAAA,CAAW,CAAC,EAAEM,UAAAA,CAAWN,WAAW,CAAC;AACzD,QAAA,GAAGE,SAAS;AAEZuB,QAAAA,QAAAA,EAAAA;;AAGP,CAAA;;;;"}

View File

@@ -0,0 +1,161 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var DocumentStatus = require('../pages/EditView/components/DocumentStatus.js');
var homepage = require('../services/homepage.js');
var RelativeTime = require('./RelativeTime.js');
const CellTypography = styledComponents.styled(designSystem.Typography).attrs({
maxWidth: '14.4rem',
display: 'block'
})`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const RecentDocumentsTable = ({ documents })=>{
const { formatMessage } = reactIntl.useIntl();
const { trackUsage } = strapiAdmin.useTracking();
const navigate = reactRouterDom.useNavigate();
const getEditViewLink = (document)=>{
const isSingleType = document.kind === 'singleType';
const kindPath = isSingleType ? 'single-types' : 'collection-types';
const queryParams = document.locale ? `?plugins[i18n][locale]=${document.locale}` : '';
return `/content-manager/${kindPath}/${document.contentTypeUid}${isSingleType ? '' : '/' + document.documentId}${queryParams}`;
};
const handleRowClick = (document)=>()=>{
trackUsage('willEditEntryFromHome');
const link = getEditViewLink(document);
navigate(link);
};
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Table, {
colCount: 5,
rowCount: documents?.length ?? 0,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tbody, {
children: documents?.map((document)=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Tr, {
onClick: handleRowClick(document),
cursor: "pointer",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
children: /*#__PURE__*/ jsxRuntime.jsx(CellTypography, {
title: document.title,
variant: "omega",
textColor: "neutral800",
children: document.title
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
children: /*#__PURE__*/ jsxRuntime.jsx(CellTypography, {
variant: "omega",
textColor: "neutral600",
children: document.kind === 'singleType' ? formatMessage({
id: 'content-manager.widget.last-edited.single-type',
defaultMessage: 'Single-Type'
}) : formatMessage({
id: document.contentTypeDisplayName,
defaultMessage: document.contentTypeDisplayName
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
display: "inline-block",
children: document.status ? /*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: document.status
}) : /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral600",
"aria-hidden": true,
children: "-"
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral600",
children: /*#__PURE__*/ jsxRuntime.jsx(RelativeTime.RelativeTime, {
timestamp: new Date(document.updatedAt)
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
onClick: (e)=>e.stopPropagation(),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
display: "inline-block",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
tag: reactRouterDom.Link,
to: getEditViewLink(document),
onClick: ()=>trackUsage('willEditEntryFromHome'),
label: formatMessage({
id: 'content-manager.actions.edit.label',
defaultMessage: 'Edit'
}),
variant: "ghost",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.Pencil, {})
})
})
})
]
}, document.documentId))
})
});
};
/* -------------------------------------------------------------------------------------------------
* LastEditedWidget
* -----------------------------------------------------------------------------------------------*/ const LastEditedWidget = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { data, isLoading, error } = homepage.useGetRecentDocumentsQuery({
action: 'update'
});
if (isLoading) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.Loading, {});
}
if (error || !data) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.Error, {});
}
if (data.length === 0) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.NoData, {
children: formatMessage({
id: 'content-manager.widget.last-edited.no-data',
defaultMessage: 'No edited entries'
})
});
}
return /*#__PURE__*/ jsxRuntime.jsx(RecentDocumentsTable, {
documents: data
});
};
/* -------------------------------------------------------------------------------------------------
* LastPublishedWidget
* -----------------------------------------------------------------------------------------------*/ const LastPublishedWidget = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { data, isLoading, error } = homepage.useGetRecentDocumentsQuery({
action: 'publish'
});
if (isLoading) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.Loading, {});
}
if (error || !data) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.Error, {});
}
if (data.length === 0) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Widget.NoData, {
children: formatMessage({
id: 'content-manager.widget.last-published.no-data',
defaultMessage: 'No published entries'
})
});
}
return /*#__PURE__*/ jsxRuntime.jsx(RecentDocumentsTable, {
documents: data
});
};
exports.LastEditedWidget = LastEditedWidget;
exports.LastPublishedWidget = LastPublishedWidget;
//# sourceMappingURL=Widgets.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,158 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { Widget, useTracking } from '@strapi/admin/strapi-admin';
import { Typography, Table, Tbody, Tr, Td, Box, IconButton } from '@strapi/design-system';
import { Pencil } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { useNavigate, Link } from 'react-router-dom';
import { styled } from 'styled-components';
import { DocumentStatus } from '../pages/EditView/components/DocumentStatus.mjs';
import { useGetRecentDocumentsQuery } from '../services/homepage.mjs';
import { RelativeTime } from './RelativeTime.mjs';
const CellTypography = styled(Typography).attrs({
maxWidth: '14.4rem',
display: 'block'
})`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const RecentDocumentsTable = ({ documents })=>{
const { formatMessage } = useIntl();
const { trackUsage } = useTracking();
const navigate = useNavigate();
const getEditViewLink = (document)=>{
const isSingleType = document.kind === 'singleType';
const kindPath = isSingleType ? 'single-types' : 'collection-types';
const queryParams = document.locale ? `?plugins[i18n][locale]=${document.locale}` : '';
return `/content-manager/${kindPath}/${document.contentTypeUid}${isSingleType ? '' : '/' + document.documentId}${queryParams}`;
};
const handleRowClick = (document)=>()=>{
trackUsage('willEditEntryFromHome');
const link = getEditViewLink(document);
navigate(link);
};
return /*#__PURE__*/ jsx(Table, {
colCount: 5,
rowCount: documents?.length ?? 0,
children: /*#__PURE__*/ jsx(Tbody, {
children: documents?.map((document)=>/*#__PURE__*/ jsxs(Tr, {
onClick: handleRowClick(document),
cursor: "pointer",
children: [
/*#__PURE__*/ jsx(Td, {
children: /*#__PURE__*/ jsx(CellTypography, {
title: document.title,
variant: "omega",
textColor: "neutral800",
children: document.title
})
}),
/*#__PURE__*/ jsx(Td, {
children: /*#__PURE__*/ jsx(CellTypography, {
variant: "omega",
textColor: "neutral600",
children: document.kind === 'singleType' ? formatMessage({
id: 'content-manager.widget.last-edited.single-type',
defaultMessage: 'Single-Type'
}) : formatMessage({
id: document.contentTypeDisplayName,
defaultMessage: document.contentTypeDisplayName
})
})
}),
/*#__PURE__*/ jsx(Td, {
children: /*#__PURE__*/ jsx(Box, {
display: "inline-block",
children: document.status ? /*#__PURE__*/ jsx(DocumentStatus, {
status: document.status
}) : /*#__PURE__*/ jsx(Typography, {
textColor: "neutral600",
"aria-hidden": true,
children: "-"
})
})
}),
/*#__PURE__*/ jsx(Td, {
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral600",
children: /*#__PURE__*/ jsx(RelativeTime, {
timestamp: new Date(document.updatedAt)
})
})
}),
/*#__PURE__*/ jsx(Td, {
onClick: (e)=>e.stopPropagation(),
children: /*#__PURE__*/ jsx(Box, {
display: "inline-block",
children: /*#__PURE__*/ jsx(IconButton, {
tag: Link,
to: getEditViewLink(document),
onClick: ()=>trackUsage('willEditEntryFromHome'),
label: formatMessage({
id: 'content-manager.actions.edit.label',
defaultMessage: 'Edit'
}),
variant: "ghost",
children: /*#__PURE__*/ jsx(Pencil, {})
})
})
})
]
}, document.documentId))
})
});
};
/* -------------------------------------------------------------------------------------------------
* LastEditedWidget
* -----------------------------------------------------------------------------------------------*/ const LastEditedWidget = ()=>{
const { formatMessage } = useIntl();
const { data, isLoading, error } = useGetRecentDocumentsQuery({
action: 'update'
});
if (isLoading) {
return /*#__PURE__*/ jsx(Widget.Loading, {});
}
if (error || !data) {
return /*#__PURE__*/ jsx(Widget.Error, {});
}
if (data.length === 0) {
return /*#__PURE__*/ jsx(Widget.NoData, {
children: formatMessage({
id: 'content-manager.widget.last-edited.no-data',
defaultMessage: 'No edited entries'
})
});
}
return /*#__PURE__*/ jsx(RecentDocumentsTable, {
documents: data
});
};
/* -------------------------------------------------------------------------------------------------
* LastPublishedWidget
* -----------------------------------------------------------------------------------------------*/ const LastPublishedWidget = ()=>{
const { formatMessage } = useIntl();
const { data, isLoading, error } = useGetRecentDocumentsQuery({
action: 'publish'
});
if (isLoading) {
return /*#__PURE__*/ jsx(Widget.Loading, {});
}
if (error || !data) {
return /*#__PURE__*/ jsx(Widget.Error, {});
}
if (data.length === 0) {
return /*#__PURE__*/ jsx(Widget.NoData, {
children: formatMessage({
id: 'content-manager.widget.last-published.no-data',
defaultMessage: 'No published entries'
})
});
}
return /*#__PURE__*/ jsx(RecentDocumentsTable, {
documents: data
});
};
export { LastEditedWidget, LastPublishedWidget };
//# sourceMappingURL=Widgets.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
'use strict';
const ID = 'id';
const CREATED_BY_ATTRIBUTE_NAME = 'createdBy';
const UPDATED_BY_ATTRIBUTE_NAME = 'updatedBy';
const CREATOR_FIELDS = [
CREATED_BY_ATTRIBUTE_NAME,
UPDATED_BY_ATTRIBUTE_NAME
];
const PUBLISHED_BY_ATTRIBUTE_NAME = 'publishedBy';
const CREATED_AT_ATTRIBUTE_NAME = 'createdAt';
const UPDATED_AT_ATTRIBUTE_NAME = 'updatedAt';
const PUBLISHED_AT_ATTRIBUTE_NAME = 'publishedAt';
const DOCUMENT_META_FIELDS = [
ID,
...CREATOR_FIELDS,
PUBLISHED_BY_ATTRIBUTE_NAME,
CREATED_AT_ATTRIBUTE_NAME,
UPDATED_AT_ATTRIBUTE_NAME,
PUBLISHED_AT_ATTRIBUTE_NAME
];
/**
* List of attribute types that cannot be used as the main field.
* Not sure the name could be any clearer.
*/ const ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = [
'dynamiczone',
'json',
'text',
'relation',
'component',
'boolean',
'media',
'password',
'richtext',
'timestamp',
'blocks'
];
exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
exports.CREATED_AT_ATTRIBUTE_NAME = CREATED_AT_ATTRIBUTE_NAME;
exports.CREATED_BY_ATTRIBUTE_NAME = CREATED_BY_ATTRIBUTE_NAME;
exports.CREATOR_FIELDS = CREATOR_FIELDS;
exports.DOCUMENT_META_FIELDS = DOCUMENT_META_FIELDS;
exports.PUBLISHED_AT_ATTRIBUTE_NAME = PUBLISHED_AT_ATTRIBUTE_NAME;
exports.PUBLISHED_BY_ATTRIBUTE_NAME = PUBLISHED_BY_ATTRIBUTE_NAME;
exports.UPDATED_AT_ATTRIBUTE_NAME = UPDATED_AT_ATTRIBUTE_NAME;
exports.UPDATED_BY_ATTRIBUTE_NAME = UPDATED_BY_ATTRIBUTE_NAME;
//# sourceMappingURL=attributes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"attributes.js","sources":["../../../admin/src/constants/attributes.ts"],"sourcesContent":["const ID = 'id';\n\nconst CREATED_BY_ATTRIBUTE_NAME = 'createdBy';\nconst UPDATED_BY_ATTRIBUTE_NAME = 'updatedBy';\n\nconst CREATOR_FIELDS = [CREATED_BY_ATTRIBUTE_NAME, UPDATED_BY_ATTRIBUTE_NAME];\n\nconst PUBLISHED_BY_ATTRIBUTE_NAME = 'publishedBy';\nconst CREATED_AT_ATTRIBUTE_NAME = 'createdAt';\nconst UPDATED_AT_ATTRIBUTE_NAME = 'updatedAt';\nconst PUBLISHED_AT_ATTRIBUTE_NAME = 'publishedAt';\n\nconst DOCUMENT_META_FIELDS = [\n ID,\n ...CREATOR_FIELDS,\n PUBLISHED_BY_ATTRIBUTE_NAME,\n CREATED_AT_ATTRIBUTE_NAME,\n UPDATED_AT_ATTRIBUTE_NAME,\n PUBLISHED_AT_ATTRIBUTE_NAME,\n];\n\n/**\n * List of attribute types that cannot be used as the main field.\n * Not sure the name could be any clearer.\n */\nconst ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = [\n 'dynamiczone',\n 'json',\n 'text',\n 'relation',\n 'component',\n 'boolean',\n 'media',\n 'password',\n 'richtext',\n 'timestamp',\n 'blocks',\n];\n\nexport {\n ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD,\n CREATED_AT_ATTRIBUTE_NAME,\n UPDATED_AT_ATTRIBUTE_NAME,\n PUBLISHED_AT_ATTRIBUTE_NAME,\n CREATED_BY_ATTRIBUTE_NAME,\n UPDATED_BY_ATTRIBUTE_NAME,\n PUBLISHED_BY_ATTRIBUTE_NAME,\n CREATOR_FIELDS,\n DOCUMENT_META_FIELDS,\n};\n"],"names":["ID","CREATED_BY_ATTRIBUTE_NAME","UPDATED_BY_ATTRIBUTE_NAME","CREATOR_FIELDS","PUBLISHED_BY_ATTRIBUTE_NAME","CREATED_AT_ATTRIBUTE_NAME","UPDATED_AT_ATTRIBUTE_NAME","PUBLISHED_AT_ATTRIBUTE_NAME","DOCUMENT_META_FIELDS","ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD"],"mappings":";;AAAA,MAAMA,EAAK,GAAA,IAAA;AAEX,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,yBAA4B,GAAA;AAElC,MAAMC,cAAiB,GAAA;AAACF,IAAAA,yBAAAA;AAA2BC,IAAAA;AAA0B;AAE7E,MAAME,2BAA8B,GAAA;AACpC,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,2BAA8B,GAAA;AAEpC,MAAMC,oBAAuB,GAAA;AAC3BR,IAAAA,EAAAA;AACGG,IAAAA,GAAAA,cAAAA;AACHC,IAAAA,2BAAAA;AACAC,IAAAA,yBAAAA;AACAC,IAAAA,yBAAAA;AACAC,IAAAA;AACD;AAED;;;AAGC,UACKE,yCAA4C,GAAA;AAChD,IAAA,aAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA,SAAA;AACA,IAAA,OAAA;AACA,IAAA,UAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA;AACD;;;;;;;;;;;;"}

View File

@@ -0,0 +1,38 @@
const ID = 'id';
const CREATED_BY_ATTRIBUTE_NAME = 'createdBy';
const UPDATED_BY_ATTRIBUTE_NAME = 'updatedBy';
const CREATOR_FIELDS = [
CREATED_BY_ATTRIBUTE_NAME,
UPDATED_BY_ATTRIBUTE_NAME
];
const PUBLISHED_BY_ATTRIBUTE_NAME = 'publishedBy';
const CREATED_AT_ATTRIBUTE_NAME = 'createdAt';
const UPDATED_AT_ATTRIBUTE_NAME = 'updatedAt';
const PUBLISHED_AT_ATTRIBUTE_NAME = 'publishedAt';
const DOCUMENT_META_FIELDS = [
ID,
...CREATOR_FIELDS,
PUBLISHED_BY_ATTRIBUTE_NAME,
CREATED_AT_ATTRIBUTE_NAME,
UPDATED_AT_ATTRIBUTE_NAME,
PUBLISHED_AT_ATTRIBUTE_NAME
];
/**
* List of attribute types that cannot be used as the main field.
* Not sure the name could be any clearer.
*/ const ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = [
'dynamiczone',
'json',
'text',
'relation',
'component',
'boolean',
'media',
'password',
'richtext',
'timestamp',
'blocks'
];
export { ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD, CREATED_AT_ATTRIBUTE_NAME, CREATED_BY_ATTRIBUTE_NAME, CREATOR_FIELDS, DOCUMENT_META_FIELDS, PUBLISHED_AT_ATTRIBUTE_NAME, PUBLISHED_BY_ATTRIBUTE_NAME, UPDATED_AT_ATTRIBUTE_NAME, UPDATED_BY_ATTRIBUTE_NAME };
//# sourceMappingURL=attributes.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"attributes.mjs","sources":["../../../admin/src/constants/attributes.ts"],"sourcesContent":["const ID = 'id';\n\nconst CREATED_BY_ATTRIBUTE_NAME = 'createdBy';\nconst UPDATED_BY_ATTRIBUTE_NAME = 'updatedBy';\n\nconst CREATOR_FIELDS = [CREATED_BY_ATTRIBUTE_NAME, UPDATED_BY_ATTRIBUTE_NAME];\n\nconst PUBLISHED_BY_ATTRIBUTE_NAME = 'publishedBy';\nconst CREATED_AT_ATTRIBUTE_NAME = 'createdAt';\nconst UPDATED_AT_ATTRIBUTE_NAME = 'updatedAt';\nconst PUBLISHED_AT_ATTRIBUTE_NAME = 'publishedAt';\n\nconst DOCUMENT_META_FIELDS = [\n ID,\n ...CREATOR_FIELDS,\n PUBLISHED_BY_ATTRIBUTE_NAME,\n CREATED_AT_ATTRIBUTE_NAME,\n UPDATED_AT_ATTRIBUTE_NAME,\n PUBLISHED_AT_ATTRIBUTE_NAME,\n];\n\n/**\n * List of attribute types that cannot be used as the main field.\n * Not sure the name could be any clearer.\n */\nconst ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = [\n 'dynamiczone',\n 'json',\n 'text',\n 'relation',\n 'component',\n 'boolean',\n 'media',\n 'password',\n 'richtext',\n 'timestamp',\n 'blocks',\n];\n\nexport {\n ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD,\n CREATED_AT_ATTRIBUTE_NAME,\n UPDATED_AT_ATTRIBUTE_NAME,\n PUBLISHED_AT_ATTRIBUTE_NAME,\n CREATED_BY_ATTRIBUTE_NAME,\n UPDATED_BY_ATTRIBUTE_NAME,\n PUBLISHED_BY_ATTRIBUTE_NAME,\n CREATOR_FIELDS,\n DOCUMENT_META_FIELDS,\n};\n"],"names":["ID","CREATED_BY_ATTRIBUTE_NAME","UPDATED_BY_ATTRIBUTE_NAME","CREATOR_FIELDS","PUBLISHED_BY_ATTRIBUTE_NAME","CREATED_AT_ATTRIBUTE_NAME","UPDATED_AT_ATTRIBUTE_NAME","PUBLISHED_AT_ATTRIBUTE_NAME","DOCUMENT_META_FIELDS","ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD"],"mappings":"AAAA,MAAMA,EAAK,GAAA,IAAA;AAEX,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,yBAA4B,GAAA;AAElC,MAAMC,cAAiB,GAAA;AAACF,IAAAA,yBAAAA;AAA2BC,IAAAA;AAA0B;AAE7E,MAAME,2BAA8B,GAAA;AACpC,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,yBAA4B,GAAA;AAClC,MAAMC,2BAA8B,GAAA;AAEpC,MAAMC,oBAAuB,GAAA;AAC3BR,IAAAA,EAAAA;AACGG,IAAAA,GAAAA,cAAAA;AACHC,IAAAA,2BAAAA;AACAC,IAAAA,yBAAAA;AACAC,IAAAA,yBAAAA;AACAC,IAAAA;AACD;AAED;;;AAGC,UACKE,yCAA4C,GAAA;AAChD,IAAA,aAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA,SAAA;AACA,IAAA,OAAA;AACA,IAAA,UAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA;AACD;;;;"}

View File

@@ -0,0 +1,8 @@
'use strict';
const SINGLE_TYPES = 'single-types';
const COLLECTION_TYPES = 'collection-types';
exports.COLLECTION_TYPES = COLLECTION_TYPES;
exports.SINGLE_TYPES = SINGLE_TYPES;
//# sourceMappingURL=collections.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"collections.js","sources":["../../../admin/src/constants/collections.ts"],"sourcesContent":["const SINGLE_TYPES = 'single-types';\nconst COLLECTION_TYPES = 'collection-types';\n\nexport { SINGLE_TYPES, COLLECTION_TYPES };\n"],"names":["SINGLE_TYPES","COLLECTION_TYPES"],"mappings":";;AAAA,MAAMA,YAAe,GAAA;AACrB,MAAMC,gBAAmB,GAAA;;;;;"}

View File

@@ -0,0 +1,5 @@
const SINGLE_TYPES = 'single-types';
const COLLECTION_TYPES = 'collection-types';
export { COLLECTION_TYPES, SINGLE_TYPES };
//# sourceMappingURL=collections.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"collections.mjs","sources":["../../../admin/src/constants/collections.ts"],"sourcesContent":["const SINGLE_TYPES = 'single-types';\nconst COLLECTION_TYPES = 'collection-types';\n\nexport { SINGLE_TYPES, COLLECTION_TYPES };\n"],"names":["SINGLE_TYPES","COLLECTION_TYPES"],"mappings":"AAAA,MAAMA,YAAe,GAAA;AACrB,MAAMC,gBAAmB,GAAA;;;;"}

View File

@@ -0,0 +1,13 @@
'use strict';
const ItemTypes = {
COMPONENT: 'component',
EDIT_FIELD: 'editField',
FIELD: 'field',
DYNAMIC_ZONE: 'dynamicZone',
RELATION: 'relation',
BLOCKS: 'blocks'
};
exports.ItemTypes = ItemTypes;
//# sourceMappingURL=dragAndDrop.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dragAndDrop.js","sources":["../../../admin/src/constants/dragAndDrop.ts"],"sourcesContent":["export const ItemTypes = {\n COMPONENT: 'component',\n EDIT_FIELD: 'editField',\n FIELD: 'field',\n DYNAMIC_ZONE: 'dynamicZone',\n RELATION: 'relation',\n BLOCKS: 'blocks',\n} as const;\n"],"names":["ItemTypes","COMPONENT","EDIT_FIELD","FIELD","DYNAMIC_ZONE","RELATION","BLOCKS"],"mappings":";;MAAaA,SAAY,GAAA;IACvBC,SAAW,EAAA,WAAA;IACXC,UAAY,EAAA,WAAA;IACZC,KAAO,EAAA,OAAA;IACPC,YAAc,EAAA,aAAA;IACdC,QAAU,EAAA,UAAA;IACVC,MAAQ,EAAA;AACV;;;;"}

View File

@@ -0,0 +1,11 @@
const ItemTypes = {
COMPONENT: 'component',
EDIT_FIELD: 'editField',
FIELD: 'field',
DYNAMIC_ZONE: 'dynamicZone',
RELATION: 'relation',
BLOCKS: 'blocks'
};
export { ItemTypes };
//# sourceMappingURL=dragAndDrop.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dragAndDrop.mjs","sources":["../../../admin/src/constants/dragAndDrop.ts"],"sourcesContent":["export const ItemTypes = {\n COMPONENT: 'component',\n EDIT_FIELD: 'editField',\n FIELD: 'field',\n DYNAMIC_ZONE: 'dynamicZone',\n RELATION: 'relation',\n BLOCKS: 'blocks',\n} as const;\n"],"names":["ItemTypes","COMPONENT","EDIT_FIELD","FIELD","DYNAMIC_ZONE","RELATION","BLOCKS"],"mappings":"MAAaA,SAAY,GAAA;IACvBC,SAAW,EAAA,WAAA;IACXC,UAAY,EAAA,WAAA;IACZC,KAAO,EAAA,OAAA;IACPC,YAAc,EAAA,aAAA;IACdC,QAAU,EAAA,UAAA;IACVC,MAAQ,EAAA;AACV;;;;"}

View File

@@ -0,0 +1,27 @@
'use strict';
const HOOKS = {
/**
* Hook that allows to mutate the displayed headers of the list view table
* @constant
* @type {string}
*/ INJECT_COLUMN_IN_TABLE: 'Admin/CM/pages/ListView/inject-column-in-table',
/**
* Hook that allows to mutate the CM's collection types links pre-set filters
* @constant
* @type {string}
*/ MUTATE_COLLECTION_TYPES_LINKS: 'Admin/CM/pages/App/mutate-collection-types-links',
/**
* Hook that allows to mutate the CM's edit view layout
* @constant
* @type {string}
*/ MUTATE_EDIT_VIEW_LAYOUT: 'Admin/CM/pages/EditView/mutate-edit-view-layout',
/**
* Hook that allows to mutate the CM's single types links pre-set filters
* @constant
* @type {string}
*/ MUTATE_SINGLE_TYPES_LINKS: 'Admin/CM/pages/App/mutate-single-types-links'
};
exports.HOOKS = HOOKS;
//# sourceMappingURL=hooks.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hooks.js","sources":["../../../admin/src/constants/hooks.ts"],"sourcesContent":["export const HOOKS = {\n /**\n * Hook that allows to mutate the displayed headers of the list view table\n * @constant\n * @type {string}\n */\n INJECT_COLUMN_IN_TABLE: 'Admin/CM/pages/ListView/inject-column-in-table',\n\n /**\n * Hook that allows to mutate the CM's collection types links pre-set filters\n * @constant\n * @type {string}\n */\n MUTATE_COLLECTION_TYPES_LINKS: 'Admin/CM/pages/App/mutate-collection-types-links',\n\n /**\n * Hook that allows to mutate the CM's edit view layout\n * @constant\n * @type {string}\n */\n MUTATE_EDIT_VIEW_LAYOUT: 'Admin/CM/pages/EditView/mutate-edit-view-layout',\n\n /**\n * Hook that allows to mutate the CM's single types links pre-set filters\n * @constant\n * @type {string}\n */\n MUTATE_SINGLE_TYPES_LINKS: 'Admin/CM/pages/App/mutate-single-types-links',\n};\n"],"names":["HOOKS","INJECT_COLUMN_IN_TABLE","MUTATE_COLLECTION_TYPES_LINKS","MUTATE_EDIT_VIEW_LAYOUT","MUTATE_SINGLE_TYPES_LINKS"],"mappings":";;MAAaA,KAAQ,GAAA;AACnB;;;;AAIC,MACDC,sBAAwB,EAAA,gDAAA;AAExB;;;;AAIC,MACDC,6BAA+B,EAAA,kDAAA;AAE/B;;;;AAIC,MACDC,uBAAyB,EAAA,iDAAA;AAEzB;;;;AAIC,MACDC,yBAA2B,EAAA;AAC7B;;;;"}

View File

@@ -0,0 +1,25 @@
const HOOKS = {
/**
* Hook that allows to mutate the displayed headers of the list view table
* @constant
* @type {string}
*/ INJECT_COLUMN_IN_TABLE: 'Admin/CM/pages/ListView/inject-column-in-table',
/**
* Hook that allows to mutate the CM's collection types links pre-set filters
* @constant
* @type {string}
*/ MUTATE_COLLECTION_TYPES_LINKS: 'Admin/CM/pages/App/mutate-collection-types-links',
/**
* Hook that allows to mutate the CM's edit view layout
* @constant
* @type {string}
*/ MUTATE_EDIT_VIEW_LAYOUT: 'Admin/CM/pages/EditView/mutate-edit-view-layout',
/**
* Hook that allows to mutate the CM's single types links pre-set filters
* @constant
* @type {string}
*/ MUTATE_SINGLE_TYPES_LINKS: 'Admin/CM/pages/App/mutate-single-types-links'
};
export { HOOKS };
//# sourceMappingURL=hooks.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hooks.mjs","sources":["../../../admin/src/constants/hooks.ts"],"sourcesContent":["export const HOOKS = {\n /**\n * Hook that allows to mutate the displayed headers of the list view table\n * @constant\n * @type {string}\n */\n INJECT_COLUMN_IN_TABLE: 'Admin/CM/pages/ListView/inject-column-in-table',\n\n /**\n * Hook that allows to mutate the CM's collection types links pre-set filters\n * @constant\n * @type {string}\n */\n MUTATE_COLLECTION_TYPES_LINKS: 'Admin/CM/pages/App/mutate-collection-types-links',\n\n /**\n * Hook that allows to mutate the CM's edit view layout\n * @constant\n * @type {string}\n */\n MUTATE_EDIT_VIEW_LAYOUT: 'Admin/CM/pages/EditView/mutate-edit-view-layout',\n\n /**\n * Hook that allows to mutate the CM's single types links pre-set filters\n * @constant\n * @type {string}\n */\n MUTATE_SINGLE_TYPES_LINKS: 'Admin/CM/pages/App/mutate-single-types-links',\n};\n"],"names":["HOOKS","INJECT_COLUMN_IN_TABLE","MUTATE_COLLECTION_TYPES_LINKS","MUTATE_EDIT_VIEW_LAYOUT","MUTATE_SINGLE_TYPES_LINKS"],"mappings":"MAAaA,KAAQ,GAAA;AACnB;;;;AAIC,MACDC,sBAAwB,EAAA,gDAAA;AAExB;;;;AAIC,MACDC,6BAA+B,EAAA,kDAAA;AAE/B;;;;AAIC,MACDC,uBAAyB,EAAA,iDAAA;AAEzB;;;;AAIC,MACDC,yBAA2B,EAAA;AAC7B;;;;"}

View File

@@ -0,0 +1,14 @@
'use strict';
const PLUGIN_ID = 'content-manager';
const PERMISSIONS = [
'plugin::content-manager.explorer.create',
'plugin::content-manager.explorer.read',
'plugin::content-manager.explorer.update',
'plugin::content-manager.explorer.delete',
'plugin::content-manager.explorer.publish'
];
exports.PERMISSIONS = PERMISSIONS;
exports.PLUGIN_ID = PLUGIN_ID;
//# sourceMappingURL=plugin.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.js","sources":["../../../admin/src/constants/plugin.ts"],"sourcesContent":["const PLUGIN_ID = 'content-manager';\n\nconst PERMISSIONS = [\n 'plugin::content-manager.explorer.create',\n 'plugin::content-manager.explorer.read',\n 'plugin::content-manager.explorer.update',\n 'plugin::content-manager.explorer.delete',\n 'plugin::content-manager.explorer.publish',\n];\n\nexport { PLUGIN_ID, PERMISSIONS };\n"],"names":["PLUGIN_ID","PERMISSIONS"],"mappings":";;AAAA,MAAMA,SAAY,GAAA;AAElB,MAAMC,WAAc,GAAA;AAClB,IAAA,yCAAA;AACA,IAAA,uCAAA;AACA,IAAA,yCAAA;AACA,IAAA,yCAAA;AACA,IAAA;AACD;;;;;"}

View File

@@ -0,0 +1,11 @@
const PLUGIN_ID = 'content-manager';
const PERMISSIONS = [
'plugin::content-manager.explorer.create',
'plugin::content-manager.explorer.read',
'plugin::content-manager.explorer.update',
'plugin::content-manager.explorer.delete',
'plugin::content-manager.explorer.publish'
];
export { PERMISSIONS, PLUGIN_ID };
//# sourceMappingURL=plugin.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.mjs","sources":["../../../admin/src/constants/plugin.ts"],"sourcesContent":["const PLUGIN_ID = 'content-manager';\n\nconst PERMISSIONS = [\n 'plugin::content-manager.explorer.create',\n 'plugin::content-manager.explorer.read',\n 'plugin::content-manager.explorer.update',\n 'plugin::content-manager.explorer.delete',\n 'plugin::content-manager.explorer.publish',\n];\n\nexport { PLUGIN_ID, PERMISSIONS };\n"],"names":["PLUGIN_ID","PERMISSIONS"],"mappings":"AAAA,MAAMA,SAAY,GAAA;AAElB,MAAMC,WAAc,GAAA;AAClB,IAAA,yCAAA;AACA,IAAA,uCAAA;AACA,IAAA,yCAAA;AACA,IAAA,yCAAA;AACA,IAAA;AACD;;;;"}

View File

@@ -0,0 +1,133 @@
'use strict';
var InjectionZone = require('./components/InjectionZone.js');
var plugin = require('./constants/plugin.js');
var DocumentActions = require('./pages/EditView/components/DocumentActions.js');
var Header = require('./pages/EditView/components/Header.js');
var Panels = require('./pages/EditView/components/Panels.js');
var Actions = require('./pages/ListView/components/BulkActions/Actions.js');
var TableActions = require('./pages/ListView/components/TableActions.js');
/* -------------------------------------------------------------------------------------------------
* ContentManager plugin
* -----------------------------------------------------------------------------------------------*/ class ContentManagerPlugin {
addEditViewSidePanel(panels) {
if (Array.isArray(panels)) {
this.editViewSidePanels = [
...this.editViewSidePanels,
...panels
];
} else if (typeof panels === 'function') {
this.editViewSidePanels = panels(this.editViewSidePanels);
} else {
throw new Error(`Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(panels)}`);
}
}
addDocumentAction(actions) {
if (Array.isArray(actions)) {
this.documentActions = [
...this.documentActions,
...actions
];
} else if (typeof actions === 'function') {
this.documentActions = actions(this.documentActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
addDocumentHeaderAction(actions) {
if (Array.isArray(actions)) {
this.headerActions = [
...this.headerActions,
...actions
];
} else if (typeof actions === 'function') {
this.headerActions = actions(this.headerActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
addBulkAction(actions) {
if (Array.isArray(actions)) {
this.bulkActions = [
...this.bulkActions,
...actions
];
} else if (typeof actions === 'function') {
this.bulkActions = actions(this.bulkActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
get config() {
return {
id: plugin.PLUGIN_ID,
name: 'Content Manager',
injectionZones: InjectionZone.INJECTION_ZONES,
apis: {
addBulkAction: this.addBulkAction.bind(this),
addDocumentAction: this.addDocumentAction.bind(this),
addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
getBulkActions: ()=>this.bulkActions,
getDocumentActions: (position)=>{
/**
* When possible, pre-filter the actions by the components static position property.
* This avoids rendering the actions in multiple places where they weren't displayed,
* which wasn't visible but created issues with useEffect for instance.
* The response should still be filtered by the position, as the static property is new
* and not mandatory to avoid a breaking change.
*/ if (position) {
return this.documentActions.filter((action)=>{
return action.position == undefined || [
action.position
].flat().includes(position);
});
}
return this.documentActions;
},
getEditViewSidePanels: ()=>this.editViewSidePanels,
getHeaderActions: ()=>this.headerActions
}
};
}
constructor(){
/**
* The following properties are the stored ones provided by any plugins registering with
* the content-manager. The function calls however, need to be called at runtime in the
* application, so instead we collate them and run them later with the complete list incl.
* ones already registered & the context of the view.
*/ this.bulkActions = [
...Actions.DEFAULT_BULK_ACTIONS
];
this.documentActions = [
...DocumentActions.DEFAULT_ACTIONS,
...TableActions.DEFAULT_TABLE_ROW_ACTIONS,
...Header.DEFAULT_HEADER_ACTIONS
];
this.editViewSidePanels = [
Panels.ActionsPanel
];
this.headerActions = [];
}
}
/* -------------------------------------------------------------------------------------------------
* getPrintableType
* -----------------------------------------------------------------------------------------------*/ /**
* @internal
* @description Gets the human-friendly printable type name for the given value, for instance it will yield
* `array` instead of `object`, as the native `typeof` operator would do.
*/ const getPrintableType = (value)=>{
const nativeType = typeof value;
if (nativeType === 'object') {
if (value === null) return 'null';
if (Array.isArray(value)) return 'array';
if (value instanceof Object && value.constructor.name !== 'Object') {
return value.constructor.name;
}
}
return nativeType;
};
exports.ContentManagerPlugin = ContentManagerPlugin;
//# sourceMappingURL=content-manager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,131 @@
import { INJECTION_ZONES } from './components/InjectionZone.mjs';
import { PLUGIN_ID } from './constants/plugin.mjs';
import { DEFAULT_ACTIONS } from './pages/EditView/components/DocumentActions.mjs';
import { DEFAULT_HEADER_ACTIONS } from './pages/EditView/components/Header.mjs';
import { ActionsPanel } from './pages/EditView/components/Panels.mjs';
import { DEFAULT_BULK_ACTIONS } from './pages/ListView/components/BulkActions/Actions.mjs';
import { DEFAULT_TABLE_ROW_ACTIONS } from './pages/ListView/components/TableActions.mjs';
/* -------------------------------------------------------------------------------------------------
* ContentManager plugin
* -----------------------------------------------------------------------------------------------*/ class ContentManagerPlugin {
addEditViewSidePanel(panels) {
if (Array.isArray(panels)) {
this.editViewSidePanels = [
...this.editViewSidePanels,
...panels
];
} else if (typeof panels === 'function') {
this.editViewSidePanels = panels(this.editViewSidePanels);
} else {
throw new Error(`Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(panels)}`);
}
}
addDocumentAction(actions) {
if (Array.isArray(actions)) {
this.documentActions = [
...this.documentActions,
...actions
];
} else if (typeof actions === 'function') {
this.documentActions = actions(this.documentActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
addDocumentHeaderAction(actions) {
if (Array.isArray(actions)) {
this.headerActions = [
...this.headerActions,
...actions
];
} else if (typeof actions === 'function') {
this.headerActions = actions(this.headerActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
addBulkAction(actions) {
if (Array.isArray(actions)) {
this.bulkActions = [
...this.bulkActions,
...actions
];
} else if (typeof actions === 'function') {
this.bulkActions = actions(this.bulkActions);
} else {
throw new Error(`Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(actions)}`);
}
}
get config() {
return {
id: PLUGIN_ID,
name: 'Content Manager',
injectionZones: INJECTION_ZONES,
apis: {
addBulkAction: this.addBulkAction.bind(this),
addDocumentAction: this.addDocumentAction.bind(this),
addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
getBulkActions: ()=>this.bulkActions,
getDocumentActions: (position)=>{
/**
* When possible, pre-filter the actions by the components static position property.
* This avoids rendering the actions in multiple places where they weren't displayed,
* which wasn't visible but created issues with useEffect for instance.
* The response should still be filtered by the position, as the static property is new
* and not mandatory to avoid a breaking change.
*/ if (position) {
return this.documentActions.filter((action)=>{
return action.position == undefined || [
action.position
].flat().includes(position);
});
}
return this.documentActions;
},
getEditViewSidePanels: ()=>this.editViewSidePanels,
getHeaderActions: ()=>this.headerActions
}
};
}
constructor(){
/**
* The following properties are the stored ones provided by any plugins registering with
* the content-manager. The function calls however, need to be called at runtime in the
* application, so instead we collate them and run them later with the complete list incl.
* ones already registered & the context of the view.
*/ this.bulkActions = [
...DEFAULT_BULK_ACTIONS
];
this.documentActions = [
...DEFAULT_ACTIONS,
...DEFAULT_TABLE_ROW_ACTIONS,
...DEFAULT_HEADER_ACTIONS
];
this.editViewSidePanels = [
ActionsPanel
];
this.headerActions = [];
}
}
/* -------------------------------------------------------------------------------------------------
* getPrintableType
* -----------------------------------------------------------------------------------------------*/ /**
* @internal
* @description Gets the human-friendly printable type name for the given value, for instance it will yield
* `array` instead of `object`, as the native `typeof` operator would do.
*/ const getPrintableType = (value)=>{
const nativeType = typeof value;
if (nativeType === 'object') {
if (value === null) return 'null';
if (Array.isArray(value)) return 'array';
if (value instanceof Object && value.constructor.name !== 'Object') {
return value.constructor.name;
}
}
return nativeType;
};
export { ContentManagerPlugin };
//# sourceMappingURL=content-manager.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,126 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var reactRouterDom = require('react-router-dom');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const [DocumentRBACProvider, useDocumentRBAC] = strapiAdmin.createContext('DocumentRBAC', {
canCreate: false,
canCreateFields: [],
canDelete: false,
canPublish: false,
canRead: false,
canReadFields: [],
canUpdate: false,
canUpdateFields: [],
canUserAction: ()=>false,
isLoading: false
});
/**
* @internal This component is not meant to be used outside of the Content Manager plugin.
* It depends on knowing the slug/model of the content-type using the params of the URL or the model if it is passed as arg.
* If you do use the hook outside of the context, we default to `false` for all actions.
*
* It then creates an list of `can{Action}` that are passed to the context for consumption
* within the app to enforce RBAC.
*/ const DocumentRBAC = ({ children, permissions, model })=>{
const { slug } = reactRouterDom.useParams();
if (!slug && !model) {
throw new Error('Cannot find the slug param in the URL or the model prop is not provided.');
}
const contentTypeUid = model ?? slug;
const [{ rawQuery }] = strapiAdmin.useQueryParams();
const userPermissions = strapiAdmin.useAuth('DocumentRBAC', (state)=>state.permissions);
const contentTypePermissions = React__namespace.useMemo(()=>{
const contentTypePermissions = userPermissions.filter((permission)=>permission.subject === contentTypeUid);
return contentTypePermissions.reduce((acc, permission)=>{
const [action] = permission.action.split('.').slice(-1);
return {
...acc,
[action]: [
permission
]
};
}, {});
}, [
contentTypeUid,
userPermissions
]);
const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? undefined, // TODO: useRBAC context should be typed and built differently
// We are passing raw query as context to the hook so that it can
// rely on the locale provided from DocumentRBAC for its permission calculations.
rawQuery);
const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
/**
* @description Checks if the user can perform an action on a field based on the field names
* provided as the second argument.
*/ const canUserAction = React__namespace.useCallback((fieldName, fieldsUserCanAction, fieldType)=>{
const name = removeNumericalStrings(fieldName.split('.'));
const componentFieldNames = fieldsUserCanAction// filter out fields that aren't components (components are dot separated)
.filter((field)=>field.split('.').length > 1);
if (fieldType === 'component') {
// check if the field name is within any of those arrays
return componentFieldNames.some((field)=>{
return field.includes(name.join('.'));
});
}
/**
* The field is within a component.
*/ if (name.length > 1) {
return componentFieldNames.includes(name.join('.'));
}
/**
* just a regular field
*/ return fieldsUserCanAction.includes(fieldName);
}, []);
if (isLoading) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Loading, {});
}
return /*#__PURE__*/ jsxRuntime.jsx(DocumentRBACProvider, {
isLoading: isLoading,
canCreateFields: canCreateFields,
canReadFields: canReadFields,
canUpdateFields: canUpdateFields,
canUserAction: canUserAction,
...allowedActions,
children: children
});
};
/**
* @internal it's really small, but it's used three times in a row and DRY for something this straight forward.
*/ const extractAndDedupeFields = (permissions = [])=>permissions.flatMap((permission)=>permission.properties?.fields).filter((field, index, arr)=>arr.indexOf(field) === index && typeof field === 'string');
/**
* @internal removes numerical strings from arrays.
* @example
* ```ts
* const name = 'a.0.b';
* const res = removeNumericalStrings(name.split('.'));
* console.log(res); // ['a', 'b']
* ```
*/ const removeNumericalStrings = (arr)=>arr.filter((item)=>isNaN(Number(item)));
exports.DocumentRBAC = DocumentRBAC;
exports.useDocumentRBAC = useDocumentRBAC;
//# sourceMappingURL=DocumentRBAC.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
import { jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { createContext, useQueryParams, useAuth, useRBAC, Page } from '@strapi/admin/strapi-admin';
import { useParams } from 'react-router-dom';
const [DocumentRBACProvider, useDocumentRBAC] = createContext('DocumentRBAC', {
canCreate: false,
canCreateFields: [],
canDelete: false,
canPublish: false,
canRead: false,
canReadFields: [],
canUpdate: false,
canUpdateFields: [],
canUserAction: ()=>false,
isLoading: false
});
/**
* @internal This component is not meant to be used outside of the Content Manager plugin.
* It depends on knowing the slug/model of the content-type using the params of the URL or the model if it is passed as arg.
* If you do use the hook outside of the context, we default to `false` for all actions.
*
* It then creates an list of `can{Action}` that are passed to the context for consumption
* within the app to enforce RBAC.
*/ const DocumentRBAC = ({ children, permissions, model })=>{
const { slug } = useParams();
if (!slug && !model) {
throw new Error('Cannot find the slug param in the URL or the model prop is not provided.');
}
const contentTypeUid = model ?? slug;
const [{ rawQuery }] = useQueryParams();
const userPermissions = useAuth('DocumentRBAC', (state)=>state.permissions);
const contentTypePermissions = React.useMemo(()=>{
const contentTypePermissions = userPermissions.filter((permission)=>permission.subject === contentTypeUid);
return contentTypePermissions.reduce((acc, permission)=>{
const [action] = permission.action.split('.').slice(-1);
return {
...acc,
[action]: [
permission
]
};
}, {});
}, [
contentTypeUid,
userPermissions
]);
const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? undefined, // TODO: useRBAC context should be typed and built differently
// We are passing raw query as context to the hook so that it can
// rely on the locale provided from DocumentRBAC for its permission calculations.
rawQuery);
const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
/**
* @description Checks if the user can perform an action on a field based on the field names
* provided as the second argument.
*/ const canUserAction = React.useCallback((fieldName, fieldsUserCanAction, fieldType)=>{
const name = removeNumericalStrings(fieldName.split('.'));
const componentFieldNames = fieldsUserCanAction// filter out fields that aren't components (components are dot separated)
.filter((field)=>field.split('.').length > 1);
if (fieldType === 'component') {
// check if the field name is within any of those arrays
return componentFieldNames.some((field)=>{
return field.includes(name.join('.'));
});
}
/**
* The field is within a component.
*/ if (name.length > 1) {
return componentFieldNames.includes(name.join('.'));
}
/**
* just a regular field
*/ return fieldsUserCanAction.includes(fieldName);
}, []);
if (isLoading) {
return /*#__PURE__*/ jsx(Page.Loading, {});
}
return /*#__PURE__*/ jsx(DocumentRBACProvider, {
isLoading: isLoading,
canCreateFields: canCreateFields,
canReadFields: canReadFields,
canUpdateFields: canUpdateFields,
canUserAction: canUserAction,
...allowedActions,
children: children
});
};
/**
* @internal it's really small, but it's used three times in a row and DRY for something this straight forward.
*/ const extractAndDedupeFields = (permissions = [])=>permissions.flatMap((permission)=>permission.properties?.fields).filter((field, index, arr)=>arr.indexOf(field) === index && typeof field === 'string');
/**
* @internal removes numerical strings from arrays.
* @example
* ```ts
* const name = 'a.0.b';
* const res = removeNumericalStrings(name.split('.'));
* console.log(res); // ['a', 'b']
* ```
*/ const removeNumericalStrings = (arr)=>arr.filter((item)=>isNaN(Number(item)));
export { DocumentRBAC, useDocumentRBAC };
//# sourceMappingURL=DocumentRBAC.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var Icons = require('@strapi/icons');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
const HistoryAction = ({ model, document })=>{
const { formatMessage } = reactIntl.useIntl();
const [{ query }] = strapiAdmin.useQueryParams();
const navigate = reactRouterDom.useNavigate();
const { trackUsage } = strapiAdmin.useTracking();
const { pathname } = reactRouterDom.useLocation();
const pluginsQueryParams = qs.stringify({
plugins: query.plugins
}, {
encode: false
});
if (!window.strapi.features.isEnabled('cms-content-history')) {
return null;
}
const handleOnClick = ()=>{
const destination = {
pathname: 'history',
search: pluginsQueryParams
};
trackUsage('willNavigate', {
from: pathname,
to: `${pathname}/${destination.pathname}`
});
navigate(destination);
};
return {
icon: /*#__PURE__*/ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
label: formatMessage({
id: 'content-manager.history.document-action',
defaultMessage: 'Content History'
}),
onClick: handleOnClick,
disabled: /**
* The user is creating a new document.
* It hasn't been saved yet, so there's no history to go to
*/ !document || /**
* The document has been created but the current dimension has never been saved.
* For example, the user is creating a new locale in an existing document,
* so there's no history for the document in that locale
*/ !document.id || /**
* History is only available for content types created by the user.
* These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
* which start with `admin::` or `plugin::`
*/ !model.startsWith('api::'),
position: 'header'
};
};
HistoryAction.type = 'history';
HistoryAction.position = 'header';
exports.HistoryAction = HistoryAction;
//# sourceMappingURL=HistoryAction.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HistoryAction.js","sources":["../../../../admin/src/history/components/HistoryAction.tsx"],"sourcesContent":["import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';\nimport { ClockCounterClockwise } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\nimport type { DocumentActionComponent } from '../../content-manager';\n\nconst HistoryAction: DocumentActionComponent = ({ model, document }) => {\n const { formatMessage } = useIntl();\n const [{ query }] = useQueryParams<{ plugins?: Record<string, unknown> }>();\n const navigate = useNavigate();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });\n\n if (!window.strapi.features.isEnabled('cms-content-history')) {\n return null;\n }\n\n const handleOnClick = () => {\n const destination = { pathname: 'history', search: pluginsQueryParams };\n trackUsage('willNavigate', {\n from: pathname,\n to: `${pathname}/${destination.pathname}`,\n });\n navigate(destination);\n };\n\n return {\n icon: <ClockCounterClockwise />,\n label: formatMessage({\n id: 'content-manager.history.document-action',\n defaultMessage: 'Content History',\n }),\n onClick: handleOnClick,\n disabled:\n /**\n * The user is creating a new document.\n * It hasn't been saved yet, so there's no history to go to\n */\n !document ||\n /**\n * The document has been created but the current dimension has never been saved.\n * For example, the user is creating a new locale in an existing document,\n * so there's no history for the document in that locale\n */\n !document.id ||\n /**\n * History is only available for content types created by the user.\n * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,\n * which start with `admin::` or `plugin::`\n */\n !model.startsWith('api::'),\n position: 'header',\n };\n};\n\nHistoryAction.type = 'history';\nHistoryAction.position = 'header';\n\nexport { HistoryAction };\n"],"names":["HistoryAction","model","document","formatMessage","useIntl","query","useQueryParams","navigate","useNavigate","trackUsage","useTracking","pathname","useLocation","pluginsQueryParams","stringify","plugins","encode","window","strapi","features","isEnabled","handleOnClick","destination","search","from","to","icon","_jsx","ClockCounterClockwise","label","id","defaultMessage","onClick","disabled","startsWith","position","type"],"mappings":";;;;;;;;;AAQA,MAAMA,gBAAyC,CAAC,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAA;IACjE,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AACpB,IAAA,MAAMC,QAAWC,GAAAA,0BAAAA,EAAAA;IACjB,MAAM,EAAEC,UAAU,EAAE,GAAGC,uBAAAA,EAAAA;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,0BAAAA,EAAAA;AACrB,IAAA,MAAMC,qBAAqBC,YAAU,CAAA;AAAEC,QAAAA,OAAAA,EAASV,MAAMU;KAAW,EAAA;QAAEC,MAAQ,EAAA;AAAM,KAAA,CAAA;IAEjF,IAAI,CAACC,OAAOC,MAAM,CAACC,QAAQ,CAACC,SAAS,CAAC,qBAAwB,CAAA,EAAA;QAC5D,OAAO,IAAA;AACT;AAEA,IAAA,MAAMC,aAAgB,GAAA,IAAA;AACpB,QAAA,MAAMC,WAAc,GAAA;YAAEX,QAAU,EAAA,SAAA;YAAWY,MAAQV,EAAAA;AAAmB,SAAA;AACtEJ,QAAAA,UAAAA,CAAW,cAAgB,EAAA;YACzBe,IAAMb,EAAAA,QAAAA;YACNc,EAAI,EAAA,CAAC,EAAEd,QAAS,CAAA,CAAC,EAAEW,WAAYX,CAAAA,QAAQ,CAAC;AAC1C,SAAA,CAAA;QACAJ,QAASe,CAAAA,WAAAA,CAAAA;AACX,KAAA;IAEA,OAAO;AACLI,QAAAA,IAAAA,gBAAMC,cAACC,CAAAA,2BAAAA,EAAAA,EAAAA,CAAAA;AACPC,QAAAA,KAAAA,EAAO1B,aAAc,CAAA;YACnB2B,EAAI,EAAA,yCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA,CAAA;QACAC,OAASX,EAAAA,aAAAA;QACTY,QACE;;;AAGC,UACD,CAAC/B,QACD;;;;UAKA,CAACA,QAAS4B,CAAAA,EAAE;;;;UAMZ,CAAC7B,KAAMiC,CAAAA,UAAU,CAAC,OAAA,CAAA;QACpBC,QAAU,EAAA;AACZ,KAAA;AACF;AAEAnC,aAAAA,CAAcoC,IAAI,GAAG,SAAA;AACrBpC,aAAAA,CAAcmC,QAAQ,GAAG,QAAA;;;;"}

View File

@@ -0,0 +1,59 @@
import { jsx } from 'react/jsx-runtime';
import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';
import { ClockCounterClockwise } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useNavigate, useLocation } from 'react-router-dom';
const HistoryAction = ({ model, document })=>{
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const navigate = useNavigate();
const { trackUsage } = useTracking();
const { pathname } = useLocation();
const pluginsQueryParams = stringify({
plugins: query.plugins
}, {
encode: false
});
if (!window.strapi.features.isEnabled('cms-content-history')) {
return null;
}
const handleOnClick = ()=>{
const destination = {
pathname: 'history',
search: pluginsQueryParams
};
trackUsage('willNavigate', {
from: pathname,
to: `${pathname}/${destination.pathname}`
});
navigate(destination);
};
return {
icon: /*#__PURE__*/ jsx(ClockCounterClockwise, {}),
label: formatMessage({
id: 'content-manager.history.document-action',
defaultMessage: 'Content History'
}),
onClick: handleOnClick,
disabled: /**
* The user is creating a new document.
* It hasn't been saved yet, so there's no history to go to
*/ !document || /**
* The document has been created but the current dimension has never been saved.
* For example, the user is creating a new locale in an existing document,
* so there's no history for the document in that locale
*/ !document.id || /**
* History is only available for content types created by the user.
* These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
* which start with `admin::` or `plugin::`
*/ !model.startsWith('api::'),
position: 'header'
};
};
HistoryAction.type = 'history';
HistoryAction.position = 'header';
export { HistoryAction };
//# sourceMappingURL=HistoryAction.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HistoryAction.mjs","sources":["../../../../admin/src/history/components/HistoryAction.tsx"],"sourcesContent":["import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';\nimport { ClockCounterClockwise } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\nimport type { DocumentActionComponent } from '../../content-manager';\n\nconst HistoryAction: DocumentActionComponent = ({ model, document }) => {\n const { formatMessage } = useIntl();\n const [{ query }] = useQueryParams<{ plugins?: Record<string, unknown> }>();\n const navigate = useNavigate();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });\n\n if (!window.strapi.features.isEnabled('cms-content-history')) {\n return null;\n }\n\n const handleOnClick = () => {\n const destination = { pathname: 'history', search: pluginsQueryParams };\n trackUsage('willNavigate', {\n from: pathname,\n to: `${pathname}/${destination.pathname}`,\n });\n navigate(destination);\n };\n\n return {\n icon: <ClockCounterClockwise />,\n label: formatMessage({\n id: 'content-manager.history.document-action',\n defaultMessage: 'Content History',\n }),\n onClick: handleOnClick,\n disabled:\n /**\n * The user is creating a new document.\n * It hasn't been saved yet, so there's no history to go to\n */\n !document ||\n /**\n * The document has been created but the current dimension has never been saved.\n * For example, the user is creating a new locale in an existing document,\n * so there's no history for the document in that locale\n */\n !document.id ||\n /**\n * History is only available for content types created by the user.\n * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,\n * which start with `admin::` or `plugin::`\n */\n !model.startsWith('api::'),\n position: 'header',\n };\n};\n\nHistoryAction.type = 'history';\nHistoryAction.position = 'header';\n\nexport { HistoryAction };\n"],"names":["HistoryAction","model","document","formatMessage","useIntl","query","useQueryParams","navigate","useNavigate","trackUsage","useTracking","pathname","useLocation","pluginsQueryParams","stringify","plugins","encode","window","strapi","features","isEnabled","handleOnClick","destination","search","from","to","icon","_jsx","ClockCounterClockwise","label","id","defaultMessage","onClick","disabled","startsWith","position","type"],"mappings":";;;;;;;AAQA,MAAMA,gBAAyC,CAAC,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAA;IACjE,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAC1B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AACpB,IAAA,MAAMC,QAAWC,GAAAA,WAAAA,EAAAA;IACjB,MAAM,EAAEC,UAAU,EAAE,GAAGC,WAAAA,EAAAA;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,WAAAA,EAAAA;AACrB,IAAA,MAAMC,qBAAqBC,SAAU,CAAA;AAAEC,QAAAA,OAAAA,EAASV,MAAMU;KAAW,EAAA;QAAEC,MAAQ,EAAA;AAAM,KAAA,CAAA;IAEjF,IAAI,CAACC,OAAOC,MAAM,CAACC,QAAQ,CAACC,SAAS,CAAC,qBAAwB,CAAA,EAAA;QAC5D,OAAO,IAAA;AACT;AAEA,IAAA,MAAMC,aAAgB,GAAA,IAAA;AACpB,QAAA,MAAMC,WAAc,GAAA;YAAEX,QAAU,EAAA,SAAA;YAAWY,MAAQV,EAAAA;AAAmB,SAAA;AACtEJ,QAAAA,UAAAA,CAAW,cAAgB,EAAA;YACzBe,IAAMb,EAAAA,QAAAA;YACNc,EAAI,EAAA,CAAC,EAAEd,QAAS,CAAA,CAAC,EAAEW,WAAYX,CAAAA,QAAQ,CAAC;AAC1C,SAAA,CAAA;QACAJ,QAASe,CAAAA,WAAAA,CAAAA;AACX,KAAA;IAEA,OAAO;AACLI,QAAAA,IAAAA,gBAAMC,GAACC,CAAAA,qBAAAA,EAAAA,EAAAA,CAAAA;AACPC,QAAAA,KAAAA,EAAO1B,aAAc,CAAA;YACnB2B,EAAI,EAAA,yCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA,CAAA;QACAC,OAASX,EAAAA,aAAAA;QACTY,QACE;;;AAGC,UACD,CAAC/B,QACD;;;;UAKA,CAACA,QAAS4B,CAAAA,EAAE;;;;UAMZ,CAAC7B,KAAMiC,CAAAA,UAAU,CAAC,OAAA,CAAA;QACpBC,QAAU,EAAA;AACZ,KAAA;AACF;AAEAnC,aAAAA,CAAcoC,IAAI,GAAG,SAAA;AACrBpC,aAAAA,CAAcmC,QAAQ,GAAG,QAAA;;;;"}

View File

@@ -0,0 +1,264 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var pipe = require('lodash/fp/pipe');
var reactIntl = require('react-intl');
var useDocument = require('../../hooks/useDocument.js');
var hooks = require('../../modules/hooks.js');
var data = require('../../pages/EditView/utils/data.js');
var History = require('../pages/History.js');
var VersionInputRenderer = require('./VersionInputRenderer.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const createLayoutFromFields = (fields)=>{
return fields.reduce((rows, field)=>{
if (field.type === 'dynamiczone') {
// Dynamic zones take up all the columns in a row
rows.push([
field
]);
return rows;
}
if (!rows[rows.length - 1]) {
// Create a new row if there isn't one available
rows.push([]);
}
// Push fields to the current row, they wrap and handle their own column size
rows[rows.length - 1].push(field);
return rows;
}, [])// Map the rows to panels
.map((row)=>[
row
]);
};
/**
* Build a layout for the fields that are were deleted from the edit view layout
* via the configure the view page. This layout will be merged with the main one.
* Those fields would be restored if the user restores the history version, which is why it's
* important to show them, even if they're not in the normal layout.
*/ function getRemaingFieldsLayout({ layout, metadatas, schemaAttributes, fieldSizes }) {
const fieldsInLayout = layout.flatMap((panel)=>panel.flatMap((row)=>row.flatMap((field)=>field.name)));
const remainingFields = Object.entries(metadatas).reduce((currentRemainingFields, [name, field])=>{
// Make sure we do not fields that are not visible, e.g. "id"
if (!fieldsInLayout.includes(name) && field.edit.visible === true) {
const attribute = schemaAttributes[name];
// @ts-expect-error not sure why attribute causes type error
currentRemainingFields.push({
attribute,
type: attribute.type,
visible: true,
disabled: true,
label: field.edit.label || name,
name: name,
size: fieldSizes[attribute.type].default ?? 12
});
}
return currentRemainingFields;
}, []);
return createLayoutFromFields(remainingFields);
}
/* -------------------------------------------------------------------------------------------------
* FormPanel
* -----------------------------------------------------------------------------------------------*/ const FormPanel = ({ panel })=>{
if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
const [row] = panel;
const [field] = row;
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 12,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer.VersionInputRenderer, {
...field
})
})
}, field.name);
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
hasRadius: true,
background: "neutral0",
shadow: "tableShadow",
paddingLeft: 6,
paddingRight: 6,
paddingTop: 6,
paddingBottom: 6,
borderColor: "neutral150",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: panel.map((row, gridRowIndex)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
children: row.map(({ size, ...field })=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer.VersionInputRenderer, {
...field
})
}, field.name);
})
}, gridRowIndex))
})
});
};
const VersionContent = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { fieldSizes } = hooks.useTypedSelector((state)=>state['content-manager'].app);
const version = History.useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const layout = History.useHistoryContext('VersionContent', (state)=>state.layout);
const configuration = History.useHistoryContext('VersionContent', (state)=>state.configuration);
const schema = History.useHistoryContext('VersionContent', (state)=>state.schema);
// Build a layout for the unknown fields section
const removedAttributes = version.meta.unknownAttributes.removed;
const removedAttributesAsFields = Object.entries(removedAttributes).map(([attributeName, attribute])=>{
const field = {
attribute,
shouldIgnoreRBAC: true,
type: attribute.type,
visible: true,
disabled: true,
label: attributeName,
name: attributeName,
size: fieldSizes[attribute.type].default ?? 12
};
return field;
});
const unknownFieldsLayout = createLayoutFromFields(removedAttributesAsFields);
// Build a layout for the fields that are were deleted from the layout
const remainingFieldsLayout = getRemaingFieldsLayout({
metadatas: configuration.contentType.metadatas,
layout,
schemaAttributes: schema.attributes,
fieldSizes
});
const { components } = useDocument.useDoc();
/**
* Transform the data before passing it to the form so that each field
* has a uniquely generated key
*/ const transformedData = React__namespace.useMemo(()=>{
const transform = (schemaAttributes, components = {})=>(document)=>{
const schema = {
attributes: schemaAttributes
};
const transformations = pipe(data.removeFieldsThatDontExistOnSchema(schema), data.prepareTempKeys(schema, components));
return transformations(document);
};
return transform(version.schema, components)(version.data);
}, [
components,
version.data,
version.schema
]);
return /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Layouts.Content, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 8,
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
disabled: true,
method: "PUT",
initialValues: transformedData,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: [
...layout,
...remainingFieldsLayout
].map((panel, index)=>{
return /*#__PURE__*/ jsxRuntime.jsx(FormPanel, {
panel: panel
}, index);
})
})
})
}),
removedAttributesAsFields.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Divider, {}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
paddingTop: 8,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
paddingBottom: 6,
gap: 1,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "delta",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.title',
defaultMessage: 'Unknown fields'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.message',
defaultMessage: 'These fields have been deleted or renamed in the Content-Type Builder. <b>These fields will not be restored.</b>'
}, {
b: (chunks)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
fontWeight: "bold",
children: chunks
})
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
disabled: true,
method: "PUT",
initialValues: version.data,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: unknownFieldsLayout.map((panel, index)=>{
return /*#__PURE__*/ jsxRuntime.jsx(FormPanel, {
panel: panel
}, index);
})
})
})
]
})
]
})
]
});
};
exports.VersionContent = VersionContent;
exports.getRemaingFieldsLayout = getRemaingFieldsLayout;
//# sourceMappingURL=VersionContent.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,242 @@
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { Layouts, Form } from '@strapi/admin/strapi-admin';
import { Box, Flex, Divider, Typography, Grid } from '@strapi/design-system';
import pipe from 'lodash/fp/pipe';
import { useIntl } from 'react-intl';
import { useDoc } from '../../hooks/useDocument.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { removeFieldsThatDontExistOnSchema, prepareTempKeys } from '../../pages/EditView/utils/data.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { VersionInputRenderer } from './VersionInputRenderer.mjs';
const createLayoutFromFields = (fields)=>{
return fields.reduce((rows, field)=>{
if (field.type === 'dynamiczone') {
// Dynamic zones take up all the columns in a row
rows.push([
field
]);
return rows;
}
if (!rows[rows.length - 1]) {
// Create a new row if there isn't one available
rows.push([]);
}
// Push fields to the current row, they wrap and handle their own column size
rows[rows.length - 1].push(field);
return rows;
}, [])// Map the rows to panels
.map((row)=>[
row
]);
};
/**
* Build a layout for the fields that are were deleted from the edit view layout
* via the configure the view page. This layout will be merged with the main one.
* Those fields would be restored if the user restores the history version, which is why it's
* important to show them, even if they're not in the normal layout.
*/ function getRemaingFieldsLayout({ layout, metadatas, schemaAttributes, fieldSizes }) {
const fieldsInLayout = layout.flatMap((panel)=>panel.flatMap((row)=>row.flatMap((field)=>field.name)));
const remainingFields = Object.entries(metadatas).reduce((currentRemainingFields, [name, field])=>{
// Make sure we do not fields that are not visible, e.g. "id"
if (!fieldsInLayout.includes(name) && field.edit.visible === true) {
const attribute = schemaAttributes[name];
// @ts-expect-error not sure why attribute causes type error
currentRemainingFields.push({
attribute,
type: attribute.type,
visible: true,
disabled: true,
label: field.edit.label || name,
name: name,
size: fieldSizes[attribute.type].default ?? 12
});
}
return currentRemainingFields;
}, []);
return createLayoutFromFields(remainingFields);
}
/* -------------------------------------------------------------------------------------------------
* FormPanel
* -----------------------------------------------------------------------------------------------*/ const FormPanel = ({ panel })=>{
if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
const [row] = panel;
const [field] = row;
return /*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
children: /*#__PURE__*/ jsx(Grid.Item, {
col: 12,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(VersionInputRenderer, {
...field
})
})
}, field.name);
}
return /*#__PURE__*/ jsx(Box, {
hasRadius: true,
background: "neutral0",
shadow: "tableShadow",
paddingLeft: 6,
paddingRight: 6,
paddingTop: 6,
paddingBottom: 6,
borderColor: "neutral150",
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: panel.map((row, gridRowIndex)=>/*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
children: row.map(({ size, ...field })=>{
return /*#__PURE__*/ jsx(Grid.Item, {
col: size,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(VersionInputRenderer, {
...field
})
}, field.name);
})
}, gridRowIndex))
})
});
};
const VersionContent = ()=>{
const { formatMessage } = useIntl();
const { fieldSizes } = useTypedSelector((state)=>state['content-manager'].app);
const version = useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const layout = useHistoryContext('VersionContent', (state)=>state.layout);
const configuration = useHistoryContext('VersionContent', (state)=>state.configuration);
const schema = useHistoryContext('VersionContent', (state)=>state.schema);
// Build a layout for the unknown fields section
const removedAttributes = version.meta.unknownAttributes.removed;
const removedAttributesAsFields = Object.entries(removedAttributes).map(([attributeName, attribute])=>{
const field = {
attribute,
shouldIgnoreRBAC: true,
type: attribute.type,
visible: true,
disabled: true,
label: attributeName,
name: attributeName,
size: fieldSizes[attribute.type].default ?? 12
};
return field;
});
const unknownFieldsLayout = createLayoutFromFields(removedAttributesAsFields);
// Build a layout for the fields that are were deleted from the layout
const remainingFieldsLayout = getRemaingFieldsLayout({
metadatas: configuration.contentType.metadatas,
layout,
schemaAttributes: schema.attributes,
fieldSizes
});
const { components } = useDoc();
/**
* Transform the data before passing it to the form so that each field
* has a uniquely generated key
*/ const transformedData = React.useMemo(()=>{
const transform = (schemaAttributes, components = {})=>(document)=>{
const schema = {
attributes: schemaAttributes
};
const transformations = pipe(removeFieldsThatDontExistOnSchema(schema), prepareTempKeys(schema, components));
return transformations(document);
};
return transform(version.schema, components)(version.data);
}, [
components,
version.data,
version.schema
]);
return /*#__PURE__*/ jsxs(Layouts.Content, {
children: [
/*#__PURE__*/ jsx(Box, {
paddingBottom: 8,
children: /*#__PURE__*/ jsx(Form, {
disabled: true,
method: "PUT",
initialValues: transformedData,
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: [
...layout,
...remainingFieldsLayout
].map((panel, index)=>{
return /*#__PURE__*/ jsx(FormPanel, {
panel: panel
}, index);
})
})
})
}),
removedAttributesAsFields.length > 0 && /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Divider, {}),
/*#__PURE__*/ jsxs(Box, {
paddingTop: 8,
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
paddingBottom: 6,
gap: 1,
children: [
/*#__PURE__*/ jsx(Typography, {
variant: "delta",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.title',
defaultMessage: 'Unknown fields'
})
}),
/*#__PURE__*/ jsx(Typography, {
variant: "pi",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.message',
defaultMessage: 'These fields have been deleted or renamed in the Content-Type Builder. <b>These fields will not be restored.</b>'
}, {
b: (chunks)=>/*#__PURE__*/ jsx(Typography, {
variant: "pi",
fontWeight: "bold",
children: chunks
})
})
})
]
}),
/*#__PURE__*/ jsx(Form, {
disabled: true,
method: "PUT",
initialValues: version.data,
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: unknownFieldsLayout.map((panel, index)=>{
return /*#__PURE__*/ jsx(FormPanel, {
panel: panel
}, index);
})
})
})
]
})
]
})
]
});
};
export { VersionContent, getRemaingFieldsLayout };
//# sourceMappingURL=VersionContent.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var plugin = require('../../constants/plugin.js');
var History = require('../pages/History.js');
var historyVersion = require('../services/historyVersion.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const VersionHeader = ({ headerId })=>{
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = React__namespace.useState(false);
const navigate = reactRouterDom.useNavigate();
const { formatMessage, formatDate } = reactIntl.useIntl();
const { trackUsage } = strapiAdmin.useTracking();
const { toggleNotification } = strapiAdmin.useNotification();
const [{ query }] = strapiAdmin.useQueryParams();
const { collectionType, slug } = reactRouterDom.useParams();
const [restoreVersion, { isLoading }] = historyVersion.useRestoreVersionMutation();
const { allowedActions } = strapiAdmin.useRBAC(plugin.PERMISSIONS.map((action)=>({
action,
subject: slug
})));
const version = History.useHistoryContext('VersionHeader', (state)=>state.selectedVersion);
const mainField = History.useHistoryContext('VersionHeader', (state)=>state.mainField);
const schema = History.useHistoryContext('VersionHeader', (state)=>state.schema);
const isCurrentVersion = History.useHistoryContext('VersionHeader', (state)=>state.page === 1 && state.versions.data[0].id === state.selectedVersion.id);
const mainFieldValue = version.data[mainField];
const getNextNavigation = ()=>{
const pluginsQueryParams = qs.stringify({
plugins: query.plugins
}, {
encode: false
});
return {
pathname: '..',
search: pluginsQueryParams
};
};
const handleRestore = async ()=>{
try {
const response = await restoreVersion({
documentId: version.relatedDocumentId,
collectionType,
params: {
versionId: version.id,
contentType: version.contentType
},
body: {
contentType: version.contentType
}
});
if ('data' in response) {
navigate(getNextNavigation(), {
relative: 'path'
});
toggleNotification({
type: 'success',
title: formatMessage({
id: 'content-manager.restore.success.title',
defaultMessage: 'Version restored.'
}),
message: formatMessage({
id: 'content-manager.restore.success.message',
defaultMessage: 'A past version of the content was restored.'
})
});
trackUsage('didRestoreHistoryVersion');
}
if ('error' in response) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.history.restore.error.message',
defaultMessage: 'Could not restore version.'
})
});
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Root, {
open: isConfirmDialogOpen,
onOpenChange: setIsConfirmDialogOpen,
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.BaseHeader, {
id: headerId,
title: formatDate(new Date(version.createdAt), {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
}),
subtitle: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "epsilon",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.version.subtitle',
defaultMessage: '{hasLocale, select, true {{subtitle}, in {locale}} other {{subtitle}}}'
}, {
hasLocale: Boolean(version.locale),
subtitle: `${mainFieldValue || ''} (${schema.info.singularName})`.trim(),
locale: version.locale?.name
})
}),
navigationAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icons.ArrowLeft, {}),
tag: reactRouterDom.NavLink,
to: getNextNavigation(),
relative: "path",
isExternal: false,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
}),
sticky: false,
primaryAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
disabled: !allowedActions.canUpdate || isCurrentVersion,
onClick: ()=>{
setIsConfirmDialogOpen(true);
},
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
onConfirm: handleRestore,
endAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "secondary",
onClick: handleRestore,
loading: isLoading,
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
}),
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
textAlign: "center",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
justifyContent: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.WarningCircle, {
width: "24px",
height: "24px",
fill: "danger600"
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.title',
defaultMessage: 'Are you sure you want to restore this version?'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.message',
defaultMessage: "{isDraft, select, true {The restored content will override your draft.} other {The restored content won't be published, it will override the draft and be saved as pending changes. You'll be able to publish the changes at anytime.}}"
}, {
isDraft: version.status === 'draft'
})
})
]
})
})
]
});
};
exports.VersionHeader = VersionHeader;
//# sourceMappingURL=VersionHeader.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,189 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useTracking, useNotification, useQueryParams, useRBAC, Layouts, ConfirmDialog } from '@strapi/admin/strapi-admin';
import { Dialog, Typography, Link, Button, Flex } from '@strapi/design-system';
import { ArrowLeft, WarningCircle } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useNavigate, useParams, NavLink } from 'react-router-dom';
import { PERMISSIONS } from '../../constants/plugin.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { useRestoreVersionMutation } from '../services/historyVersion.mjs';
const VersionHeader = ({ headerId })=>{
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = React.useState(false);
const navigate = useNavigate();
const { formatMessage, formatDate } = useIntl();
const { trackUsage } = useTracking();
const { toggleNotification } = useNotification();
const [{ query }] = useQueryParams();
const { collectionType, slug } = useParams();
const [restoreVersion, { isLoading }] = useRestoreVersionMutation();
const { allowedActions } = useRBAC(PERMISSIONS.map((action)=>({
action,
subject: slug
})));
const version = useHistoryContext('VersionHeader', (state)=>state.selectedVersion);
const mainField = useHistoryContext('VersionHeader', (state)=>state.mainField);
const schema = useHistoryContext('VersionHeader', (state)=>state.schema);
const isCurrentVersion = useHistoryContext('VersionHeader', (state)=>state.page === 1 && state.versions.data[0].id === state.selectedVersion.id);
const mainFieldValue = version.data[mainField];
const getNextNavigation = ()=>{
const pluginsQueryParams = stringify({
plugins: query.plugins
}, {
encode: false
});
return {
pathname: '..',
search: pluginsQueryParams
};
};
const handleRestore = async ()=>{
try {
const response = await restoreVersion({
documentId: version.relatedDocumentId,
collectionType,
params: {
versionId: version.id,
contentType: version.contentType
},
body: {
contentType: version.contentType
}
});
if ('data' in response) {
navigate(getNextNavigation(), {
relative: 'path'
});
toggleNotification({
type: 'success',
title: formatMessage({
id: 'content-manager.restore.success.title',
defaultMessage: 'Version restored.'
}),
message: formatMessage({
id: 'content-manager.restore.success.message',
defaultMessage: 'A past version of the content was restored.'
})
});
trackUsage('didRestoreHistoryVersion');
}
if ('error' in response) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.history.restore.error.message',
defaultMessage: 'Could not restore version.'
})
});
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
};
return /*#__PURE__*/ jsxs(Dialog.Root, {
open: isConfirmDialogOpen,
onOpenChange: setIsConfirmDialogOpen,
children: [
/*#__PURE__*/ jsx(Layouts.BaseHeader, {
id: headerId,
title: formatDate(new Date(version.createdAt), {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
}),
subtitle: /*#__PURE__*/ jsx(Typography, {
variant: "epsilon",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.version.subtitle',
defaultMessage: '{hasLocale, select, true {{subtitle}, in {locale}} other {{subtitle}}}'
}, {
hasLocale: Boolean(version.locale),
subtitle: `${mainFieldValue || ''} (${schema.info.singularName})`.trim(),
locale: version.locale?.name
})
}),
navigationAction: /*#__PURE__*/ jsx(Link, {
startIcon: /*#__PURE__*/ jsx(ArrowLeft, {}),
tag: NavLink,
to: getNextNavigation(),
relative: "path",
isExternal: false,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
}),
sticky: false,
primaryAction: /*#__PURE__*/ jsx(Dialog.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
disabled: !allowedActions.canUpdate || isCurrentVersion,
onClick: ()=>{
setIsConfirmDialogOpen(true);
},
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
})
})
}),
/*#__PURE__*/ jsx(ConfirmDialog, {
onConfirm: handleRestore,
endAction: /*#__PURE__*/ jsx(Button, {
variant: "secondary",
onClick: handleRestore,
loading: isLoading,
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
}),
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
textAlign: "center",
children: [
/*#__PURE__*/ jsx(Flex, {
justifyContent: "center",
children: /*#__PURE__*/ jsx(WarningCircle, {
width: "24px",
height: "24px",
fill: "danger600"
})
}),
/*#__PURE__*/ jsx(Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.title',
defaultMessage: 'Are you sure you want to restore this version?'
})
}),
/*#__PURE__*/ jsx(Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.message',
defaultMessage: "{isDraft, select, true {The restored content will override your draft.} other {The restored content won't be published, it will override the draft and be saved as pending changes. You'll be able to publish the changes at anytime.}}"
}, {
isDraft: version.status === 'draft'
})
})
]
})
})
]
});
};
export { VersionHeader };
//# sourceMappingURL=VersionHeader.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,490 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var collections = require('../../constants/collections.js');
var DocumentRBAC = require('../../features/DocumentRBAC.js');
var useDocument = require('../../hooks/useDocument.js');
var useDocumentLayout = require('../../hooks/useDocumentLayout.js');
var useLazyComponents = require('../../hooks/useLazyComponents.js');
var hooks = require('../../modules/hooks.js');
var DocumentStatus = require('../../pages/EditView/components/DocumentStatus.js');
var BlocksInput = require('../../pages/EditView/components/FormInputs/BlocksInput/BlocksInput.js');
var Input = require('../../pages/EditView/components/FormInputs/Component/Input.js');
var Field = require('../../pages/EditView/components/FormInputs/DynamicZone/Field.js');
var NotAllowed = require('../../pages/EditView/components/FormInputs/NotAllowed.js');
var UID = require('../../pages/EditView/components/FormInputs/UID.js');
var Field$1 = require('../../pages/EditView/components/FormInputs/Wysiwyg/Field.js');
var InputRenderer = require('../../pages/EditView/components/InputRenderer.js');
var relations = require('../../utils/relations.js');
var History = require('../pages/History.js');
var VersionContent = require('./VersionContent.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const StyledAlert = styledComponents.styled(designSystem.Alert).attrs({
closeLabel: 'Close',
onClose: ()=>{},
shadow: 'none'
})`
button {
display: none;
}
`;
/* -------------------------------------------------------------------------------------------------
* CustomRelationInput
* -----------------------------------------------------------------------------------------------*/ const LinkEllipsis = styledComponents.styled(designSystem.Link)`
display: block;
& > span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
`;
const CustomRelationInput = (props)=>{
const { formatMessage } = reactIntl.useIntl();
const field = strapiAdmin.useField(props.name);
/**
* Ideally the server would return the correct shape, however, for admin user relations
* it sanitizes everything out when it finds an object for the relation value.
*/ let formattedFieldValue;
if (field) {
formattedFieldValue = Array.isArray(field.value) ? {
results: field.value,
meta: {
missingCount: 0
}
} : field.value;
}
if (!formattedFieldValue || formattedFieldValue.results.length === 0 && formattedFieldValue.meta.missingCount === 0) {
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
action: props.labelAction,
children: props.label
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
marginTop: 1,
children: /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
variant: "default",
children: formatMessage({
id: 'content-manager.history.content.no-relations',
defaultMessage: 'No relations.'
})
})
})
]
});
}
const { results, meta } = formattedFieldValue;
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: props.label
}),
results.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
gap: 2,
marginTop: 1,
alignItems: "stretch",
children: results.map((relationData)=>{
// @ts-expect-error - targetModel does exist on the attribute. But it's not typed.
const { targetModel } = props.attribute;
const href = `../${collections.COLLECTION_TYPES}/${targetModel}/${relationData.documentId}`;
const label = relations.getRelationLabel(relationData, props.mainField);
const isAdminUserRelation = targetModel === 'admin::user';
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 4,
paddingRight: 4,
hasRadius: true,
borderColor: "neutral200",
background: "neutral150",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
label: label,
children: isAdminUserRelation ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: label
}) : /*#__PURE__*/ jsxRuntime.jsx(LinkEllipsis, {
tag: reactRouterDom.NavLink,
to: href,
children: label
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: relationData.status
})
]
}, relationData.documentId ?? relationData.id);
})
}),
meta.missingCount > 0 && /* @ts-expect-error we dont need closeLabel */ /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
marginTop: 1,
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.missing-relations.title',
defaultMessage: '{number, plural, =1 {Missing relation} other {{number} missing relations}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-relations.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* CustomMediaInput
* -----------------------------------------------------------------------------------------------*/ // Create an object with value at key path (i.e. 'a.b.c')
const createInitialValuesForPath = (keyPath, value)=>{
const keys = keyPath.split('.');
// The root level object
const root = {};
// Make the first node the root
let node = root;
keys.forEach((key, index)=>{
// Skip prototype pollution keys
if (key === '__proto__' || key === 'constructor') return;
// If it's the last key, set the node value
if (index === keys.length - 1) {
node[key] = value;
} else {
// Ensure the key exists and is an object
node[key] = node[key] || {};
}
// Traverse down the tree
node = node[key];
});
return root;
};
const CustomMediaInput = (props)=>{
const { value } = strapiAdmin.useField(props.name);
const results = value?.results ?? [];
const meta = value?.meta ?? {
missingCount: 0
};
const { formatMessage } = reactIntl.useIntl();
const fields = strapiAdmin.useStrapiApp('CustomMediaInput', (state)=>state.fields);
const MediaLibrary = fields.media;
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
method: "PUT",
disabled: true,
initialValues: createInitialValuesForPath(props.name, results),
children: /*#__PURE__*/ jsxRuntime.jsx(MediaLibrary, {
...props,
disabled: true,
multiple: results.length > 1
})
}),
meta.missingCount > 0 && /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
variant: "warning",
closeLabel: "Close",
onClose: ()=>{},
title: formatMessage({
id: 'content-manager.history.content.missing-assets.title',
defaultMessage: '{number, plural, =1 {Missing asset} other {{number} missing assets}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-assets.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted in the Media Library and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/**
* Checks if the i18n plugin added a label action to the field and modifies it
* to adapt the wording for the history page.
*/ const getLabelAction = (labelAction)=>{
if (!/*#__PURE__*/ React__namespace.isValidElement(labelAction)) {
return labelAction;
}
// TODO: find a better way to do this rather than access internals
const labelActionTitleId = labelAction.props.title.id;
if (labelActionTitleId === 'i18n.Field.localized') {
return /*#__PURE__*/ React__namespace.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.localized',
defaultMessage: 'This value is specific to this locale. If you restore this version, the content will not be replaced for other locales.'
}
});
}
if (labelActionTitleId === 'i18n.Field.not-localized') {
return /*#__PURE__*/ React__namespace.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.not-localized',
defaultMessage: 'This value is common to all locales. If you restore this version and save the changes, the content will be replaced for all locales.'
}
});
}
// Label action is unrelated to i18n, don't touch it.
return labelAction;
};
/**
* @internal
*
* @description An abstraction around the regular form input renderer designed specifically
* to be used on the History page in the content-manager. It understands how to render specific
* inputs within the context of a history version (i.e. relations, media, ignored RBAC, etc...)
*/ const VersionInputRenderer = ({ visible, hint: providedHint, shouldIgnoreRBAC = false, labelAction, ...props })=>{
const customLabelAction = getLabelAction(labelAction);
const { formatMessage } = reactIntl.useIntl();
const version = History.useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const configuration = History.useHistoryContext('VersionContent', (state)=>state.configuration);
const fieldSizes = hooks.useTypedSelector((state)=>state['content-manager'].app.fieldSizes);
const { id, components } = useDocument.useDoc();
const isFormDisabled = strapiAdmin.useForm('InputRenderer', (state)=>state.disabled);
const isInDynamicZone = Field.useDynamicZone('isInDynamicZone', (state)=>state.isInDynamicZone);
const canCreateFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canCreateFields);
const canReadFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canReadFields);
const canUpdateFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUpdateFields);
const canUserAction = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUserAction);
const editableFields = id ? canUpdateFields : canCreateFields;
const readableFields = id ? canReadFields : canCreateFields;
/**
* Component fields are always readable and editable,
* however the fields within them may not be.
*/ const canUserReadField = canUserAction(props.name, readableFields, props.type);
const canUserEditField = canUserAction(props.name, editableFields, props.type);
const fields = strapiAdmin.useStrapiApp('InputRenderer', (app)=>app.fields);
const { lazyComponentStore } = useLazyComponents.useLazyComponents(attributeHasCustomFieldProperty(props.attribute) ? [
props.attribute.customField
] : undefined);
const hint = InputRenderer.useFieldHint(providedHint, props.attribute);
const { edit: { components: componentsLayout } } = useDocumentLayout.useDocLayout();
if (!visible) {
return null;
}
/**
* Don't render the field if the user can't read it.
*/ if (!shouldIgnoreRBAC && !canUserReadField && !isInDynamicZone) {
return /*#__PURE__*/ jsxRuntime.jsx(NotAllowed.NotAllowedInput, {
hint: hint,
...props
});
}
const fieldIsDisabled = !canUserEditField && !isInDynamicZone || props.disabled || isFormDisabled;
/**
* Attributes found on the current content-type schema cannot be restored. We handle
* this by displaying a warning alert to the user instead of the input for that field type.
*/ const addedAttributes = version.meta.unknownAttributes.added;
if (Object.keys(addedAttributes).includes(props.name)) {
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
gap: 1,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: props.label
}),
/*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
width: "100%",
closeLabel: "Close",
onClose: ()=>{},
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.new-field.title',
defaultMessage: 'New field'
}),
children: formatMessage({
id: 'content-manager.history.content.new-field.message',
defaultMessage: "This field didn't exist when this version was saved. If you restore this version, it will be empty."
})
})
]
});
}
/**
* Because a custom field has a unique prop but the type could be confused with either
* the useField hook or the type of the field we need to handle it separately and first.
*/ if (attributeHasCustomFieldProperty(props.attribute)) {
const CustomInput = lazyComponentStore[props.attribute.customField];
if (CustomInput) {
return /*#__PURE__*/ jsxRuntime.jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLazyComponents hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error this workaround lets us display that the custom field is missing.
type: props.attribute.customField,
disabled: fieldIsDisabled
});
}
/**
* Since media fields use a custom input via the upload plugin provided by the useLibrary hook,
* we need to handle the them before other custom inputs coming from the useLibrary hook.
*/ if (props.type === 'media') {
return /*#__PURE__*/ jsxRuntime.jsx(CustomMediaInput, {
...props,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* This is where we handle ONLY the fields from the `useLibrary` hook.
*/ const addedInputTypes = Object.keys(fields);
if (!attributeHasCustomFieldProperty(props.attribute) && addedInputTypes.includes(props.type)) {
const CustomInput = fields[props.type];
return /*#__PURE__*/ jsxRuntime.jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLibrary hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* These include the content-manager specific fields, failing that we fall back
* to the more generic form input renderer.
*/ switch(props.type){
case 'blocks':
return /*#__PURE__*/ jsxRuntime.jsx(BlocksInput.BlocksInput, {
...props,
hint: hint,
type: props.type,
disabled: fieldIsDisabled
});
case 'component':
const { layout } = componentsLayout[props.attribute.component];
// Components can only have one panel, so only save the first layout item
const [remainingFieldsLayout] = VersionContent.getRemaingFieldsLayout({
layout: [
layout
],
metadatas: configuration.components[props.attribute.component].metadatas,
fieldSizes,
schemaAttributes: components[props.attribute.component].attributes
});
return /*#__PURE__*/ jsxRuntime.jsx(Input.ComponentInput, {
...props,
layout: [
...layout,
...remainingFieldsLayout || []
],
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'dynamiczone':
return /*#__PURE__*/ jsxRuntime.jsx(Field.DynamicZone, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'relation':
return /*#__PURE__*/ jsxRuntime.jsx(CustomRelationInput, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'richtext':
return /*#__PURE__*/ jsxRuntime.jsx(Field$1.Wysiwyg, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'uid':
return /*#__PURE__*/ jsxRuntime.jsx(UID.UIDInput, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
/**
* Enumerations are a special case because they require options.
*/ case 'enumeration':
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
options: props.attribute.enum.map((value)=>({
value
})),
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
default:
// These props are not needed for the generic form input renderer.
const { unique: _unique, mainField: _mainField, ...restProps } = props;
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...restProps,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
}
};
const attributeHasCustomFieldProperty = (attribute)=>'customField' in attribute && typeof attribute.customField === 'string';
exports.VersionInputRenderer = VersionInputRenderer;
//# sourceMappingURL=VersionInputRenderer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,469 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { useForm, useStrapiApp, InputRenderer, useField, Form } from '@strapi/admin/strapi-admin';
import { Alert, Link, Flex, Field, Box, Tooltip, Typography } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
import { COLLECTION_TYPES } from '../../constants/collections.mjs';
import { useDocumentRBAC } from '../../features/DocumentRBAC.mjs';
import { useDoc } from '../../hooks/useDocument.mjs';
import { useDocLayout } from '../../hooks/useDocumentLayout.mjs';
import { useLazyComponents } from '../../hooks/useLazyComponents.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { DocumentStatus } from '../../pages/EditView/components/DocumentStatus.mjs';
import { BlocksInput as MemoizedBlocksInput } from '../../pages/EditView/components/FormInputs/BlocksInput/BlocksInput.mjs';
import { ComponentInput as MemoizedComponentInput } from '../../pages/EditView/components/FormInputs/Component/Input.mjs';
import { useDynamicZone, DynamicZone } from '../../pages/EditView/components/FormInputs/DynamicZone/Field.mjs';
import { NotAllowedInput } from '../../pages/EditView/components/FormInputs/NotAllowed.mjs';
import { UIDInput as MemoizedUIDInput } from '../../pages/EditView/components/FormInputs/UID.mjs';
import { Wysiwyg as MemoizedWysiwyg } from '../../pages/EditView/components/FormInputs/Wysiwyg/Field.mjs';
import { useFieldHint } from '../../pages/EditView/components/InputRenderer.mjs';
import { getRelationLabel } from '../../utils/relations.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { getRemaingFieldsLayout } from './VersionContent.mjs';
const StyledAlert = styled(Alert).attrs({
closeLabel: 'Close',
onClose: ()=>{},
shadow: 'none'
})`
button {
display: none;
}
`;
/* -------------------------------------------------------------------------------------------------
* CustomRelationInput
* -----------------------------------------------------------------------------------------------*/ const LinkEllipsis = styled(Link)`
display: block;
& > span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
`;
const CustomRelationInput = (props)=>{
const { formatMessage } = useIntl();
const field = useField(props.name);
/**
* Ideally the server would return the correct shape, however, for admin user relations
* it sanitizes everything out when it finds an object for the relation value.
*/ let formattedFieldValue;
if (field) {
formattedFieldValue = Array.isArray(field.value) ? {
results: field.value,
meta: {
missingCount: 0
}
} : field.value;
}
if (!formattedFieldValue || formattedFieldValue.results.length === 0 && formattedFieldValue.meta.missingCount === 0) {
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Field.Label, {
action: props.labelAction,
children: props.label
}),
/*#__PURE__*/ jsx(Box, {
marginTop: 1,
children: /*#__PURE__*/ jsx(StyledAlert, {
variant: "default",
children: formatMessage({
id: 'content-manager.history.content.no-relations',
defaultMessage: 'No relations.'
})
})
})
]
});
}
const { results, meta } = formattedFieldValue;
return /*#__PURE__*/ jsxs(Box, {
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: props.label
}),
results.length > 0 && /*#__PURE__*/ jsx(Flex, {
direction: "column",
gap: 2,
marginTop: 1,
alignItems: "stretch",
children: results.map((relationData)=>{
// @ts-expect-error - targetModel does exist on the attribute. But it's not typed.
const { targetModel } = props.attribute;
const href = `../${COLLECTION_TYPES}/${targetModel}/${relationData.documentId}`;
const label = getRelationLabel(relationData, props.mainField);
const isAdminUserRelation = targetModel === 'admin::user';
return /*#__PURE__*/ jsxs(Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 4,
paddingRight: 4,
hasRadius: true,
borderColor: "neutral200",
background: "neutral150",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsx(Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsx(Tooltip, {
label: label,
children: isAdminUserRelation ? /*#__PURE__*/ jsx(Typography, {
children: label
}) : /*#__PURE__*/ jsx(LinkEllipsis, {
tag: NavLink,
to: href,
children: label
})
})
}),
/*#__PURE__*/ jsx(DocumentStatus, {
status: relationData.status
})
]
}, relationData.documentId ?? relationData.id);
})
}),
meta.missingCount > 0 && /* @ts-expect-error we dont need closeLabel */ /*#__PURE__*/ jsx(StyledAlert, {
marginTop: 1,
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.missing-relations.title',
defaultMessage: '{number, plural, =1 {Missing relation} other {{number} missing relations}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-relations.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* CustomMediaInput
* -----------------------------------------------------------------------------------------------*/ // Create an object with value at key path (i.e. 'a.b.c')
const createInitialValuesForPath = (keyPath, value)=>{
const keys = keyPath.split('.');
// The root level object
const root = {};
// Make the first node the root
let node = root;
keys.forEach((key, index)=>{
// Skip prototype pollution keys
if (key === '__proto__' || key === 'constructor') return;
// If it's the last key, set the node value
if (index === keys.length - 1) {
node[key] = value;
} else {
// Ensure the key exists and is an object
node[key] = node[key] || {};
}
// Traverse down the tree
node = node[key];
});
return root;
};
const CustomMediaInput = (props)=>{
const { value } = useField(props.name);
const results = value?.results ?? [];
const meta = value?.meta ?? {
missingCount: 0
};
const { formatMessage } = useIntl();
const fields = useStrapiApp('CustomMediaInput', (state)=>state.fields);
const MediaLibrary = fields.media;
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
children: [
/*#__PURE__*/ jsx(Form, {
method: "PUT",
disabled: true,
initialValues: createInitialValuesForPath(props.name, results),
children: /*#__PURE__*/ jsx(MediaLibrary, {
...props,
disabled: true,
multiple: results.length > 1
})
}),
meta.missingCount > 0 && /*#__PURE__*/ jsx(StyledAlert, {
variant: "warning",
closeLabel: "Close",
onClose: ()=>{},
title: formatMessage({
id: 'content-manager.history.content.missing-assets.title',
defaultMessage: '{number, plural, =1 {Missing asset} other {{number} missing assets}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-assets.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted in the Media Library and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/**
* Checks if the i18n plugin added a label action to the field and modifies it
* to adapt the wording for the history page.
*/ const getLabelAction = (labelAction)=>{
if (!/*#__PURE__*/ React.isValidElement(labelAction)) {
return labelAction;
}
// TODO: find a better way to do this rather than access internals
const labelActionTitleId = labelAction.props.title.id;
if (labelActionTitleId === 'i18n.Field.localized') {
return /*#__PURE__*/ React.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.localized',
defaultMessage: 'This value is specific to this locale. If you restore this version, the content will not be replaced for other locales.'
}
});
}
if (labelActionTitleId === 'i18n.Field.not-localized') {
return /*#__PURE__*/ React.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.not-localized',
defaultMessage: 'This value is common to all locales. If you restore this version and save the changes, the content will be replaced for all locales.'
}
});
}
// Label action is unrelated to i18n, don't touch it.
return labelAction;
};
/**
* @internal
*
* @description An abstraction around the regular form input renderer designed specifically
* to be used on the History page in the content-manager. It understands how to render specific
* inputs within the context of a history version (i.e. relations, media, ignored RBAC, etc...)
*/ const VersionInputRenderer = ({ visible, hint: providedHint, shouldIgnoreRBAC = false, labelAction, ...props })=>{
const customLabelAction = getLabelAction(labelAction);
const { formatMessage } = useIntl();
const version = useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const configuration = useHistoryContext('VersionContent', (state)=>state.configuration);
const fieldSizes = useTypedSelector((state)=>state['content-manager'].app.fieldSizes);
const { id, components } = useDoc();
const isFormDisabled = useForm('InputRenderer', (state)=>state.disabled);
const isInDynamicZone = useDynamicZone('isInDynamicZone', (state)=>state.isInDynamicZone);
const canCreateFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canCreateFields);
const canReadFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canReadFields);
const canUpdateFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUpdateFields);
const canUserAction = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUserAction);
const editableFields = id ? canUpdateFields : canCreateFields;
const readableFields = id ? canReadFields : canCreateFields;
/**
* Component fields are always readable and editable,
* however the fields within them may not be.
*/ const canUserReadField = canUserAction(props.name, readableFields, props.type);
const canUserEditField = canUserAction(props.name, editableFields, props.type);
const fields = useStrapiApp('InputRenderer', (app)=>app.fields);
const { lazyComponentStore } = useLazyComponents(attributeHasCustomFieldProperty(props.attribute) ? [
props.attribute.customField
] : undefined);
const hint = useFieldHint(providedHint, props.attribute);
const { edit: { components: componentsLayout } } = useDocLayout();
if (!visible) {
return null;
}
/**
* Don't render the field if the user can't read it.
*/ if (!shouldIgnoreRBAC && !canUserReadField && !isInDynamicZone) {
return /*#__PURE__*/ jsx(NotAllowedInput, {
hint: hint,
...props
});
}
const fieldIsDisabled = !canUserEditField && !isInDynamicZone || props.disabled || isFormDisabled;
/**
* Attributes found on the current content-type schema cannot be restored. We handle
* this by displaying a warning alert to the user instead of the input for that field type.
*/ const addedAttributes = version.meta.unknownAttributes.added;
if (Object.keys(addedAttributes).includes(props.name)) {
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
gap: 1,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: props.label
}),
/*#__PURE__*/ jsx(StyledAlert, {
width: "100%",
closeLabel: "Close",
onClose: ()=>{},
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.new-field.title',
defaultMessage: 'New field'
}),
children: formatMessage({
id: 'content-manager.history.content.new-field.message',
defaultMessage: "This field didn't exist when this version was saved. If you restore this version, it will be empty."
})
})
]
});
}
/**
* Because a custom field has a unique prop but the type could be confused with either
* the useField hook or the type of the field we need to handle it separately and first.
*/ if (attributeHasCustomFieldProperty(props.attribute)) {
const CustomInput = lazyComponentStore[props.attribute.customField];
if (CustomInput) {
return /*#__PURE__*/ jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLazyComponents hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
return /*#__PURE__*/ jsx(InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error this workaround lets us display that the custom field is missing.
type: props.attribute.customField,
disabled: fieldIsDisabled
});
}
/**
* Since media fields use a custom input via the upload plugin provided by the useLibrary hook,
* we need to handle the them before other custom inputs coming from the useLibrary hook.
*/ if (props.type === 'media') {
return /*#__PURE__*/ jsx(CustomMediaInput, {
...props,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* This is where we handle ONLY the fields from the `useLibrary` hook.
*/ const addedInputTypes = Object.keys(fields);
if (!attributeHasCustomFieldProperty(props.attribute) && addedInputTypes.includes(props.type)) {
const CustomInput = fields[props.type];
return /*#__PURE__*/ jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLibrary hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* These include the content-manager specific fields, failing that we fall back
* to the more generic form input renderer.
*/ switch(props.type){
case 'blocks':
return /*#__PURE__*/ jsx(MemoizedBlocksInput, {
...props,
hint: hint,
type: props.type,
disabled: fieldIsDisabled
});
case 'component':
const { layout } = componentsLayout[props.attribute.component];
// Components can only have one panel, so only save the first layout item
const [remainingFieldsLayout] = getRemaingFieldsLayout({
layout: [
layout
],
metadatas: configuration.components[props.attribute.component].metadatas,
fieldSizes,
schemaAttributes: components[props.attribute.component].attributes
});
return /*#__PURE__*/ jsx(MemoizedComponentInput, {
...props,
layout: [
...layout,
...remainingFieldsLayout || []
],
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'dynamiczone':
return /*#__PURE__*/ jsx(DynamicZone, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'relation':
return /*#__PURE__*/ jsx(CustomRelationInput, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'richtext':
return /*#__PURE__*/ jsx(MemoizedWysiwyg, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'uid':
return /*#__PURE__*/ jsx(MemoizedUIDInput, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
/**
* Enumerations are a special case because they require options.
*/ case 'enumeration':
return /*#__PURE__*/ jsx(InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
options: props.attribute.enum.map((value)=>({
value
})),
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
default:
// These props are not needed for the generic form input renderer.
const { unique: _unique, mainField: _mainField, ...restProps } = props;
return /*#__PURE__*/ jsx(InputRenderer, {
...restProps,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
}
};
const attributeHasCustomFieldProperty = (attribute)=>'customField' in attribute && typeof attribute.customField === 'string';
export { VersionInputRenderer };
//# sourceMappingURL=VersionInputRenderer.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var RelativeTime = require('../../components/RelativeTime.js');
var DocumentStatus = require('../../pages/EditView/components/DocumentStatus.js');
var users = require('../../utils/users.js');
var History = require('../pages/History.js');
/* -------------------------------------------------------------------------------------------------
* BlueText
* -----------------------------------------------------------------------------------------------*/ const BlueText = (children)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "primary600",
variant: "pi",
children: children
});
const VersionCard = ({ version, isCurrent })=>{
const { formatDate, formatMessage } = reactIntl.useIntl();
const [{ query }] = strapiAdmin.useQueryParams();
const isActive = query.id === version.id.toString();
const author = version.createdBy && users.getDisplayName(version.createdBy);
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
gap: 3,
hasRadius: true,
borderWidth: "1px",
borderStyle: "solid",
borderColor: isActive ? 'primary600' : 'neutral200',
color: "neutral800",
padding: 5,
tag: reactRouterDom.Link,
to: `?${qs.stringify({
...query,
id: version.id
})}`,
style: {
textDecoration: 'none'
},
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 1,
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "h3",
fontWeight: "semiBold",
children: formatDate(version.createdAt, {
day: 'numeric',
month: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "p",
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.sidebar.versionDescription',
defaultMessage: '{distanceToNow}{isAnonymous, select, true {} other { by {author}}}{isCurrent, select, true { <b>(current)</b>} other {}}'
}, {
distanceToNow: /*#__PURE__*/ jsxRuntime.jsx(RelativeTime.RelativeTime, {
timestamp: new Date(version.createdAt)
}),
author,
isAnonymous: !Boolean(version.createdBy),
isCurrent,
b: BlueText
})
})
]
}),
version.status && /*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: version.status,
size: "XS"
})
]
});
};
const PaginationButton = ({ page, children })=>{
const [{ query }] = strapiAdmin.useQueryParams();
// Remove the id from the pagination link, so that the history page can redirect
// to the id of the first history version in the new page once it's loaded
const { id: _id, ...queryRest } = query;
return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Link, {
to: {
search: qs.stringify({
...queryRest,
page
})
},
style: {
textDecoration: 'none'
},
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "omega",
textColor: "primary600",
children: children
})
});
};
/* -------------------------------------------------------------------------------------------------
* VersionsList
* -----------------------------------------------------------------------------------------------*/ const VersionsList = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { versions, page } = History.useHistoryContext('VersionsList', (state)=>({
versions: state.versions,
page: state.page
}));
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
shrink: 0,
direction: "column",
alignItems: "stretch",
width: "320px",
height: "100vh",
background: "neutral0",
borderColor: "neutral200",
borderWidth: "0 0 0 1px",
borderStyle: "solid",
tag: "aside",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "row",
justifyContent: "space-between",
padding: 4,
borderColor: "neutral200",
borderWidth: "0 0 1px",
borderStyle: "solid",
tag: "header",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "h2",
variant: "omega",
fontWeight: "semiBold",
children: formatMessage({
id: 'content-manager.history.sidebar.title',
defaultMessage: 'Versions'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
background: "neutral150",
hasRadius: true,
padding: 1,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "sigma",
textColor: "neutral600",
children: versions.meta.pagination.total
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
flex: 1,
overflow: "auto",
children: [
versions.meta.pagination.page > 1 && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingTop: 4,
textAlign: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(PaginationButton, {
page: page - 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-newer',
defaultMessage: 'Show newer versions'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
gap: 3,
padding: 4,
tag: "ul",
alignItems: "stretch",
children: versions.data.map((version, index)=>/*#__PURE__*/ jsxRuntime.jsx("li", {
"aria-label": formatMessage({
id: 'content-manager.history.sidebar.title.version-card.aria-label',
defaultMessage: 'Version card'
}),
children: /*#__PURE__*/ jsxRuntime.jsx(VersionCard, {
version: version,
isCurrent: page === 1 && index === 0
})
}, version.id))
}),
versions.meta.pagination.page < versions.meta.pagination.pageCount && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 4,
textAlign: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(PaginationButton, {
page: page + 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-older',
defaultMessage: 'Show older versions'
})
})
})
]
})
]
});
};
exports.VersionsList = VersionsList;
//# sourceMappingURL=VersionsList.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,208 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import 'react';
import { useQueryParams } from '@strapi/admin/strapi-admin';
import { Flex, Typography, Box } from '@strapi/design-system';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { RelativeTime } from '../../components/RelativeTime.mjs';
import { DocumentStatus } from '../../pages/EditView/components/DocumentStatus.mjs';
import { getDisplayName } from '../../utils/users.mjs';
import { useHistoryContext } from '../pages/History.mjs';
/* -------------------------------------------------------------------------------------------------
* BlueText
* -----------------------------------------------------------------------------------------------*/ const BlueText = (children)=>/*#__PURE__*/ jsx(Typography, {
textColor: "primary600",
variant: "pi",
children: children
});
const VersionCard = ({ version, isCurrent })=>{
const { formatDate, formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const isActive = query.id === version.id.toString();
const author = version.createdBy && getDisplayName(version.createdBy);
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
gap: 3,
hasRadius: true,
borderWidth: "1px",
borderStyle: "solid",
borderColor: isActive ? 'primary600' : 'neutral200',
color: "neutral800",
padding: 5,
tag: Link,
to: `?${stringify({
...query,
id: version.id
})}`,
style: {
textDecoration: 'none'
},
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 1,
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsx(Typography, {
tag: "h3",
fontWeight: "semiBold",
children: formatDate(version.createdAt, {
day: 'numeric',
month: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}),
/*#__PURE__*/ jsx(Typography, {
tag: "p",
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.sidebar.versionDescription',
defaultMessage: '{distanceToNow}{isAnonymous, select, true {} other { by {author}}}{isCurrent, select, true { <b>(current)</b>} other {}}'
}, {
distanceToNow: /*#__PURE__*/ jsx(RelativeTime, {
timestamp: new Date(version.createdAt)
}),
author,
isAnonymous: !Boolean(version.createdBy),
isCurrent,
b: BlueText
})
})
]
}),
version.status && /*#__PURE__*/ jsx(DocumentStatus, {
status: version.status,
size: "XS"
})
]
});
};
const PaginationButton = ({ page, children })=>{
const [{ query }] = useQueryParams();
// Remove the id from the pagination link, so that the history page can redirect
// to the id of the first history version in the new page once it's loaded
const { id: _id, ...queryRest } = query;
return /*#__PURE__*/ jsx(Link, {
to: {
search: stringify({
...queryRest,
page
})
},
style: {
textDecoration: 'none'
},
children: /*#__PURE__*/ jsx(Typography, {
variant: "omega",
textColor: "primary600",
children: children
})
});
};
/* -------------------------------------------------------------------------------------------------
* VersionsList
* -----------------------------------------------------------------------------------------------*/ const VersionsList = ()=>{
const { formatMessage } = useIntl();
const { versions, page } = useHistoryContext('VersionsList', (state)=>({
versions: state.versions,
page: state.page
}));
return /*#__PURE__*/ jsxs(Flex, {
shrink: 0,
direction: "column",
alignItems: "stretch",
width: "320px",
height: "100vh",
background: "neutral0",
borderColor: "neutral200",
borderWidth: "0 0 0 1px",
borderStyle: "solid",
tag: "aside",
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "row",
justifyContent: "space-between",
padding: 4,
borderColor: "neutral200",
borderWidth: "0 0 1px",
borderStyle: "solid",
tag: "header",
children: [
/*#__PURE__*/ jsx(Typography, {
tag: "h2",
variant: "omega",
fontWeight: "semiBold",
children: formatMessage({
id: 'content-manager.history.sidebar.title',
defaultMessage: 'Versions'
})
}),
/*#__PURE__*/ jsx(Box, {
background: "neutral150",
hasRadius: true,
padding: 1,
children: /*#__PURE__*/ jsx(Typography, {
variant: "sigma",
textColor: "neutral600",
children: versions.meta.pagination.total
})
})
]
}),
/*#__PURE__*/ jsxs(Box, {
flex: 1,
overflow: "auto",
children: [
versions.meta.pagination.page > 1 && /*#__PURE__*/ jsx(Box, {
paddingTop: 4,
textAlign: "center",
children: /*#__PURE__*/ jsx(PaginationButton, {
page: page - 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-newer',
defaultMessage: 'Show newer versions'
})
})
}),
/*#__PURE__*/ jsx(Flex, {
direction: "column",
gap: 3,
padding: 4,
tag: "ul",
alignItems: "stretch",
children: versions.data.map((version, index)=>/*#__PURE__*/ jsx("li", {
"aria-label": formatMessage({
id: 'content-manager.history.sidebar.title.version-card.aria-label',
defaultMessage: 'Version card'
}),
children: /*#__PURE__*/ jsx(VersionCard, {
version: version,
isCurrent: page === 1 && index === 0
})
}, version.id))
}),
versions.meta.pagination.page < versions.meta.pagination.pageCount && /*#__PURE__*/ jsx(Box, {
paddingBottom: 4,
textAlign: "center",
children: /*#__PURE__*/ jsx(PaginationButton, {
page: page + 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-older',
defaultMessage: 'Show older versions'
})
})
})
]
})
]
});
};
export { VersionsList };
//# sourceMappingURL=VersionsList.mjs.map

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More