@mirrormedia/lilith-draft-editor
Version:
## Installation
463 lines (417 loc) • 15.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ImageSelector = ImageSelector;
var _react = _interopRequireWildcard(require("react"));
var _debounce = _interopRequireDefault(require("lodash/debounce"));
var _styledComponents = _interopRequireWildcard(require("styled-components"));
var _fields = require("@keystone-ui/fields");
var _modals = require("@keystone-ui/modals");
var _apollo = require("@keystone-6/core/admin-ui/apollo");
var _alignSelector = require("./align-selector");
var _searchBox = require("./search-box");
var _pagination = require("./pagination");
var _button = require("@keystone-ui/button");
var _imageUploader = require("./image-uploader");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const imagesQuery = (0, _apollo.gql)`
query Images($searchText: String!, $take: Int, $skip: Int) {
imagesCount(where: { name: { contains: $searchText } })
images(
where: { name: { contains: $searchText } }
orderBy: { id: desc }
take: $take
skip: $skip
) {
id
name
file {
url
width
height
}
resized {
original
w480
w800
w1200
w1600
w2400
}
resizedWebp {
original
w480
w800
w1200
w1600
w2400
}
}
}
`;
const _ = {
debounce: _debounce.default
};
const GlobalStyle = (0, _styledComponents.createGlobalStyle)`
form {
@media (max-width: 575px) {
width: 100vw !important;
}
}
`;
const ImageSearchBox = (0, _styledComponents.default)(_searchBox.SearchBox)`
margin-top: 10px;
`;
const CustomButton = (0, _styledComponents.default)(_button.Button)`
margin-top: 10px;
`;
const ImageSelectionWrapper = _styledComponents.default.div`
overflow: auto;
margin-top: 10px;
`;
const ImageBlockMetaWrapper = _styledComponents.default.div``;
const ImageGridsWrapper = _styledComponents.default.div`
display: flex;
flex-wrap: wrap;
overflow: auto;
margin-top: 5px;
`;
const ImageGridWrapper = _styledComponents.default.div`
width: 33.3333%;
cursor: pointer;
padding: 0 10px 10px;
`;
const ImageMetaGridsWrapper = _styledComponents.default.div`
display: flex;
flex-wrap: wrap;
overflow: auto;
`;
const ImageMetaGridWrapper = _styledComponents.default.div`
width: 100%;
cursor: pointer;
padding: 0 10px 10px;
`;
const Image = _styledComponents.default.img`
display: block;
width: 100%;
aspect-ratio: 2;
object-fit: cover;
`;
const Label = _styledComponents.default.label`
display: block;
margin: 10px 0;
font-weight: 600;
`;
const StyledTextInput = (0, _styledComponents.default)(_fields.TextInput)`
width: 100%;
`;
const SeparationLine = _styledComponents.default.div`
border: #e1e5e9 1px solid;
margin-top: 10px;
margin-bottom: 10px;
`;
const ImageSelected = _styledComponents.default.div`
height: 1.4rem;
`;
const ErrorWrapper = _styledComponents.default.div`
& * {
margin: 0;
}
`;
const ImageName = _styledComponents.default.p`
text-align: center;
`;
function ImageGrids(props) {
const {
images,
selected,
onSelect
} = props;
return /*#__PURE__*/_react.default.createElement(ImageGridsWrapper, null, images.map(image => {
return /*#__PURE__*/_react.default.createElement(ImageGrid, {
key: image.id,
isSelected: !!(selected !== null && selected !== void 0 && selected.find(selectedImage => selectedImage.id === image.id)),
onSelect: () => onSelect(image),
image: image
});
}));
}
function ImageGrid(props) {
var _image$resized, _image$file;
const {
image,
onSelect,
isSelected
} = props;
const initialSrc = (image === null || image === void 0 ? void 0 : (_image$resized = image.resized) === null || _image$resized === void 0 ? void 0 : _image$resized.w800) || (image === null || image === void 0 ? void 0 : (_image$file = image.file) === null || _image$file === void 0 ? void 0 : _image$file.url);
return /*#__PURE__*/_react.default.createElement(ImageGridWrapper, {
key: image === null || image === void 0 ? void 0 : image.id,
onClick: () => onSelect(image)
}, /*#__PURE__*/_react.default.createElement(ImageSelected, null, isSelected ? /*#__PURE__*/_react.default.createElement("i", {
className: "fas fa-check-circle",
style: {
color: '#007bff'
}
}) : null), /*#__PURE__*/_react.default.createElement(Image, {
src: initialSrc,
onError: e => {
var _image$file2;
if (image !== null && image !== void 0 && (_image$file2 = image.file) !== null && _image$file2 !== void 0 && _image$file2.url && e.currentTarget.src !== image.file.url) {
e.currentTarget.src = image.file.url;
}
}
}));
}
function ImageMetaGrids(props) {
const {
imageMetas,
onChange,
enableCaption,
enableUrl
} = props;
return /*#__PURE__*/_react.default.createElement(ImageMetaGridsWrapper, null, imageMetas.map(imageMeta => {
var _imageMeta$image;
return /*#__PURE__*/_react.default.createElement(ImageMetaGrid, {
key: imageMeta === null || imageMeta === void 0 ? void 0 : (_imageMeta$image = imageMeta.image) === null || _imageMeta$image === void 0 ? void 0 : _imageMeta$image.id,
imageMeta: imageMeta,
enableCaption: enableCaption,
enableUrl: enableUrl,
onChange: onChange
});
}));
}
function ImageMetaGrid(props) {
var _image$resized2, _image$file3;
const {
imageMeta,
enableCaption,
enableUrl,
onChange
} = props;
const {
image,
desc,
url
} = imageMeta;
const src = (image === null || image === void 0 ? void 0 : (_image$resized2 = image.resized) === null || _image$resized2 === void 0 ? void 0 : _image$resized2.w800) || (image === null || image === void 0 ? void 0 : (_image$file3 = image.file) === null || _image$file3 === void 0 ? void 0 : _image$file3.url);
return /*#__PURE__*/_react.default.createElement(ImageMetaGridWrapper, null, /*#__PURE__*/_react.default.createElement(Image, {
src: src,
onError: e => {
var _image$file4;
if (image !== null && image !== void 0 && (_image$file4 = image.file) !== null && _image$file4 !== void 0 && _image$file4.url && e.currentTarget.src !== image.file.url) {
e.currentTarget.src = image.file.url;
}
}
}), /*#__PURE__*/_react.default.createElement(ImageName, {
style: {
textAlign: 'left'
}
}, image === null || image === void 0 ? void 0 : image.name), enableCaption && /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement(Label, {
htmlFor: "caption"
}, "Image Caption:"), /*#__PURE__*/_react.default.createElement(_fields.TextInput, {
id: "caption",
type: "text",
placeholder: image === null || image === void 0 ? void 0 : image.name,
defaultValue: desc,
onChange: _.debounce(e => {
onChange({
image,
desc: e.target.value,
url
});
}, 300)
})), enableUrl && /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement(Label, {
htmlFor: "url"
}, "Url:"), /*#__PURE__*/_react.default.createElement(_fields.TextInput, {
id: "url",
type: "text",
placeholder: "(Optional)",
defaultValue: url,
onChange: _.debounce(e => {
onChange({
image,
desc,
url: e.target.value
});
}, 300)
})));
}
function DelayInput(props) {
const {
delay,
onChange
} = props;
return /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement(Label, null, "Slideshow delay:"), /*#__PURE__*/_react.default.createElement(_fields.TextInput, {
type: "number",
placeholder: "\u8ACB\u8F38\u5165\u81EA\u52D5\u5207\u63DB\u79D2\u6578",
step: "0.5",
min: "1",
value: delay,
onChange: e => {
onChange(e.target.value);
}
}));
}
function ImageSelector(props) {
const {
enableMultiSelect = false,
enableCaption = false,
enableUrl = false,
enableAlignment = false,
enableDelay = false,
onChange,
initialSelected = [],
initialAlign,
initialDelay
} = props;
const [queryImages, {
loading,
error,
data: {
images = [],
imagesCount = 0
} = {}
}] = (0, _apollo.useLazyQuery)(imagesQuery, {
fetchPolicy: 'no-cache'
});
const [currentPage, setCurrentPage] = (0, _react.useState)(0);
const [searchText, setSearchText] = (0, _react.useState)('');
const [selected, setSelected] = (0, _react.useState)(initialSelected);
const [delay, setDelay] = (0, _react.useState)((initialDelay === null || initialDelay === void 0 ? void 0 : initialDelay.toString()) ?? '5');
const [align, setAlign] = (0, _react.useState)(initialAlign);
const [showImageUploader, setShowImageUploader] = (0, _react.useState)(false);
const contentWrapperRef = (0, _react.useRef)(null);
const pageSize = 18; // Data Fetching Logic
(0, _react.useEffect)(() => {
if (currentPage !== 0) {
queryImages({
variables: {
searchText,
skip: (currentPage - 1) * pageSize,
take: pageSize
}
});
}
}, [currentPage, searchText, queryImages]); // Selection Handler
const onImagesGridSelect = imageEntity => {
setSelected(prev => {
const isSelected = prev.find(ele => {
var _ele$image;
return ((_ele$image = ele.image) === null || _ele$image === void 0 ? void 0 : _ele$image.id) === imageEntity.id;
});
if (isSelected) {
return prev.filter(ele => {
var _ele$image2;
return ((_ele$image2 = ele.image) === null || _ele$image2 === void 0 ? void 0 : _ele$image2.id) !== imageEntity.id;
});
}
const newItem = {
image: imageEntity,
desc: '',
url: ''
};
if (enableMultiSelect) {
return [...prev, newItem];
}
return [newItem];
});
}; // 3. Metadata Handler
const onImageMetaChange = updatedItem => {
setSelected(prev => prev.map(item => item.image.id === updatedItem.image.id ? updatedItem : item));
};
const onSave = () => {
const adjustedDelay = Math.max(1, parseFloat(delay) || 1);
onChange(selected, align, adjustedDelay);
};
const onCancel = () => {
onChange([]);
};
const onSearchBoxChange = searchInput => {
setSearchText(searchInput);
setCurrentPage(1);
};
const onImageUploaderChange = uploadedImages => {
const newItems = uploadedImages.map(img => ({
image: img,
desc: '',
url: ''
}));
setSelected(prev => [...prev, ...newItems]);
setShowImageUploader(false);
};
let searchResult = /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement(ImageGrids, {
images: images,
selected: selected.map(s => s.image),
onSelect: onImagesGridSelect
}), /*#__PURE__*/_react.default.createElement(_pagination.Pagination, {
currentPage: currentPage,
total: imagesCount,
pageSize: pageSize,
onChange: page => setCurrentPage(page)
}));
if (loading) searchResult = /*#__PURE__*/_react.default.createElement("p", null, "searching...");
if (error) {
searchResult = /*#__PURE__*/_react.default.createElement(ErrorWrapper, null, /*#__PURE__*/_react.default.createElement("h3", null, "Errors in query"), /*#__PURE__*/_react.default.createElement("div", null, error.message));
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(GlobalStyle, null), /*#__PURE__*/_react.default.createElement(_modals.DrawerController, {
isOpen: true
}, /*#__PURE__*/_react.default.createElement(_modals.Drawer, {
title: "Select images",
actions: {
cancel: {
label: 'Cancel',
action: onCancel
},
confirm: {
label: 'Confirm',
action: onSave
}
},
width: "narrow"
}, /*#__PURE__*/_react.default.createElement("div", {
ref: contentWrapperRef
}, /*#__PURE__*/_react.default.createElement(CustomButton, {
onClick: () => setShowImageUploader(true)
}, "\u4E0A\u50B3\u5716\u7247"), /*#__PURE__*/_react.default.createElement(ImageSearchBox, {
onChange: onSearchBoxChange
}), /*#__PURE__*/_react.default.createElement(ImageSelectionWrapper, null, /*#__PURE__*/_react.default.createElement("div", null, searchResult), !!selected.length && /*#__PURE__*/_react.default.createElement(SeparationLine, null), /*#__PURE__*/_react.default.createElement(ImageMetaGridsWrapper, null, selected.map(item => /*#__PURE__*/_react.default.createElement(ImageMetaGrid, {
key: item.image.id,
imageMeta: item,
enableCaption: enableCaption,
enableUrl: enableUrl,
onChange: onImageMetaChange
})))), /*#__PURE__*/_react.default.createElement("div", {
style: {
marginTop: '20px'
}
}, (enableDelay || enableAlignment) && /*#__PURE__*/_react.default.createElement(SeparationLine, null), enableDelay && /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement(Label, null, "Slideshow delay:"), /*#__PURE__*/_react.default.createElement(_fields.TextInput, {
type: "number",
step: "0.5",
min: "1",
value: delay,
onChange: e => setDelay(e.target.value)
})), enableAlignment && /*#__PURE__*/_react.default.createElement(_alignSelector.AlignSelector, {
align: align,
options: [{
value: undefined,
label: 'default'
}, {
value: 'left',
label: 'left'
}, {
value: 'right',
label: 'right'
}],
onChange: setAlign,
onOpen: () => {
var _contentWrapperRef$cu;
const wrapper = (_contentWrapperRef$cu = contentWrapperRef.current) === null || _contentWrapperRef$cu === void 0 ? void 0 : _contentWrapperRef$cu.parentElement;
if (wrapper) wrapper.scrollTop = wrapper.scrollHeight;
}
}))))), showImageUploader && /*#__PURE__*/_react.default.createElement(_imageUploader.ImageUploader, {
onChange: onImageUploaderChange
}));
}