mirror of
https://github.com/strapi/strapi.git
synced 2025-09-03 22:03:08 +00:00
Merge pull request #5322 from strapi/single-types/uid-ctm
Add UID in CTM and fix Single types alignments
This commit is contained in:
commit
0bb347f79b
Binary file not shown.
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 352 KiB |
@ -26,7 +26,7 @@ const Wrapper = styled.div`
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.05rem;
|
||||
vertical-align: middle;
|
||||
color: ${props => props.theme.main.colors.strapi['gray-light']};
|
||||
color: ${({ theme }) => theme.main.colors.strapi.grayLight};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const A = styled.a`
|
||||
position: relative;
|
||||
padding-top: 0.8rem;
|
||||
padding-top: 0.7rem;
|
||||
padding-bottom: 0.2rem;
|
||||
padding-left: 1.6rem;
|
||||
min-height: 3.6rem;
|
||||
|
@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
||||
position: absolute;
|
||||
top: calc(50% - 0.9rem + 0.5rem);
|
||||
top: calc(50% - 0.9rem + 0.3rem);
|
||||
left: 1.6rem;
|
||||
margin-right: 1.2rem;
|
||||
font-size: ${props => (props.small ? '1rem' : '1.4rem')};
|
||||
@ -14,9 +14,7 @@ const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const LeftMenuIcon = ({ icon }) => (
|
||||
<FaIcon small={icon === 'circle'} icon={icon} />
|
||||
);
|
||||
const LeftMenuIcon = ({ icon }) => <FaIcon small={icon === 'circle'} icon={icon} />;
|
||||
|
||||
LeftMenuIcon.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
|
@ -2,9 +2,9 @@ import styled from 'styled-components';
|
||||
|
||||
const Search = styled.input`
|
||||
width: 100%;
|
||||
padding: 0 21px;
|
||||
padding: 0 15px;
|
||||
outline: 0;
|
||||
font-size: 1.3rem;
|
||||
font-size: 1.2rem;
|
||||
color: ${props => props.theme.main.colors.white};
|
||||
`;
|
||||
|
||||
|
@ -5,13 +5,14 @@ const Title = styled.div`
|
||||
justify-content: space-between;
|
||||
padding-left: 2rem;
|
||||
padding-right: 1.6rem;
|
||||
padding-top: 0.7rem;
|
||||
padding-top: 1rem;
|
||||
margin-bottom: 0.8rem;
|
||||
color: ${props => props.theme.main.colors.leftMenu['title-color']};
|
||||
text-transform: uppercase;
|
||||
font-size: 1.1rem;
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.1rem;
|
||||
font-weight: 800;
|
||||
max-height: 28px;
|
||||
`;
|
||||
|
||||
Title.defaultProps = {
|
||||
|
@ -6,7 +6,7 @@ const EmptyLinksList = styled.div`
|
||||
padding-right: 1.6rem;
|
||||
font-weight: 300;
|
||||
min-height: 3.6rem;
|
||||
padding-top: 0.2rem;
|
||||
padding-top: 0.6rem;
|
||||
`;
|
||||
|
||||
EmptyLinksList.defaultProps = {
|
||||
|
@ -10,12 +10,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { get } from 'lodash';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
ButtonDropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownToggle,
|
||||
} from 'reactstrap';
|
||||
import { ButtonDropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import { auth } from 'strapi-helper-plugin';
|
||||
import Wrapper from './components';
|
||||
|
||||
@ -26,14 +21,14 @@ const Logout = ({ history: { push } }) => {
|
||||
const id = get(auth.getUserInfo(), 'id');
|
||||
|
||||
push({
|
||||
pathname: `/plugins/content-manager/strapi::administrator/${id}`,
|
||||
pathname: `/plugins/content-manager/collectionType/strapi::administrator/${id}`,
|
||||
search:
|
||||
'?redirectUrl=/plugins/content-manager/strapi::administrator/&_page=0&_limit=0&_sort=id',
|
||||
'?redirectUrl=/plugins/content-manager/collectionType/strapi::administrator/&_page=0&_limit=0&_sort=id',
|
||||
});
|
||||
};
|
||||
const handleGoToAdministrator = () => {
|
||||
push({
|
||||
pathname: '/plugins/content-manager/strapi::administrator',
|
||||
pathname: '/plugins/content-manager/collectionType/strapi::administrator',
|
||||
});
|
||||
};
|
||||
const handleLogout = () => {
|
||||
|
@ -10,8 +10,9 @@ const colors = {
|
||||
pink: '#ff5b77',
|
||||
purple: '#613d7c',
|
||||
gray: '#464a4c',
|
||||
border: '#E3E9F3',
|
||||
'gray-dark': '#292b2c',
|
||||
'gray-light': '#636c72',
|
||||
grayLight: '#636c72',
|
||||
'gray-lighter': '#eceeef',
|
||||
'gray-lightest': '#f7f7f9',
|
||||
|
||||
|
@ -12,9 +12,7 @@ export { default as Button } from './components/Button';
|
||||
export { default as ButtonModal } from './components/ButtonModal';
|
||||
export { default as CircleButton } from './components/CircleButton';
|
||||
export { default as ContainerFluid } from './components/ContainerFluid';
|
||||
export {
|
||||
default as EmptyAttributesBlock,
|
||||
} from './components/EmptyAttributesBlock';
|
||||
export { default as EmptyAttributesBlock } from './components/EmptyAttributesBlock';
|
||||
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
||||
export { default as ExtendComponent } from './components/ExtendComponent';
|
||||
export { default as GlobalPagination } from './components/GlobalPagination';
|
||||
@ -24,54 +22,32 @@ export { default as HeaderModalTitle } from './components/HeaderModalTitle';
|
||||
export { default as IcoContainer } from './components/IcoContainer';
|
||||
export { default as InputAddon } from './components/InputAddon';
|
||||
|
||||
export {
|
||||
default as InputAddonWithErrors,
|
||||
} from './components/InputAddonWithErrors';
|
||||
export { default as InputAddonWithErrors } from './components/InputAddonWithErrors';
|
||||
export { default as InputCheckbox } from './components/InputCheckbox';
|
||||
export {
|
||||
default as InputCheckboxWithErrors,
|
||||
} from './components/InputCheckboxWithErrors';
|
||||
export { default as InputCheckboxWithErrors } from './components/InputCheckboxWithErrors';
|
||||
export { default as InputDate } from './components/InputDate';
|
||||
export {
|
||||
default as InputDateWithErrors,
|
||||
} from './components/InputDateWithErrors';
|
||||
export { default as InputDateWithErrors } from './components/InputDateWithErrors';
|
||||
export { default as InputDescription } from './components/InputDescription';
|
||||
export { default as InputEmail } from './components/InputEmail';
|
||||
export {
|
||||
default as InputEmailWithErrors,
|
||||
} from './components/InputEmailWithErrors';
|
||||
export { default as InputEmailWithErrors } from './components/InputEmailWithErrors';
|
||||
export { default as InputErrors } from './components/InputErrors';
|
||||
export { default as InputFile } from './components/InputFile';
|
||||
export { default as InputNumber } from './components/InputNumber';
|
||||
export {
|
||||
default as InputNumberWithErrors,
|
||||
} from './components/InputNumberWithErrors';
|
||||
export { default as InputNumberWithErrors } from './components/InputNumberWithErrors';
|
||||
export { default as InputPassword } from './components/InputPassword';
|
||||
export {
|
||||
default as InputPasswordWithErrors,
|
||||
} from './components/InputPasswordWithErrors';
|
||||
export { default as InputPasswordWithErrors } from './components/InputPasswordWithErrors';
|
||||
export { default as InputSearch } from './components/InputSearch';
|
||||
export {
|
||||
default as InputSearchWithErrors,
|
||||
} from './components/InputSearchWithErrors';
|
||||
export { default as InputSearchWithErrors } from './components/InputSearchWithErrors';
|
||||
export { default as InputSelect } from './components/InputSelect';
|
||||
export {
|
||||
default as InputSelectWithErrors,
|
||||
} from './components/InputSelectWithErrors';
|
||||
export { default as InputSelectWithErrors } from './components/InputSelectWithErrors';
|
||||
export { default as InputsIndex } from './components/InputsIndex';
|
||||
export { default as InputSpacer } from './components/InputSpacer';
|
||||
export { default as InputText } from './components/InputText';
|
||||
export {
|
||||
default as InputTextWithErrors,
|
||||
} from './components/InputTextWithErrors';
|
||||
export { default as InputTextWithErrors } from './components/InputTextWithErrors';
|
||||
export { default as InputTextArea } from './components/InputTextArea';
|
||||
export {
|
||||
default as InputTextAreaWithErrors,
|
||||
} from './components/InputTextAreaWithErrors';
|
||||
export { default as InputTextAreaWithErrors } from './components/InputTextAreaWithErrors';
|
||||
export { default as InputToggle } from './components/InputToggle';
|
||||
export {
|
||||
default as InputToggleWithErrors,
|
||||
} from './components/InputToggleWithErrors';
|
||||
export { default as InputToggleWithErrors } from './components/InputToggleWithErrors';
|
||||
|
||||
export { default as Label } from './components/Label';
|
||||
export { default as LeftMenu } from './components/LeftMenu';
|
||||
@ -86,9 +62,7 @@ export { default as ListTitle } from './components/ListTitle';
|
||||
|
||||
export { default as LoadingBar } from './components/LoadingBar';
|
||||
export { default as LoadingIndicator } from './components/LoadingIndicator';
|
||||
export {
|
||||
default as LoadingIndicatorPage,
|
||||
} from './components/LoadingIndicatorPage';
|
||||
export { default as LoadingIndicatorPage } from './components/LoadingIndicatorPage';
|
||||
|
||||
export { default as Modal } from './components/Modal';
|
||||
export { default as ModalBody } from './components/BodyModal';
|
||||
@ -105,11 +79,7 @@ export { default as TrashButton } from './components/TrashButton';
|
||||
export { default as ViewContainer } from './components/ViewContainer';
|
||||
|
||||
// Contexts
|
||||
export {
|
||||
GlobalContext,
|
||||
GlobalContextProvider,
|
||||
useGlobalContext,
|
||||
} from './contexts/GlobalContext';
|
||||
export { GlobalContext, GlobalContextProvider, useGlobalContext } from './contexts/GlobalContext';
|
||||
|
||||
// Utils
|
||||
export { default as auth } from './utils/auth';
|
||||
|
@ -58,12 +58,7 @@ const CustomTable = ({ data, headers, isBulkable }) => {
|
||||
handleGoTo(row.id);
|
||||
}}
|
||||
>
|
||||
<Row
|
||||
isBulkable={isBulkable}
|
||||
headers={headers}
|
||||
row={row}
|
||||
goTo={handleGoTo}
|
||||
/>
|
||||
<Row isBulkable={isBulkable} headers={headers} row={row} goTo={handleGoTo} />
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
@ -73,9 +68,7 @@ const CustomTable = ({ data, headers, isBulkable }) => {
|
||||
<Table className="table">
|
||||
<TableHeader headers={headers} isBulkable={isBulkable} />
|
||||
<tbody>
|
||||
{entriesToDelete.length > 0 && (
|
||||
<ActionCollapse colSpan={colSpanLength} />
|
||||
)}
|
||||
{entriesToDelete.length > 0 && <ActionCollapse colSpan={colSpanLength} />}
|
||||
{content}
|
||||
</tbody>
|
||||
</Table>
|
||||
@ -91,13 +84,6 @@ CustomTable.defaultProps = {
|
||||
CustomTable.propTypes = {
|
||||
data: PropTypes.array,
|
||||
headers: PropTypes.array,
|
||||
history: PropTypes.shape({
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string,
|
||||
search: PropTypes.string,
|
||||
}),
|
||||
push: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
isBulkable: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components';
|
||||
import { InputText } from '@buffetjs/core';
|
||||
import { colors } from '@buffetjs/styles';
|
||||
|
||||
const InputUID = styled(InputText)`
|
||||
width: 100%;
|
||||
${({ error }) =>
|
||||
error &&
|
||||
`
|
||||
> input {
|
||||
border-color: ${colors.darkOrange};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export default InputUID;
|
@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Option = styled.div`
|
||||
&:hover {
|
||||
background-color: #e4f0fc;
|
||||
.right-label {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
line-height: 2.6rem;
|
||||
font-size: 1.5rem;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.right-label {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Option;
|
@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const OptionsTitle = styled.div`
|
||||
line-height: 2.1rem;
|
||||
font-size: 1.2rem;
|
||||
padding: 5px;
|
||||
text-transform: uppercase;
|
||||
color: ${({ theme }) => theme.main.colors.grayLight};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.main.colors.border};
|
||||
`;
|
||||
|
||||
export default OptionsTitle;
|
@ -0,0 +1,9 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const RightOptionLabel = styled.div`
|
||||
color: ${({ theme }) => theme.main.colors.strapi.blue};
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export default RightOptionLabel;
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import getTrad from '../../../utils/getTrad'
|
||||
import OptionsWrapper from './wrapper';
|
||||
import Option from './Option';
|
||||
import OptionsTitle from './OptionsTitle';
|
||||
import RightOptionLabel from './RightOptionLabel';
|
||||
|
||||
const Options = ({ options, title }) => (
|
||||
<OptionsWrapper>
|
||||
{title && <OptionsTitle>{title}</OptionsTitle>}
|
||||
{options.map(option => (
|
||||
<Option key={option.id} onClick={option.onClick}>
|
||||
<div>{option.label}</div>
|
||||
|
||||
<FormattedMessage id={getTrad('components.uid.apply')}>
|
||||
{msg => <RightOptionLabel className="right-label">{msg}</RightOptionLabel>}
|
||||
</FormattedMessage>
|
||||
</Option>
|
||||
))}
|
||||
</OptionsWrapper>
|
||||
);
|
||||
|
||||
Options.propTypes = {
|
||||
options: PropTypes.array.isRequired,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
Options.defaultProps = {
|
||||
title: null,
|
||||
};
|
||||
|
||||
export default Options;
|
@ -0,0 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const wrapper = styled.div`
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
margin-top: 3px;
|
||||
border: 1px solid ${props => props.theme.main.colors.border};
|
||||
border-radius: 2px;
|
||||
background-color: white;
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
export default wrapper;
|
@ -0,0 +1,18 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const RegenerateButton = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 32px;
|
||||
background-color: #fafafb;
|
||||
z-index: 10;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #aed4fb;
|
||||
}
|
||||
`;
|
||||
|
||||
export default RegenerateButton;
|
@ -0,0 +1,14 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const RightContent = styled.div`
|
||||
display: flex;
|
||||
z-index: 10;
|
||||
background-color: ${({ theme }) => theme.main.colors.white};
|
||||
align-items: center;
|
||||
line-height: 32px;
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
export default RightContent;
|
@ -0,0 +1,69 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Success, Remove } from '@buffetjs/icons';
|
||||
import styled from 'styled-components';
|
||||
import { useGlobalContext } from 'strapi-helper-plugin';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
import getTrad from '../../utils/getTrad';
|
||||
|
||||
// Note you don't need to create a specific file for this one
|
||||
// as it will soon be replaced by the Text one so you can leave it in this file.
|
||||
const RightContentLabel = styled.div`
|
||||
padding: 0 5px;
|
||||
text-transform: capitalize;
|
||||
color: ${({ theme, color }) => theme.main.colors[color]};
|
||||
`;
|
||||
|
||||
const RightLabel = ({ label, availability }) => {
|
||||
const { formatMessage } = useGlobalContext();
|
||||
|
||||
if (label) {
|
||||
return (
|
||||
<RightContentLabel color="blue">
|
||||
{formatMessage({
|
||||
id: getTrad('components.uid.regenerate'),
|
||||
})}
|
||||
</RightContentLabel>
|
||||
);
|
||||
}
|
||||
|
||||
if (availability !== null) {
|
||||
// This should be more generic in the futur.
|
||||
return availability.isAvailable ? (
|
||||
<>
|
||||
<Success fill="#27b70f" width="20px" height="20px" />
|
||||
<RightContentLabel color="green">
|
||||
{formatMessage({
|
||||
id: `${pluginId}.components.uid.available`,
|
||||
})}
|
||||
</RightContentLabel>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Remove fill="#ff203c" width="12px" height="12px" />
|
||||
<RightContentLabel color="red">
|
||||
{formatMessage({
|
||||
id: getTrad('components.uid.unavailable'),
|
||||
})}
|
||||
</RightContentLabel>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
RightLabel.propTypes = {
|
||||
label: PropTypes.string,
|
||||
availability: PropTypes.shape({
|
||||
isAvailable: PropTypes.bool,
|
||||
}),
|
||||
};
|
||||
|
||||
RightLabel.defaultProps = {
|
||||
label: null,
|
||||
availability: null,
|
||||
};
|
||||
|
||||
export default RightLabel;
|
@ -0,0 +1,265 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Sync } from '@buffetjs/icons';
|
||||
import { ErrorMessage as BaseErrorMessage } from '@buffetjs/styles';
|
||||
import { Label, Error } from '@buffetjs/core';
|
||||
import { useDebounce, useClickAwayListener } from '@buffetjs/hooks';
|
||||
import styled from 'styled-components';
|
||||
import { request, LoadingIndicator } from 'strapi-helper-plugin';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
import getRequestUrl from '../../utils/getRequestUrl';
|
||||
import useDataManager from '../../hooks/useDataManager';
|
||||
import RightLabel from './RightLabel';
|
||||
import Options from './Options';
|
||||
import RegenerateButton from './RegenerateButton';
|
||||
import RightContent from './RightContent';
|
||||
import Input from './InputUID';
|
||||
|
||||
// There is no need to create additional files for those little components.
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
padding-bottom: 23px;
|
||||
`;
|
||||
const InputContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
const ErrorMessage = styled(BaseErrorMessage)`
|
||||
padding-top: 10px;
|
||||
`;
|
||||
const Name = styled(Label)`
|
||||
display: block;
|
||||
text-transform: capitalize;
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
// This component should be in buffetjs. It will be used in the media lib.
|
||||
// This component will be the strapi custom dropdown component.
|
||||
// TODO : Make this component generic -> InputDropdown.
|
||||
// TODO : Use the Compounds components pattern
|
||||
// https://blog.bitsrc.io/understanding-compound-components-in-react-23c4b84535b5
|
||||
const InputUID = ({
|
||||
attribute,
|
||||
contentTypeUID,
|
||||
error: inputError,
|
||||
name,
|
||||
onChange,
|
||||
required,
|
||||
validations,
|
||||
value,
|
||||
}) => {
|
||||
const { modifiedData, initialData } = useDataManager();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [availability, setAvailability] = useState(null);
|
||||
const [isSuggestionOpen, setIsSuggestionOpen] = useState(true);
|
||||
const [isCustomized, setIsCustomized] = useState(false);
|
||||
const [label, setLabel] = useState();
|
||||
const debouncedValue = useDebounce(value, 300);
|
||||
const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300);
|
||||
const wrapperRef = useRef(null);
|
||||
const generateUid = useRef();
|
||||
const initialValue = initialData[name];
|
||||
const isCreation = isEmpty(initialData);
|
||||
|
||||
generateUid.current = async () => {
|
||||
setIsLoading(true);
|
||||
const requestURL = getRequestUrl('explorer/uid/generate');
|
||||
try {
|
||||
const { data } = await request(requestURL, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
contentTypeUID,
|
||||
field: name,
|
||||
data: modifiedData,
|
||||
},
|
||||
});
|
||||
onChange({ target: { name, value: data, type: 'text' } });
|
||||
setIsLoading(false);
|
||||
} catch (err) {
|
||||
console.error({ err });
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const checkAvailability = async () => {
|
||||
setIsLoading(true);
|
||||
const requestURL = getRequestUrl('explorer/uid/check-availability');
|
||||
try {
|
||||
const data = await request(requestURL, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
contentTypeUID,
|
||||
field: name,
|
||||
value: value || null,
|
||||
},
|
||||
});
|
||||
setAvailability(data);
|
||||
|
||||
if (data.suggestion) {
|
||||
setIsSuggestionOpen(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
} catch (err) {
|
||||
console.error({ err });
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!value && required) {
|
||||
generateUid.current();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedValue && debouncedValue !== initialValue) {
|
||||
checkAvailability();
|
||||
}
|
||||
if (!debouncedValue) {
|
||||
setAvailability(null);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedValue, initialValue]);
|
||||
|
||||
useEffect(() => {
|
||||
let timer;
|
||||
|
||||
if (availability && availability.isAvailable) {
|
||||
timer = setTimeout(() => {
|
||||
setAvailability(null);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
}, [availability]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCustomized && isCreation && debouncedTargetFieldValue !== null) {
|
||||
generateUid.current();
|
||||
}
|
||||
}, [debouncedTargetFieldValue, isCustomized, isCreation]);
|
||||
|
||||
useClickAwayListener(wrapperRef, () => setIsSuggestionOpen(false));
|
||||
|
||||
const handleFocus = () => {
|
||||
if (availability && availability.suggestion) {
|
||||
setIsSuggestionOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = () => {
|
||||
setIsSuggestionOpen(false);
|
||||
onChange({ target: { name, value: availability.suggestion, type: 'text' } });
|
||||
};
|
||||
|
||||
const handleGenerateMouseEnter = () => {
|
||||
setLabel('regenerate');
|
||||
};
|
||||
|
||||
const handleGenerateMouseLeave = () => {
|
||||
setLabel(null);
|
||||
};
|
||||
|
||||
const handleChange = (e, canCheck, dispatch) => {
|
||||
if (!canCheck) {
|
||||
dispatch({
|
||||
type: 'SET_CHECK',
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'SET_ERROR',
|
||||
error: null,
|
||||
});
|
||||
|
||||
if (e.target.value && isCreation) {
|
||||
setIsCustomized(true);
|
||||
}
|
||||
|
||||
onChange(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<Error name={name} inputError={inputError} type="text" validations={validations}>
|
||||
{({ canCheck, onBlur, error, dispatch }) => {
|
||||
const hasError = error && error !== null;
|
||||
|
||||
return (
|
||||
<Wrapper ref={wrapperRef}>
|
||||
<Name htmlFor={name}>{name}</Name>
|
||||
<InputContainer>
|
||||
<Input
|
||||
error={hasError}
|
||||
onFocus={handleFocus}
|
||||
name={name}
|
||||
onChange={e => handleChange(e, canCheck, dispatch)}
|
||||
type="text"
|
||||
onBlur={onBlur}
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
value={value || ''}
|
||||
/>
|
||||
<RightContent>
|
||||
<RightLabel availability={availability} label={label} />
|
||||
<RegenerateButton
|
||||
onMouseEnter={handleGenerateMouseEnter}
|
||||
onMouseLeave={handleGenerateMouseLeave}
|
||||
onClick={generateUid.current}
|
||||
>
|
||||
{isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<Sync fill={label ? '#007EFF' : '#B5B7BB'} width="15px" height="15px" />
|
||||
)}
|
||||
</RegenerateButton>
|
||||
</RightContent>
|
||||
{availability && availability.suggestion && isSuggestionOpen && (
|
||||
<FormattedMessage id={`${pluginId}.components.uid.suggested`}>
|
||||
{msg => (
|
||||
<Options
|
||||
title={msg}
|
||||
options={[
|
||||
{
|
||||
id: 'suggestion',
|
||||
label: availability.suggestion,
|
||||
onClick: handleSuggestionClick,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
)}
|
||||
</InputContainer>
|
||||
{hasError && <ErrorMessage>{error}</ErrorMessage>}
|
||||
</Wrapper>
|
||||
);
|
||||
}}
|
||||
</Error>
|
||||
);
|
||||
};
|
||||
|
||||
InputUID.propTypes = {
|
||||
attribute: PropTypes.object.isRequired,
|
||||
contentTypeUID: PropTypes.string.isRequired,
|
||||
error: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
required: PropTypes.bool,
|
||||
validations: PropTypes.object,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
InputUID.defaultProps = {
|
||||
error: null,
|
||||
required: false,
|
||||
validations: {},
|
||||
value: '',
|
||||
};
|
||||
|
||||
export default InputUID;
|
@ -9,6 +9,7 @@ import InputJSONWithErrors from '../InputJSONWithErrors';
|
||||
import InputFileWithErrors from '../InputFileWithErrors';
|
||||
import SelectWrapper from '../SelectWrapper';
|
||||
import WysiwygWithErrors from '../WysiwygWithErrors';
|
||||
import InputUID from '../InputUID';
|
||||
|
||||
const getInputType = (type = '') => {
|
||||
switch (toLower(type)) {
|
||||
@ -42,32 +43,19 @@ const getInputType = (type = '') => {
|
||||
case 'WYSIWYG':
|
||||
case 'richtext':
|
||||
return 'wysiwyg';
|
||||
case 'uid':
|
||||
return 'uid';
|
||||
default:
|
||||
return 'text';
|
||||
}
|
||||
};
|
||||
|
||||
function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
const {
|
||||
didCheckErrors,
|
||||
formErrors,
|
||||
modifiedData,
|
||||
onChange,
|
||||
} = useDataManager();
|
||||
|
||||
const attribute = useMemo(
|
||||
() => get(layout, ['schema', 'attributes', name], {}),
|
||||
[layout, name]
|
||||
);
|
||||
const metadatas = useMemo(
|
||||
() => get(layout, ['metadatas', name, 'edit'], {}),
|
||||
[layout, name]
|
||||
);
|
||||
const disabled = useMemo(() => !get(metadatas, 'editable', true), [
|
||||
metadatas,
|
||||
]);
|
||||
const { didCheckErrors, formErrors, modifiedData, onChange } = useDataManager();
|
||||
const attribute = useMemo(() => get(layout, ['schema', 'attributes', name], {}), [layout, name]);
|
||||
const metadatas = useMemo(() => get(layout, ['metadatas', name, 'edit'], {}), [layout, name]);
|
||||
const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
|
||||
const type = useMemo(() => get(attribute, 'type', null), [attribute]);
|
||||
|
||||
const validations = omit(attribute, [
|
||||
'type',
|
||||
'model',
|
||||
@ -83,13 +71,13 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
if (visible === false) {
|
||||
return null;
|
||||
}
|
||||
const temporaryErrorIdUntilBuffetjsSupportsFormattedMessage =
|
||||
'app.utils.defaultMessage';
|
||||
const temporaryErrorIdUntilBuffetjsSupportsFormattedMessage = 'app.utils.defaultMessage';
|
||||
const errorId = get(
|
||||
formErrors,
|
||||
[keys, 'id'],
|
||||
temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
||||
);
|
||||
const isRequired = get(validations, ['required'], false);
|
||||
|
||||
if (type === 'relation') {
|
||||
return (
|
||||
@ -131,12 +119,8 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
);
|
||||
});
|
||||
|
||||
const isRequired = get(validations, ['required'], false);
|
||||
const enumOptions = [
|
||||
<FormattedMessage
|
||||
id="components.InputSelect.option.placeholder"
|
||||
key="__enum_option_null"
|
||||
>
|
||||
<FormattedMessage id="components.InputSelect.option.placeholder" key="__enum_option_null">
|
||||
{msg => (
|
||||
<option disabled={isRequired} hidden={isRequired} value="">
|
||||
{msg}
|
||||
@ -156,19 +140,21 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
didCheckErrors={didCheckErrors}
|
||||
disabled={disabled}
|
||||
error={
|
||||
isEmpty(error) ||
|
||||
errorId === temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
||||
isEmpty(error) || errorId === temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
||||
? null
|
||||
: error
|
||||
}
|
||||
inputDescription={description}
|
||||
description={description}
|
||||
contentTypeUID={layout.uid}
|
||||
customInputs={{
|
||||
media: InputFileWithErrors,
|
||||
json: InputJSONWithErrors,
|
||||
wysiwyg: WysiwygWithErrors,
|
||||
uid: InputUID,
|
||||
}}
|
||||
multiple={get(attribute, 'multiple', false)}
|
||||
attribute={attribute}
|
||||
name={keys}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
|
@ -1,10 +1,4 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { cloneDeep, get, set } from 'lodash';
|
||||
@ -74,9 +68,9 @@ const EditSettingsView = ({
|
||||
}, [modifiedData]);
|
||||
|
||||
const getForm = () =>
|
||||
Object.keys(
|
||||
get(modifiedData, ['metadatas', metaToEdit, 'edit'], {})
|
||||
).filter(meta => meta !== 'visible');
|
||||
Object.keys(get(modifiedData, ['metadatas', metaToEdit, 'edit'], {})).filter(
|
||||
meta => meta !== 'visible'
|
||||
);
|
||||
|
||||
const getRelationsLayout = useCallback(() => {
|
||||
return get(modifiedData, ['layouts', 'editRelations'], []);
|
||||
@ -94,10 +88,7 @@ const EditSettingsView = ({
|
||||
const getEditRemainingFields = () => {
|
||||
const attributes = getAttributes;
|
||||
const metadatas = get(modifiedData, ['metadatas'], {});
|
||||
const displayedFields = getEditLayout().reduce(
|
||||
(acc, curr) => [...acc, ...curr.rowContent],
|
||||
[]
|
||||
);
|
||||
const displayedFields = getEditLayout().reduce((acc, curr) => [...acc, ...curr.rowContent], []);
|
||||
|
||||
return Object.keys(attributes)
|
||||
.filter(attr => get(attributes, [attr, 'type'], '') !== 'relation')
|
||||
@ -114,11 +105,7 @@ const EditSettingsView = ({
|
||||
}
|
||||
|
||||
const targetKey = formType === 'component' ? 'component' : 'targetModel';
|
||||
const key = get(
|
||||
modifiedData,
|
||||
['schema', 'attributes', metaToEdit, targetKey],
|
||||
''
|
||||
);
|
||||
const key = get(modifiedData, ['schema', 'attributes', metaToEdit, targetKey], '');
|
||||
|
||||
return get(componentsAndModelsMainPossibleMainFields, [key], []);
|
||||
},
|
||||
@ -129,13 +116,10 @@ const EditSettingsView = ({
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
try {
|
||||
const { data } = await request(
|
||||
getRequestUrl(`${type}/${slug || componentSlug}`),
|
||||
{
|
||||
method: 'GET',
|
||||
signal,
|
||||
}
|
||||
);
|
||||
const { data } = await request(getRequestUrl(`${type}/${slug || componentSlug}`), {
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: 'GET_DATA_SUCCEEDED',
|
||||
@ -176,6 +160,7 @@ const EditSettingsView = ({
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
const body = cloneDeep(modifiedData);
|
||||
|
||||
// We need to send the unformated edit layout
|
||||
set(body, 'layouts.edit', unformatLayout(body.layouts.edit));
|
||||
|
||||
@ -183,6 +168,7 @@ const EditSettingsView = ({
|
||||
delete body.uid;
|
||||
delete body.isComponent;
|
||||
delete body.category;
|
||||
delete body.apiID;
|
||||
|
||||
await request(getRequestUrl(`${type}/${slug || componentSlug}`), {
|
||||
method: 'PUT',
|
||||
@ -252,24 +238,15 @@ const EditSettingsView = ({
|
||||
getForm().map((meta, index) => {
|
||||
const formType = get(getAttributes, [metaToEdit, 'type']);
|
||||
|
||||
if (
|
||||
formType === 'dynamiczone' &&
|
||||
!['label', 'description'].includes(meta)
|
||||
) {
|
||||
if (formType === 'dynamiczone' && !['label', 'description'].includes(meta)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
(formType === 'component' || formType === 'media') &&
|
||||
meta !== 'label'
|
||||
) {
|
||||
if ((formType === 'component' || formType === 'media') && meta !== 'label') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
(formType === 'json' || formType === 'boolean') &&
|
||||
meta === 'placeholder'
|
||||
) {
|
||||
if ((formType === 'json' || formType === 'boolean') && meta === 'placeholder') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -284,11 +261,7 @@ const EditSettingsView = ({
|
||||
>
|
||||
{description => (
|
||||
<FormattedMessage
|
||||
id={get(
|
||||
getInputProps(meta),
|
||||
'label.id',
|
||||
'app.utils.defaultMessage'
|
||||
)}
|
||||
id={get(getInputProps(meta), 'label.id', 'app.utils.defaultMessage')}
|
||||
>
|
||||
{label => (
|
||||
<Input
|
||||
|
@ -32,7 +32,7 @@ const Header = () => {
|
||||
|
||||
const currentContentTypeMainField = get(layout, ['settings', 'mainField'], 'id');
|
||||
const currentContentTypeName = get(layout, ['schema', 'info', 'name']);
|
||||
const isCreatingEntry = id === 'create';
|
||||
const isCreatingEntry = id === 'create' || (isSingleType && !initialData.created_at);
|
||||
|
||||
/* eslint-disable indent */
|
||||
const entryHeaderTitle = isCreatingEntry
|
||||
|
@ -13,11 +13,7 @@ import * as yup from 'yup';
|
||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||
|
||||
yup.addMethod(yup.mixed, 'defined', function() {
|
||||
return this.test(
|
||||
'defined',
|
||||
errorsTrads.required,
|
||||
value => value !== undefined
|
||||
);
|
||||
return this.test('defined', errorsTrads.required, value => value !== undefined);
|
||||
});
|
||||
|
||||
yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
||||
@ -89,12 +85,9 @@ const createYupSchema = (model, { components }) => {
|
||||
}
|
||||
|
||||
if (attribute.type === 'component') {
|
||||
const componentFieldSchema = createYupSchema(
|
||||
components[attribute.component],
|
||||
{
|
||||
components,
|
||||
}
|
||||
);
|
||||
const componentFieldSchema = createYupSchema(components[attribute.component], {
|
||||
components,
|
||||
});
|
||||
|
||||
if (attribute.repeatable === true) {
|
||||
const { min, max, required } = attribute;
|
||||
@ -129,9 +122,7 @@ const createYupSchema = (model, { components }) => {
|
||||
: componentFieldSchema.nullable();
|
||||
}
|
||||
|
||||
return attribute.required === true
|
||||
? yup.object().defined()
|
||||
: yup.object().nullable();
|
||||
return attribute.required === true ? yup.object().defined() : yup.object().nullable();
|
||||
});
|
||||
|
||||
acc[current] = componentSchema;
|
||||
@ -178,11 +169,7 @@ const createYupSchema = (model, { components }) => {
|
||||
const createYupSchemaAttribute = (type, validations) => {
|
||||
let schema = yup.mixed();
|
||||
|
||||
if (
|
||||
['string', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(
|
||||
type
|
||||
)
|
||||
) {
|
||||
if (['string', 'uid', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(type)) {
|
||||
schema = yup.string();
|
||||
}
|
||||
|
||||
@ -194,12 +181,7 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
isNumber(value) ||
|
||||
isNull(value) ||
|
||||
isObject(value) ||
|
||||
isArray(value)
|
||||
) {
|
||||
if (isNumber(value) || isNull(value) || isObject(value) || isArray(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -238,9 +220,8 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
|
||||
if (
|
||||
!!validationValue ||
|
||||
((!isBoolean(validationValue) &&
|
||||
Number.isInteger(Math.floor(validationValue))) ||
|
||||
validationValue === 0)
|
||||
(!isBoolean(validationValue) && Number.isInteger(Math.floor(validationValue))) ||
|
||||
validationValue === 0
|
||||
) {
|
||||
switch (validation) {
|
||||
case 'required':
|
||||
@ -282,16 +263,12 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
}
|
||||
break;
|
||||
case 'positive':
|
||||
if (
|
||||
['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)
|
||||
) {
|
||||
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||
schema = schema.positive();
|
||||
}
|
||||
break;
|
||||
case 'negative':
|
||||
if (
|
||||
['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)
|
||||
) {
|
||||
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||
schema = schema.negative();
|
||||
}
|
||||
break;
|
||||
|
@ -50,6 +50,12 @@
|
||||
"components.TableEmpty.withSearch": "There is no {contentType} corresponding to the search ({search})...",
|
||||
"components.TableEmpty.withoutFilter": "There is no {contentType}...",
|
||||
|
||||
"components.uid.available": "available",
|
||||
"components.uid.apply": "apply",
|
||||
"components.uid.regenerate": "regenerate",
|
||||
"components.uid.suggested": "suggested",
|
||||
"components.uid.unavailable": "unavailable",
|
||||
|
||||
"containers.Edit.addAnItem": "Add an item...",
|
||||
"containers.Edit.pluginHeader.title.new": "Create an entry",
|
||||
"containers.Edit.clickToJump": "Click to jump to the entry",
|
||||
|
@ -47,6 +47,12 @@
|
||||
"components.TableEmpty.withSearch": "Aucun {contentType} n'a été trouvé avec cette recherche ({search})...",
|
||||
"components.TableEmpty.withoutFilter": "Aucun {contentType} n'a été trouvé...",
|
||||
|
||||
"components.uid.available": "disponible",
|
||||
"components.uid.apply": "appliquer",
|
||||
"components.uid.regenerate": "regénérer",
|
||||
"components.uid.unavailable": "indisponible",
|
||||
"components.uid.suggested": "suggéré",
|
||||
|
||||
"containers.Edit.addAnItem": "Ajouter un élément...",
|
||||
"containers.Edit.pluginHeader.title.new": "Créer un document",
|
||||
"containers.Edit.clickToJump": "Cliquer pour voir l'entrée",
|
||||
|
@ -704,7 +704,7 @@ const forms = {
|
||||
|
||||
if (type === 'uid') {
|
||||
const options = Object.keys(attributes)
|
||||
.filter(key => attributes[key].type === 'string')
|
||||
.filter(key => ['string', 'text'].includes(attributes[key].type))
|
||||
.map(key => ({ id: key, value: key }));
|
||||
|
||||
items[0].push({
|
||||
|
@ -99,12 +99,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
async getEnvironments(ctx) {
|
||||
const environments = Object.keys(strapi.config.environments).map(
|
||||
environment => ({
|
||||
name: environment,
|
||||
active: strapi.config.environment === environment,
|
||||
})
|
||||
);
|
||||
const environments = Object.keys(strapi.config.environments).map(environment => ({
|
||||
name: environment,
|
||||
active: strapi.config.environment === environment,
|
||||
}));
|
||||
|
||||
ctx.send({ environments });
|
||||
},
|
||||
@ -143,18 +141,14 @@ module.exports = {
|
||||
},
|
||||
|
||||
async find(ctx) {
|
||||
const data = await strapi.plugins['upload'].services.upload.fetchAll(
|
||||
ctx.query
|
||||
);
|
||||
const data = await strapi.plugins['upload'].services.upload.fetchAll(ctx.query);
|
||||
|
||||
// Send 200 `ok`
|
||||
ctx.send(data);
|
||||
},
|
||||
|
||||
async findOne(ctx) {
|
||||
const data = await strapi.plugins['upload'].services.upload.fetch(
|
||||
ctx.params
|
||||
);
|
||||
const data = await strapi.plugins['upload'].services.upload.fetch(ctx.params);
|
||||
|
||||
if (!data) {
|
||||
return ctx.notFound('file.notFound');
|
||||
@ -164,9 +158,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
async count(ctx) {
|
||||
const data = await strapi.plugins['upload'].services.upload.count(
|
||||
ctx.query
|
||||
);
|
||||
const data = await strapi.plugins['upload'].services.upload.count(ctx.query);
|
||||
|
||||
ctx.send({ count: data });
|
||||
},
|
||||
@ -208,9 +200,9 @@ const searchQueries = {
|
||||
return ({ id }) => {
|
||||
return model
|
||||
.query(qb => {
|
||||
qb.whereRaw('LOWER(hash) LIKE ?', [
|
||||
qb.whereRaw('LOWER(hash) LIKE ?', [`%${id}%`]).orWhereRaw('LOWER(name) LIKE ?', [
|
||||
`%${id}%`,
|
||||
]).orWhereRaw('LOWER(name) LIKE ?', [`%${id}%`]);
|
||||
]);
|
||||
})
|
||||
.fetchAll()
|
||||
.then(results => results.toJSON());
|
||||
|
@ -33,9 +33,7 @@ module.exports = {
|
||||
|
||||
const createBuffer = async stream => {
|
||||
const parts = await toArray(fs.createReadStream(stream.path));
|
||||
const buffers = parts.map(part =>
|
||||
_.isBuffer(part) ? part : Buffer.from(part)
|
||||
);
|
||||
const buffers = parts.map(part => (_.isBuffer(part) ? part : Buffer.from(part)));
|
||||
|
||||
const buffer = Buffer.concat(buffers);
|
||||
|
||||
@ -44,10 +42,7 @@ module.exports = {
|
||||
name: stream.name,
|
||||
sha256: niceHash(buffer),
|
||||
hash: uuid().replace(/-/g, ''),
|
||||
ext:
|
||||
stream.name.split('.').length > 1
|
||||
? `.${_.last(stream.name.split('.'))}`
|
||||
: '',
|
||||
ext: stream.name.split('.').length > 1 ? `.${_.last(stream.name.split('.'))}` : '',
|
||||
buffer,
|
||||
mime: stream.type,
|
||||
size: (stream.size / 1000).toFixed(2),
|
||||
|
12
yarn.lock
12
yarn.lock
@ -3322,9 +3322,9 @@ acorn-globals@^4.1.0:
|
||||
acorn-walk "^6.0.1"
|
||||
|
||||
acorn-jsx@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384"
|
||||
integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
|
||||
integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
|
||||
|
||||
acorn-walk@^6.0.1:
|
||||
version "6.2.0"
|
||||
@ -15555,9 +15555,9 @@ rsvp@^4.8.4:
|
||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
|
||||
integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==
|
||||
dependencies:
|
||||
is-promise "^2.1.0"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user