mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-12-11 14:32:30 -05:00
Frontent dev env (#247)
* Added frontend development files/environment * More items-categories related removals * Improvements in pages templates (inc. static pages) * Improvements in video player * Added empty home page message + cta * Updates in media, playlist and management pages * Improvements in material icons font loading * Replaced media & playlists links in frontend dev-env * frontend package version update * chnaged frontend dev url port * static files update * Changed default position of theme switcher * enabled frontend docker container
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { PageStore } from '../../utils/stores/';
|
||||
import { useLayout, useItemListInlineSlider } from '../../utils/hooks/';
|
||||
import { ItemsStaticListHandler } from './includes/itemLists/ItemsStaticListHandler';
|
||||
import { ItemList } from './ItemList';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
|
||||
export function InlineSliderItemList(props) {
|
||||
const { visibleSidebar } = useLayout();
|
||||
|
||||
const [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
classname,
|
||||
setListHandler,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
winResizeListener,
|
||||
sidebarVisibilityChangeListener,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListInlineSlider(props);
|
||||
|
||||
useEffect(() => {
|
||||
sidebarVisibilityChangeListener();
|
||||
}, [visibleSidebar]);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(new ItemsStaticListHandler(props.items, props.pageItems, props.maxItems, onItemsCount, onItemsLoad));
|
||||
|
||||
PageStore.on('window_resize', winResizeListener);
|
||||
|
||||
return () => {
|
||||
PageStore.removeListener('window_resize', winResizeListener);
|
||||
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
InlineSliderItemList.propTypes = {
|
||||
...ItemList.propTypes,
|
||||
};
|
||||
|
||||
InlineSliderItemList.defaultProps = {
|
||||
...ItemList.defaultProps,
|
||||
pageItems: 12,
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { PageStore } from '../../utils/stores/';
|
||||
import { useLayout, useItemListInlineSlider } from '../../utils/hooks/';
|
||||
import { ItemListAsync } from './ItemListAsync';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
import { ItemsListHandler } from './includes/itemLists/ItemsListHandler';
|
||||
|
||||
export function InlineSliderItemListAsync(props) {
|
||||
const { visibleSidebar } = useLayout();
|
||||
|
||||
const [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
classname,
|
||||
setListHandler,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
winResizeListener,
|
||||
sidebarVisibilityChangeListener,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListInlineSlider(props);
|
||||
|
||||
useEffect(() => {
|
||||
sidebarVisibilityChangeListener();
|
||||
}, [visibleSidebar]);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(
|
||||
new ItemsListHandler(
|
||||
props.pageItems,
|
||||
props.maxItems,
|
||||
props.firstItemRequestUrl,
|
||||
props.requestUrl,
|
||||
onItemsCount,
|
||||
onItemsLoad
|
||||
)
|
||||
);
|
||||
|
||||
PageStore.on('window_resize', winResizeListener);
|
||||
|
||||
return () => {
|
||||
PageStore.removeListener('window_resize', winResizeListener);
|
||||
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
InlineSliderItemListAsync.propTypes = {
|
||||
...ItemListAsync.propTypes,
|
||||
};
|
||||
|
||||
InlineSliderItemListAsync.defaultProps = {
|
||||
...ItemListAsync.defaultProps,
|
||||
pageItems: 12,
|
||||
};
|
||||
105
frontend/src/static/js/components/item-list/ItemList.jsx
Normal file
105
frontend/src/static/js/components/item-list/ItemList.jsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useItemListSync } from '../../utils/hooks/';
|
||||
import { PositiveIntegerOrZero } from '../../utils/helpers';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
import { ItemsStaticListHandler } from './includes/itemLists/ItemsStaticListHandler';
|
||||
|
||||
export function ItemList(props) {
|
||||
const [
|
||||
countedItems,
|
||||
items,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListSync(props);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(new ItemsStaticListHandler(props.items, props.pageItems, props.maxItems, onItemsCount, onItemsLoad));
|
||||
|
||||
return () => {
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ItemList.propTypes = {
|
||||
items: PropTypes.array.isRequired,
|
||||
className: PropTypes.string,
|
||||
hideDate: PropTypes.bool,
|
||||
hideViews: PropTypes.bool,
|
||||
hideAuthor: PropTypes.bool,
|
||||
hidePlaylistOptions: PropTypes.bool,
|
||||
hidePlaylistOrderNumber: PropTypes.bool,
|
||||
hideAllMeta: PropTypes.bool,
|
||||
preferSummary: PropTypes.bool,
|
||||
inPlaylistView: PropTypes.bool,
|
||||
inPlaylistPage: PropTypes.bool,
|
||||
playlistActiveItem: PositiveIntegerOrZero,
|
||||
playlistId: PropTypes.string,
|
||||
/* ################################################## */
|
||||
maxItems: PropTypes.number.isRequired,
|
||||
pageItems: PropTypes.number.isRequired,
|
||||
horizontalItemsOrientation: PropTypes.bool.isRequired,
|
||||
singleLinkContent: PropTypes.bool.isRequired,
|
||||
inTagsList: PropTypes.bool,
|
||||
inCategoriesList: PropTypes.bool,
|
||||
itemsCountCallback: PropTypes.func,
|
||||
itemsLoadCallback: PropTypes.func,
|
||||
firstItemViewer: PropTypes.bool,
|
||||
firstItemDescr: PropTypes.bool,
|
||||
canEdit: PropTypes.bool,
|
||||
};
|
||||
|
||||
ItemList.defaultProps = {
|
||||
hideDate: false,
|
||||
hideViews: false,
|
||||
hideAuthor: false,
|
||||
hidePlaylistOptions: true,
|
||||
hidePlaylistOrderNumber: true,
|
||||
hideAllMeta: false,
|
||||
preferSummary: false,
|
||||
inPlaylistView: false,
|
||||
inPlaylistPage: false,
|
||||
playlistActiveItem: 1,
|
||||
playlistId: void 0,
|
||||
/* ################################################## */
|
||||
maxItems: 99999,
|
||||
// pageItems: 48,
|
||||
pageItems: 24,
|
||||
horizontalItemsOrientation: false,
|
||||
singleLinkContent: false,
|
||||
inTagsList: false,
|
||||
inCategoriesList: false,
|
||||
firstItemViewer: false,
|
||||
firstItemDescr: false,
|
||||
canEdit: false,
|
||||
};
|
||||
149
frontend/src/static/js/components/item-list/ItemList.scss
Executable file
149
frontend/src/static/js/components/item-list/ItemList.scss
Executable file
@@ -0,0 +1,149 @@
|
||||
@import '../../../css/includes/_variables.scss';
|
||||
@import '../../../css/includes/_variables_dimensions.scss';
|
||||
|
||||
@import '../../../css/config/index.scss';
|
||||
|
||||
.items-list-outer {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
&.list-inline.list-slider {
|
||||
margin: 0 8px;
|
||||
|
||||
.previous-slide,
|
||||
.next-slide {
|
||||
position: absolute;
|
||||
z-index: +1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding-top: 28.125%;
|
||||
|
||||
.circle-icon-button {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.previous-slide {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.next-slide {
|
||||
right: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 420px) {
|
||||
&.list-inline.list-slider {
|
||||
margin: 0;
|
||||
|
||||
.previous-slide {
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.next-slide {
|
||||
right: -20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
&.list-inline.list-slider {
|
||||
.previous-slide,
|
||||
.next-slide {
|
||||
padding-top: calc(0.28125 * calc(var(--item-width, var(--default-item-width))));
|
||||
}
|
||||
|
||||
.next-slide {
|
||||
right: calc(-20px + var(--item-margin-right-width, var(--default-item-margin-right-width)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-list-header,
|
||||
.media-list-header {
|
||||
display: block;
|
||||
padding: 12px 0;
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
display: inline-block;
|
||||
margin: 12px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
margin: 10px 16px;
|
||||
text-decoration: none;
|
||||
color: var(--media-list-header-title-link-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-list-wrap {
|
||||
position: relative;
|
||||
display: block;
|
||||
min-height: 218px;
|
||||
|
||||
.list-inline & {
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
will-change: width, scroll-position, scroll-behavior;
|
||||
|
||||
.item {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.list-slider & {
|
||||
overflow: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
.list-slider .items-list-wrap.resizing {
|
||||
scroll-behavior: unset;
|
||||
}
|
||||
|
||||
.items-list {
|
||||
max-width: 100%;
|
||||
word-break: break-word;
|
||||
|
||||
img,
|
||||
picture {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button.load-more {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.007px;
|
||||
margin: 0 auto 24px 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
|
||||
color: var(--item-list-load-more-text-color);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--item-list-load-more-hover-text-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@import '../list-item/Item.scss';
|
||||
@import '../list-item/ItemVertical.scss';
|
||||
@import '../list-item/ItemHorizontal.scss';
|
||||
@@ -0,0 +1,75 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useItemListSync } from '../../utils/hooks/';
|
||||
import { ItemList } from './ItemList';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
import { ItemsListHandler } from './includes/itemLists/ItemsListHandler';
|
||||
|
||||
export function ItemListAsync(props) {
|
||||
const [
|
||||
countedItems,
|
||||
items,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListSync(props);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(
|
||||
new ItemsListHandler(
|
||||
props.pageItems,
|
||||
props.maxItems,
|
||||
props.firstItemRequestUrl,
|
||||
props.requestUrl,
|
||||
onItemsCount,
|
||||
onItemsLoad
|
||||
)
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ItemListAsync.propTypes = {
|
||||
...ItemList.propTypes,
|
||||
items: PropTypes.array, // Reset 'isRequired' feature.
|
||||
requestUrl: PropTypes.string.isRequired,
|
||||
firstItemRequestUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
ItemListAsync.defaultProps = {
|
||||
...ItemList.defaultProps,
|
||||
requestUrl: null,
|
||||
firstItemRequestUrl: null,
|
||||
pageItems: 24,
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { PageStore } from '../../utils/stores/';
|
||||
import { useItemListLazyLoad } from '../../utils/hooks/';
|
||||
import { ItemList } from './ItemList';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
import { ItemsStaticListHandler } from './includes/itemLists/ItemsStaticListHandler';
|
||||
|
||||
export function LazyLoadItemList(props) {
|
||||
const [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
onWindowScroll,
|
||||
onDocumentVisibilityChange,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListLazyLoad(props);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(new ItemsStaticListHandler(props.items, props.pageItems, props.maxItems, onItemsCount, onItemsLoad));
|
||||
|
||||
PageStore.on('window_scroll', onWindowScroll);
|
||||
PageStore.on('document_visibility_change', onDocumentVisibilityChange);
|
||||
|
||||
onWindowScroll();
|
||||
|
||||
return () => {
|
||||
PageStore.removeListener('window_scroll', onWindowScroll);
|
||||
PageStore.removeListener('document_visibility_change', onDocumentVisibilityChange);
|
||||
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
LazyLoadItemList.propTypes = {
|
||||
...ItemList.propTypes,
|
||||
};
|
||||
|
||||
LazyLoadItemList.defaultProps = {
|
||||
...ItemList.defaultProps,
|
||||
pageItems: 2,
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { PageStore } from '../../utils/stores/';
|
||||
import { useItemListLazyLoad } from '../../utils/hooks/';
|
||||
import { ItemListAsync } from './ItemListAsync';
|
||||
import { PendingItemsList } from './PendingItemsList';
|
||||
import { ListItem, listItemProps } from '../list-item/ListItem';
|
||||
import { ItemsListHandler } from './includes/itemLists/ItemsListHandler';
|
||||
|
||||
export function LazyLoadItemListAsync(props) {
|
||||
const [
|
||||
items,
|
||||
countedItems,
|
||||
listHandler,
|
||||
setListHandler,
|
||||
classname,
|
||||
onItemsCount,
|
||||
onItemsLoad,
|
||||
onWindowScroll,
|
||||
onDocumentVisibilityChange,
|
||||
itemsListWrapperRef,
|
||||
itemsListRef,
|
||||
renderBeforeListWrap,
|
||||
renderAfterListWrap,
|
||||
] = useItemListLazyLoad(props);
|
||||
|
||||
useEffect(() => {
|
||||
setListHandler(
|
||||
new ItemsListHandler(
|
||||
props.pageItems,
|
||||
props.maxItems,
|
||||
props.firstItemRequestUrl,
|
||||
props.requestUrl,
|
||||
onItemsCount,
|
||||
onItemsLoad
|
||||
)
|
||||
);
|
||||
|
||||
PageStore.on('window_scroll', onWindowScroll);
|
||||
PageStore.on('document_visibility_change', onDocumentVisibilityChange);
|
||||
|
||||
onWindowScroll();
|
||||
|
||||
return () => {
|
||||
PageStore.removeListener('window_scroll', onWindowScroll);
|
||||
PageStore.removeListener('document_visibility_change', onDocumentVisibilityChange);
|
||||
|
||||
if (listHandler) {
|
||||
listHandler.cancelAll();
|
||||
setListHandler(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return !countedItems ? (
|
||||
<PendingItemsList className={classname.listOuter} />
|
||||
) : !items.length ? null : (
|
||||
<div className={classname.listOuter}>
|
||||
{renderBeforeListWrap()}
|
||||
|
||||
<div ref={itemsListWrapperRef} className="items-list-wrap">
|
||||
<div ref={itemsListRef} className={classname.list}>
|
||||
{items.map((itm, index) => (
|
||||
<ListItem key={index} {...listItemProps(props, itm, index)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renderAfterListWrap()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
LazyLoadItemListAsync.propTypes = {
|
||||
...ItemListAsync.propTypes,
|
||||
};
|
||||
|
||||
LazyLoadItemListAsync.defaultProps = {
|
||||
...ItemListAsync.defaultProps,
|
||||
pageItems: 2,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { SpinnerLoader } from '../_shared/';
|
||||
|
||||
export function PendingItemsList(props) {
|
||||
return (
|
||||
<div className={props.className}>
|
||||
<div className="items-list-wrap items-list-wrap-waiting">
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
function calcCurrentSlide(wrapperDom, itemWidth, currentSlide) {
|
||||
return wrapperDom.scrollLeft ? 1 + Math.ceil(wrapperDom.scrollLeft / itemWidth) : currentSlide;
|
||||
}
|
||||
|
||||
export default function ItemsInlineSlider(container, itemSelector) {
|
||||
if (void 0 === container) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data = {
|
||||
dom: {
|
||||
wrapper: container,
|
||||
firstItem: container.querySelector(itemSelector),
|
||||
},
|
||||
item: {
|
||||
// selector: itemSelector,
|
||||
width: null,
|
||||
},
|
||||
};
|
||||
|
||||
this.data.item.width = this.data.dom.firstItem.offsetWidth;
|
||||
|
||||
this.state = {
|
||||
initedAllStateValues: false,
|
||||
currentSlide: 1,
|
||||
maxSlideIndex: null,
|
||||
slideItemsFit: null,
|
||||
slideItems: null,
|
||||
totalItems: null,
|
||||
wrapper: {
|
||||
width: null,
|
||||
scrollWidth: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ItemsInlineSlider.prototype.updateDataStateOnResize = function (totalItems, itemsLoadedAll) {
|
||||
this.data.item.width = this.data.dom.firstItem.offsetWidth;
|
||||
|
||||
this.state.wrapper.width = this.data.dom.wrapper.offsetWidth;
|
||||
this.state.wrapper.scrollWidth = this.data.dom.wrapper.scrollWidth;
|
||||
this.state.slideItemsFit = Math.floor(this.state.wrapper.width / this.data.item.width);
|
||||
|
||||
this.state.slideItems = Math.max(1, this.state.slideItemsFit);
|
||||
|
||||
if (itemsLoadedAll && this.state.slideItems <= this.state.slideItemsFit) {
|
||||
this.state.itemsLengthFit = this.state.slideItems;
|
||||
}
|
||||
|
||||
this.state.totalItems = totalItems;
|
||||
|
||||
this.state.maxSlideIndex = Math.max(1 + (this.state.totalItems - this.state.slideItemsFit));
|
||||
|
||||
this.state.currentSlide = Math.min(this.state.currentSlide, this.state.maxSlideIndex || 1);
|
||||
this.state.currentSlide = 0 >= this.state.currentSlide ? 1 : this.state.currentSlide;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.updateDataState = function (totalItems, itemsLoadedAll, forcedRefresh) {
|
||||
if (forcedRefresh || !this.state.initedAllStateValues) {
|
||||
this.state.initedAllStateValues = true;
|
||||
|
||||
this.state.wrapper.width = this.data.dom.wrapper.offsetWidth;
|
||||
this.state.wrapper.scrollWidth = this.data.dom.wrapper.scrollWidth;
|
||||
this.state.slideItemsFit = Math.floor(this.state.wrapper.width / this.data.item.width);
|
||||
|
||||
this.state.slideItems = Math.max(1, this.state.slideItemsFit);
|
||||
|
||||
if (itemsLoadedAll && this.state.slideItems <= this.state.slideItemsFit) {
|
||||
this.state.itemsLengthFit = this.state.slideItems;
|
||||
}
|
||||
}
|
||||
|
||||
this.state.totalItems = totalItems;
|
||||
|
||||
this.state.maxSlideIndex = Math.max(1, 1 + (this.state.totalItems - this.state.slideItemsFit));
|
||||
|
||||
this.state.currentSlide = Math.min(this.state.currentSlide, this.state.maxSlideIndex);
|
||||
this.state.currentSlide = 0 >= this.state.currentSlide ? 1 : this.state.currentSlide;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.nextSlide = function () {
|
||||
this.state.currentSlide = Math.min(
|
||||
calcCurrentSlide(this.data.dom.wrapper, this.data.item.width, this.state.currentSlide) + this.state.slideItems,
|
||||
this.state.maxSlideIndex
|
||||
);
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.previousSlide = function () {
|
||||
this.state.currentSlide = Math.max(
|
||||
1,
|
||||
calcCurrentSlide(this.data.dom.wrapper, this.data.item.width, this.state.currentSlide) - this.state.slideItems
|
||||
);
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.scrollToCurrentSlide = function () {
|
||||
this.data.dom.wrapper.scrollLeft = this.data.item.width * (this.state.currentSlide - 1);
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.hasNextSlide = function () {
|
||||
return this.state.currentSlide < this.state.maxSlideIndex;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.hasPreviousSlide = function () {
|
||||
return 1 < this.state.currentSlide;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.currentSlide = function () {
|
||||
return this.state.currentSlide;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.loadItemsToFit = function () {
|
||||
// Set slider minimum items length ( 2 * this.state.slideItemsFit ).
|
||||
return 2 * this.state.slideItemsFit > this.state.totalItems;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.loadMoreItems = function () {
|
||||
return this.state.currentSlide + this.state.slideItemsFit >= this.state.maxSlideIndex;
|
||||
};
|
||||
|
||||
ItemsInlineSlider.prototype.itemsFit = function () {
|
||||
return this.state.slideItemsFit;
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
import { PageStore } from '../../../../utils/stores/';
|
||||
import { formatInnerLink, getRequest } from '../../../../utils/helpers/';
|
||||
|
||||
export function ItemsListHandler(
|
||||
itemsPerPage,
|
||||
maxItems,
|
||||
first_item_request_url,
|
||||
request_url,
|
||||
itemsCountCallback,
|
||||
loadItemsCallback
|
||||
) {
|
||||
const config = {
|
||||
maxItems: maxItems || 255,
|
||||
pageItems: itemsPerPage ? Math.min(maxItems, itemsPerPage) : 1,
|
||||
};
|
||||
|
||||
const state = {
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
nextRequestUrl: formatInnerLink(request_url, PageStore.get('config-site').url),
|
||||
};
|
||||
|
||||
const waiting = {
|
||||
pageItems: 0,
|
||||
requestResponse: false,
|
||||
};
|
||||
|
||||
let firstItemUrl = null;
|
||||
|
||||
const items = [];
|
||||
const responseItems = [];
|
||||
|
||||
const callbacks = {
|
||||
itemsCount: function () {
|
||||
if ('function' === typeof itemsCountCallback) {
|
||||
itemsCountCallback(state.totalItems);
|
||||
}
|
||||
},
|
||||
itemsLoad: function () {
|
||||
if ('function' === typeof loadItemsCallback) {
|
||||
loadItemsCallback(items);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function loadNextItems(itemsLength) {
|
||||
let itemsToLoad, needExtraRequest;
|
||||
|
||||
itemsLength = !isNaN(itemsLength) ? itemsLength : config.pageItems;
|
||||
|
||||
if (waiting.pageItems && waiting.pageItems <= responseItems.length) {
|
||||
itemsToLoad = waiting.pageItems;
|
||||
needExtraRequest = false;
|
||||
waiting.pageItems = 0;
|
||||
} else {
|
||||
itemsToLoad = Math.min(itemsLength, responseItems.length);
|
||||
needExtraRequest = itemsLength > responseItems.length && !!state.nextRequestUrl;
|
||||
waiting.pageItems = needExtraRequest ? itemsLength - responseItems.length : 0;
|
||||
}
|
||||
|
||||
if (itemsToLoad) {
|
||||
let i = 0;
|
||||
while (i < itemsToLoad) {
|
||||
items.push(responseItems.shift());
|
||||
i += 1;
|
||||
}
|
||||
callbacks.itemsLoad();
|
||||
}
|
||||
|
||||
if (needExtraRequest) {
|
||||
runRequest();
|
||||
}
|
||||
}
|
||||
|
||||
function runFirstItemRequest() {
|
||||
function fn(response) {
|
||||
if (!!!response || !!!response.data) {
|
||||
} else {
|
||||
let data = response.data;
|
||||
let results = void 0 !== data.results ? data.results : data; // NOTE: The structure of response data in case of categories differs from the others.
|
||||
|
||||
if (results.length) {
|
||||
firstItemUrl = results[0].url;
|
||||
items.push(results[0]);
|
||||
}
|
||||
}
|
||||
|
||||
runRequest(true);
|
||||
}
|
||||
|
||||
getRequest(formatInnerLink(first_item_request_url, PageStore.get('config-site').url), false, fn);
|
||||
}
|
||||
|
||||
function runRequest(initialRequest) {
|
||||
waiting.requestResponse = true;
|
||||
|
||||
function fn(response) {
|
||||
waiting.requestResponse = false;
|
||||
|
||||
if (!!!response || !!!response.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = response.data;
|
||||
let results = void 0 !== data.results ? data.results : data; // NOTE: The structure of response data in case of categories differs from the others.
|
||||
|
||||
let i = 0;
|
||||
while (i < results.length && config.maxItems > responseItems.length) {
|
||||
if (null === firstItemUrl || firstItemUrl !== results[i].url) {
|
||||
responseItems.push(results[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
state.nextRequestUrl = !!data.next && config.maxItems > responseItems.length ? data.next : null;
|
||||
|
||||
if (initialRequest) {
|
||||
// In some cases, (total) 'count' field is missing, but probably doesn't need (eg. in recommended media).
|
||||
state.totalItems = !!data.count ? data.count : responseItems.length;
|
||||
state.totalItems = Math.min(config.maxItems, state.totalItems);
|
||||
|
||||
state.totalPages = Math.ceil(state.totalItems / config.pageItems);
|
||||
|
||||
callbacks.itemsCount();
|
||||
}
|
||||
|
||||
loadNextItems();
|
||||
}
|
||||
|
||||
getRequest(state.nextRequestUrl, false, fn);
|
||||
|
||||
state.nextRequestUrl = null;
|
||||
}
|
||||
|
||||
function loadItems(itemsLength) {
|
||||
if (!waiting.requestResponse && items.length < state.totalItems) {
|
||||
loadNextItems(itemsLength);
|
||||
}
|
||||
}
|
||||
|
||||
function totalPages() {
|
||||
return state.totalPages;
|
||||
}
|
||||
|
||||
function loadedAllItems() {
|
||||
return items.length === state.totalItems;
|
||||
}
|
||||
|
||||
function cancelAll() {
|
||||
itemsCountCallback = null;
|
||||
loadItemsCallback = null;
|
||||
}
|
||||
|
||||
if (void 0 !== first_item_request_url && null !== first_item_request_url) {
|
||||
runFirstItemRequest();
|
||||
} else {
|
||||
runRequest(true);
|
||||
}
|
||||
|
||||
return {
|
||||
loadItems,
|
||||
totalPages,
|
||||
loadedAllItems,
|
||||
cancelAll,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
export function ItemsStaticListHandler(itemsArray, itemsPerPage, maxItems, itemsCountCallback, loadItemsCallback) {
|
||||
const config = {
|
||||
maxItems: maxItems || 255,
|
||||
pageItems: itemsPerPage ? Math.min(maxItems, itemsPerPage) : 1,
|
||||
};
|
||||
|
||||
const state = {
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
};
|
||||
|
||||
let results = itemsArray;
|
||||
|
||||
const items = [];
|
||||
const responseItems = [];
|
||||
|
||||
const callbacks = {
|
||||
itemsCount: function () {
|
||||
if ('function' === typeof itemsCountCallback) {
|
||||
itemsCountCallback(state.totalItems);
|
||||
}
|
||||
},
|
||||
itemsLoad: function () {
|
||||
if ('function' === typeof loadItemsCallback) {
|
||||
loadItemsCallback(items);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function loadNextItems(itemsLength) {
|
||||
itemsLength = !isNaN(itemsLength) ? itemsLength : config.pageItems;
|
||||
|
||||
let itemsToLoad = Math.min(itemsLength, responseItems.length);
|
||||
|
||||
if (itemsToLoad) {
|
||||
let i = 0;
|
||||
while (i < itemsToLoad) {
|
||||
items.push(responseItems.shift());
|
||||
i += 1;
|
||||
}
|
||||
|
||||
callbacks.itemsLoad();
|
||||
}
|
||||
}
|
||||
|
||||
function loadItems(itemsLength) {
|
||||
if (items.length < state.totalItems) {
|
||||
loadNextItems(itemsLength);
|
||||
}
|
||||
}
|
||||
|
||||
function totalPages() {
|
||||
return state.totalPages;
|
||||
}
|
||||
|
||||
function loadedAllItems() {
|
||||
return items.length === state.totalItems;
|
||||
}
|
||||
|
||||
function cancelAll() {
|
||||
itemsCountCallback = null;
|
||||
loadItemsCallback = null;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
while (i < results.length && config.maxItems > responseItems.length) {
|
||||
responseItems.push(results[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
state.totalItems = Math.min(config.maxItems, results.length);
|
||||
state.totalPages = Math.ceil(state.totalItems / config.pageItems);
|
||||
|
||||
callbacks.itemsCount();
|
||||
|
||||
loadNextItems();
|
||||
|
||||
return {
|
||||
loadItems,
|
||||
totalPages,
|
||||
loadedAllItems,
|
||||
cancelAll,
|
||||
};
|
||||
}
|
||||
53
frontend/src/static/js/components/item-list/includes/itemLists/MediaItem.js
Executable file
53
frontend/src/static/js/components/item-list/includes/itemLists/MediaItem.js
Executable file
@@ -0,0 +1,53 @@
|
||||
import MediaItemPreviewer from './MediaItemPreviewer';
|
||||
|
||||
let mediaPreviewerInstance = null;
|
||||
|
||||
var CSS_selectors = {
|
||||
mediaItemPreviewer: '.item-img-preview',
|
||||
};
|
||||
|
||||
var DataAttributes = {
|
||||
mediaPreviewSrc: 'data-src',
|
||||
mediaPreviewExt: 'data-ext',
|
||||
};
|
||||
|
||||
export default class MediaItem {
|
||||
constructor(item) {
|
||||
if (!Node.prototype.isPrototypeOf(item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.element = item;
|
||||
|
||||
this.previewer = {
|
||||
element: item.querySelector(CSS_selectors.mediaItemPreviewer),
|
||||
};
|
||||
|
||||
let tmp;
|
||||
|
||||
if (this.previewer.element) {
|
||||
tmp = this.previewer.element.getAttribute(DataAttributes.mediaPreviewSrc);
|
||||
|
||||
if (tmp) {
|
||||
this.previewer.src = tmp.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.previewer.src) {
|
||||
tmp = this.previewer.element.getAttribute(DataAttributes.mediaPreviewExt);
|
||||
|
||||
if (tmp) {
|
||||
this.previewer.extensions = tmp.trim().split(',');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.previewer.extensions) {
|
||||
mediaPreviewerInstance = mediaPreviewerInstance || new MediaItemPreviewer(this.previewer.extensions);
|
||||
mediaPreviewerInstance.elementEvents(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
element() {
|
||||
return this.element;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
import { PageStore } from '../../../../utils/stores/';
|
||||
import { cancelAnimationFrame, requestAnimationFrame, addClassname } from '../../../../utils/helpers/';
|
||||
|
||||
Array.isArray =
|
||||
Array.isArray ||
|
||||
function (arg) {
|
||||
'use strict';
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
};
|
||||
|
||||
var hoverTimeoutID, requestAnimationFrameID;
|
||||
|
||||
var CSS_selectors = {
|
||||
mediaItemPreviewer: '.item-img-preview',
|
||||
};
|
||||
|
||||
var DataAttributes = {
|
||||
mediaPreviewSrc: 'data-src',
|
||||
};
|
||||
|
||||
export default class MediaItemPreviewer {
|
||||
constructor(extensions) {
|
||||
if (!Array.isArray(extensions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.extensions = {};
|
||||
|
||||
function onImageLoad(ins, evt) {
|
||||
requestAnimationFrameID = requestAnimationFrame(function () {
|
||||
if (ins.wrapperItem) {
|
||||
addClassname(ins.wrapperItem, 'on-hover-preview');
|
||||
requestAnimationFrameID = void 0;
|
||||
ins.wrapperItem = void 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const fallback_ext = ['png', 'jpg', 'jpeg']; // NOTE: Keep extentions order.
|
||||
let i, x;
|
||||
|
||||
this.element = null;
|
||||
|
||||
if (-1 < extensions.indexOf('webp')) {
|
||||
x = document.createElement('source');
|
||||
x.type = 'image/webp';
|
||||
this.extensions.anim = this.extensions.anim || [];
|
||||
this.extensions.anim.push({ elem: x, type: 'webp' });
|
||||
if (1 === extensions.length) {
|
||||
this.extensions.fallback = { elem: document.createElement('img'), type: 'webp' };
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 < extensions.indexOf('gif')) {
|
||||
x = document.createElement('source');
|
||||
x.type = 'image/gif';
|
||||
this.extensions.anim = this.extensions.anim || [];
|
||||
this.extensions.anim.push({ elem: x, type: 'gif' });
|
||||
this.extensions.fallback = { elem: document.createElement('img'), type: 'gif' };
|
||||
}
|
||||
|
||||
if (-1 < extensions.indexOf('jpg')) {
|
||||
x = document.createElement('source');
|
||||
x.type = 'image/jpg';
|
||||
this.extensions.anim = this.extensions.anim || [];
|
||||
this.extensions.anim.push({ elem: x, type: 'jpg' });
|
||||
this.extensions.fallback = { elem: document.createElement('img'), type: 'jpg' };
|
||||
}
|
||||
|
||||
if (-1 < extensions.indexOf('jpeg')) {
|
||||
x = document.createElement('source');
|
||||
x.type = 'image/jpeg';
|
||||
this.extensions.anim = this.extensions.anim || [];
|
||||
this.extensions.anim.push({ elem: x, type: 'jpeg' });
|
||||
this.extensions.fallback = { elem: document.createElement('img'), type: 'jpeg' };
|
||||
}
|
||||
|
||||
if (!this.extensions.fallback.elem) {
|
||||
i = 0;
|
||||
while (i < fallback_extensions.length) {
|
||||
if (-1 < extensions.indexOf(fallback_ext[i])) {
|
||||
this.extensions.fallback = { elem: document.createElement('img'), type: fallback_ext[i] };
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.extensions.anim.length || this.extensions.fallback.elem) {
|
||||
this.element = document.createElement('picture');
|
||||
|
||||
if (this.extensions.anim.length) {
|
||||
i = 0;
|
||||
while (i < this.extensions.anim.length) {
|
||||
this.element.appendChild(this.extensions.anim[i].elem);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.extensions.fallback.elem) {
|
||||
this.element.appendChild(this.extensions.fallback.elem);
|
||||
}
|
||||
|
||||
this.image = this.element.querySelector('img');
|
||||
this.image.addEventListener('load', onImageLoad.bind(null, this));
|
||||
}
|
||||
}
|
||||
|
||||
elementEvents(el) {
|
||||
el.addEventListener('mouseenter', this.onMediaItemMouseEnter.bind(null, this));
|
||||
el.addEventListener('mouseleave', this.onMediaItemMouseLeave.bind(null, this));
|
||||
}
|
||||
|
||||
newImage(src, width, height, item) {
|
||||
let i;
|
||||
|
||||
if (void 0 !== hoverTimeoutID) {
|
||||
clearTimeout(hoverTimeoutID);
|
||||
}
|
||||
|
||||
if (void 0 !== requestAnimationFrameID) {
|
||||
cancelAnimationFrame(requestAnimationFrameID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set source (src).
|
||||
*/
|
||||
|
||||
if (this.extensions.anim.length) {
|
||||
i = 0;
|
||||
while (i < this.extensions.anim.length) {
|
||||
this.extensions.anim[i].elem.setAttribute('srcset', src + '.' + this.extensions.anim[i].type);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if (this.extensions.fallback.elem) {
|
||||
this.extensions.fallback.elem.setAttribute('src', src + '.' + this.extensions.fallback.type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set dimensions (src).
|
||||
*/
|
||||
|
||||
if (this.extensions.fallback.elem) {
|
||||
this.extensions.fallback.elem.setAttribute('width', width + 'px');
|
||||
this.extensions.fallback.elem.setAttribute('height', height + 'px');
|
||||
}
|
||||
|
||||
/*
|
||||
* Append previewer.
|
||||
*/
|
||||
|
||||
item.querySelector(CSS_selectors.mediaItemPreviewer).appendChild(this.element);
|
||||
|
||||
/*
|
||||
* Set previewer's container element.
|
||||
*/
|
||||
|
||||
this.wrapperItem = item;
|
||||
}
|
||||
|
||||
onMediaItemMouseEnter(ins, evt) {
|
||||
var elem, src;
|
||||
|
||||
if (ins.image) {
|
||||
elem = evt.target.querySelector(CSS_selectors.mediaItemPreviewer);
|
||||
src =
|
||||
PageStore.get('config-site').url + '/' + elem.getAttribute(DataAttributes.mediaPreviewSrc).replace(/^\//g, '');
|
||||
|
||||
hoverTimeoutID = setTimeout(function () {
|
||||
ins.newImage(src, 1 + elem.offsetWidth, 1 + elem.offsetHeight, evt.target);
|
||||
}, 100); // NOTE: Avoid loading unnecessary media, when mouse is moving fast over dom items.
|
||||
}
|
||||
}
|
||||
|
||||
onMediaItemMouseLeave(ins, evt) {
|
||||
if (void 0 !== hoverTimeoutID) {
|
||||
clearTimeout(hoverTimeoutID);
|
||||
}
|
||||
|
||||
if (void 0 !== requestAnimationFrameID) {
|
||||
cancelAnimationFrame(requestAnimationFrameID);
|
||||
}
|
||||
|
||||
ins.wrapperItem = void 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import MediaItem from './MediaItem';
|
||||
import { hasClassname } from '../../../../utils/helpers/';
|
||||
|
||||
const _MediaItemsListData = {};
|
||||
|
||||
export default class MediaItemsList {
|
||||
constructor(listContainer, initialItems) {
|
||||
if (!Node.prototype.isPrototypeOf(listContainer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_MediaItemsListData[
|
||||
Object.defineProperty(this, 'id', { value: 'MediaItemsList_' + Object.keys(_MediaItemsListData).length }).id
|
||||
] = {};
|
||||
|
||||
this.items = [];
|
||||
this.container = listContainer;
|
||||
this.horizontalItems = hasClassname(this.container, 'items-list-hor');
|
||||
|
||||
this.appendItems(initialItems);
|
||||
}
|
||||
|
||||
dataObject() {
|
||||
return _MediaItemsListData;
|
||||
}
|
||||
|
||||
appendItems(items) {
|
||||
var i;
|
||||
if (NodeList.prototype.isPrototypeOf(items)) {
|
||||
i = 0;
|
||||
while (i < items.length) {
|
||||
this.items.push(new MediaItem(items[i]));
|
||||
i += 1;
|
||||
}
|
||||
} else if (Node.prototype.isPrototypeOf(items)) {
|
||||
this.items.push(new MediaItem(items));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import MediaItemsList from './MediaItemsList';
|
||||
|
||||
var CSS_selectors = {
|
||||
mediaItems: '.item',
|
||||
};
|
||||
|
||||
var mediaItemsListInstances = [];
|
||||
|
||||
export default function (lists) {
|
||||
if (!lists.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let items,
|
||||
i = 0;
|
||||
|
||||
while (i < lists.length) {
|
||||
items = lists[i].querySelectorAll(CSS_selectors.mediaItems);
|
||||
|
||||
if (items.length) {
|
||||
mediaItemsListInstances = mediaItemsListInstances || [];
|
||||
mediaItemsListInstances.push(new MediaItemsList(lists[i], items));
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return mediaItemsListInstances;
|
||||
}
|
||||
7
frontend/src/static/js/components/item-list/index.js
Normal file
7
frontend/src/static/js/components/item-list/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './InlineSliderItemList.jsx';
|
||||
export * from './InlineSliderItemListAsync.jsx';
|
||||
export * from './ItemList.jsx';
|
||||
export * from './ItemListAsync.jsx';
|
||||
export * from './LazyLoadItemList.jsx';
|
||||
export * from './LazyLoadItemListAsync.jsx';
|
||||
export * from './PendingItemsList.jsx';
|
||||
Reference in New Issue
Block a user