Skip to content

Commit

Permalink
feat(admin): added search and search to media grid
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanMatthias committed Feb 4, 2019
1 parent 27380fc commit 4991b8c
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 29 deletions.
3 changes: 2 additions & 1 deletion packages/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"access": "public"
},
"dependencies": {
"@origami/zen": "^0.0.2-alpha.1"
"@origami/zen": "^0.0.4-alpha.0",
"@origami/zen-lib": "^0.0.4-alpha.0"
}
}
25 changes: 17 additions & 8 deletions packages/admin/src/actions/UploadMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const uploadProgress = (file: File) =>
const reader = new FileReader();
reader.onload = (e: ProgressEvent) => {
// @ts-ignore
dispatch({ type: UPLOADING_MEDIA_PREVIEW, id, preview: e.target!.result});
dispatch({ type: UPLOADING_MEDIA_PREVIEW, id, preview: e.target!.result });
};
reader.readAsDataURL(file);

Expand All @@ -23,7 +23,7 @@ export const uploadProgress = (file: File) =>
request.open('POST', '/api/v1/media');
request.setRequestHeader('Authorization', API.token!);

dispatch({type: UPLOADING_MEDIA_START, id, name: file.name});
dispatch({ type: UPLOADING_MEDIA_START, id, name: file.name });

request.upload.onprogress = ((e: ProgressEvent) => {
dispatch({
Expand All @@ -40,12 +40,21 @@ export const uploadProgress = (file: File) =>
});
});

request.upload.onloadend = ((e: ProgressEvent) => {
dispatch({
type: UPLOADING_MEDIA_END,
id
});
});
request.onreadystatechange = () => {
const ready = 4;
if (request.readyState === ready) {
dispatch({
type: UPLOADING_MEDIA_END,
id
});

dispatch({
type: 'MEDIA_CREATED',
media: JSON.parse(request.response).data
});

}
};

request.send(formData);

Expand Down
4 changes: 3 additions & 1 deletion packages/admin/src/components/pages/Admin/Media/PageMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export class PageMedia extends titleSet('Media')(connect(store)(LitElement)) {
public static styles = [CSS];

public render() {
return html`<ui-media-grid></ui-media-grid>`;
return html`
<ui-media-grid searchable></ui-media-grid>
`;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {css} from 'lit-element';
// tslint:disable-next-line no-default-export export-name
export default css`.card{background:var(--card-bg, var(--color-white, #fff));border:var(--card-border, );border-radius:var(--card-border-radius, var(--border-radius, 0.4rem));padding:var(--card-padding, var(--size-main, 4rem));box-shadow:var(--card-shadow, var(--shadow-main-soft, 0 var(--size-tiny, 1rem) var(--size-main, 4rem) var(--color-main-soft, rgba(105,58,145,0.1))))}:host ui-media-grid{position:absolute;top:0;left:0;width:100%;height:100%}.card.hover{background-color:transparent;box-shadow:none;transition:all var(--transition-time)}.card.hover:hover{background-color:var(--card-bg);box-shadow:var(--card-shadow)}@keyframes fade-slide-up{from{margin-top:var(--size-large);opacity:0}to{margin-top:0;opacity:1}}:host{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;padding:var(--size-small)}
export default css`.card{background:var(--card-bg, var(--color-white, #fff));border:var(--card-border, );border-radius:var(--card-border-radius, var(--border-radius, 0.4rem));padding:var(--card-padding, var(--size-main, 4rem));box-shadow:var(--card-shadow, var(--shadow-main-soft, 0 var(--size-tiny, 1rem) var(--size-main, 4rem) var(--color-main-soft, rgba(105,58,145,0.1))))}.card.hover{background-color:transparent;box-shadow:none;transition:all var(--transition-time)}.card.hover:hover{background-color:var(--card-bg);box-shadow:var(--card-shadow)}@keyframes fade-slide-up{from{margin-top:var(--size-large);opacity:0}to{margin-top:0;opacity:1}}:host{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;padding:var(--size-small)}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@
bottom: 0;
overflow-y: auto;
padding: var(--size-small);

ui-media-grid {
@extend %cover;
}
}
35 changes: 30 additions & 5 deletions packages/admin/src/components/ui/MediaGrid/MediaGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ export class MediaGrid extends connect(store)(LitElement) {
@property({ type: Boolean, attribute: true })
public multiple: boolean = false;

@property({ type: Boolean, attribute: true })
public searchable: boolean = false;

public get value() {
return this._value;
}

@property()
private _value: MediaResource[] = [];


@property()
private _loading: boolean = true;

Expand All @@ -62,6 +64,9 @@ export class MediaGrid extends connect(store)(LitElement) {
@property()
private _meID: null | string = null;

@property()
private _filteredResources: GridResource[] = [];

@property({type: Boolean})
private _dragging: boolean = false;

Expand Down Expand Up @@ -106,9 +111,12 @@ export class MediaGrid extends connect(store)(LitElement) {

constructor() {
super();
store.dispatch<any>(mediaGet());
store.dispatch<any>(mediaGet()).then(() => {
this._filteredResources = this._resources;
});
}


public connectedCallback() {
super.connectedCallback();

Expand All @@ -127,10 +135,21 @@ export class MediaGrid extends connect(store)(LitElement) {

public render() {
if (this._loading) return html`<zen-loading></zen-loading>`;
const resources = this._resources;
const empty = resources.length === 0;
const _resources = this._resources;
const resources = this.searchable ? this._filteredResources : _resources;
const empty = _resources.length === 0 && resources.length === 0;

return html`
${this.searchable
? html`<ui-search
.keys=${['name']}
.items=${_resources}
@filtered=${({ detail }: { detail: GridResource[]}) => this._filteredResources = detail}
placeholder="Search for media…">
</ui-search>`
: null
}
<ul>
${repeat(resources, (r) => r.src, (r, i) => html`
<li
Expand Down Expand Up @@ -167,11 +186,17 @@ export class MediaGrid extends connect(store)(LitElement) {
`;
}

// public firstUpdated() {
// this._filteredResources = this._resources;
// }

public stateChanged(state: State) {
const media = state.resources.media;
if (this._loading !== media._loading.all) this._loading = state.resources.media._loading.all;
if (this._media.length !== media.media.length) this._media = state.resources.media.media;
if (this._media !== media.media) {
this._media = state.resources.media.media;
this._filteredResources = this._resources;
}
this._uploading = state.UploadingMedia.uploading;
if (!this._meID && state.Me.id) this._meID = state.Me.id;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {css} from 'lit-element';
// tslint:disable-next-line no-default-export export-name
export default css`.card{background:var(--card-bg, var(--color-white, #fff));border:var(--card-border, );border-radius:var(--card-border-radius, var(--border-radius, 0.4rem));padding:var(--card-padding, var(--size-main, 4rem));box-shadow:var(--card-shadow, var(--shadow-main-soft, 0 var(--size-tiny, 1rem) var(--size-main, 4rem) var(--color-main-soft, rgba(105,58,145,0.1))))}:host,:host *{box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;margin:0;padding:0;border:0;outline:0;font-size:100%;font:inherit;vertical-align:baseline}:host .drop-zone,:host .drop-zone:before,:host .drop-zone:after{border-radius:var(--border-radius)}:host ul li .details span{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host zen-loading,:host ul li zen-icon.tick,:host ul li ui-upload-progress,:host .drop-zone .center{position:absolute;left:50%;top:50%;transform:translate(-50%, -50%)}:host ul li:before,:host ul li.active:after,:host ul li .img ui-image,:host .drop-zone:before,:host .drop-zone:after{position:absolute;top:0;left:0;width:100%;height:100%}:host ul li .details ui-avatar{width:var(--size-medium);height:var(--size-medium)}.card.hover{background-color:transparent;box-shadow:none;transition:all var(--transition-time)}.card.hover:hover{background-color:var(--card-bg);box-shadow:var(--card-shadow)}@keyframes fade-slide-up{from{margin-top:var(--size-large);opacity:0}to{margin-top:0;opacity:1}}:host{display:block;--media-grid-columns: 5;--media-grid-gap: var(--size-medium)}:host zen-loading{--loading-size: var(--size-large)}:host ul{--card-padding: 0;display:grid;grid-template-columns:repeat(var(--media-grid-columns), 1fr);grid-gap:var(--media-grid-gap);list-style:none}:host ul li{position:relative;background:var(--color-white);overflow:hidden}:host ul li zen-icon.tick{width:40%;height:40%;transform:translate(-50%, -50%) scale(0);transition:all calc(var(--transition-time) / 2) var(--ease-out-back);z-index:1}:host ul li ui-upload-progress{transform:translate(-50%, -50%) scale(1.1);opacity:0;margin-top:calc(-1 * 5rem / 2);transition:all calc(var(--transition-time) / 2) ease-out}:host ul li:before{content:'';border:2px solid var(--color-active);opacity:0;transition:opacity calc(var(--transition-time) / 2) ease-out;box-sizing:border-box;z-index:1;visibility:hidden}:host ul li:after{content:'';opacity:0;background:var(--color-active);transition:opacity calc(var(--transition-time) / 2) ease-out}:host ul li.active zen-icon.tick{transform:translate(-50%, -50%) scale(1)}:host ul li.active:before{visibility:visible;opacity:1}:host ul li.active:after{opacity:0.5}:host ul li .img{position:relative}:host ul li .img:before{content:'';display:block;padding-bottom:56.25%}:host ul li .img ui-image{transition:all var(--transition-time) ease-out}:host ul li.uploading ui-image{transform:scale(1.2);filter:brightness(0.5)}:host ul li.uploading ui-upload-progress{transform:translate(-50%, -50%) scale(1);opacity:1}:host ul li .details{position:relative;display:flex;align-items:center;padding:var(--size-tiny) var(--size-small);height:5rem;background:var(--color-white)}:host ul li .details span{display:block;flex:1;margin-right:var(--size-tiny);font-size:var(--font-size-small);color:var(--color-grey-300)}:host ul li .details ui-avatar{flex-grow:0}:host .drop-zone{width:0;height:0}:host .drop-zone:before,:host .drop-zone:after{box-sizing:border-box;content:'';visibility:hidden;opacity:0;transition:all calc(var(--transition-time)) var(--ease-out-back)}:host .drop-zone:before{background:var(--color-active)}:host .drop-zone:after{border:2px dashed var(--color-active)}:host .drop-zone .center{visibility:hidden;font-size:var(--font-size-large)}:host .drop-zone .center *{display:block;margin:auto;transform:translateY(var(--size-main));transition:transform var(--transition-time) ease-out;opacity:0}:host .drop-zone .center span{transition-delay:calc(var(--transition-time) / 8)}:host .drop-zone.show:before{visibility:visible;opacity:0.5}:host .drop-zone.show:after{visibility:visible;opacity:1}:host .drop-zone.empty .center{color:var(--color-grey-200)}:host .drop-zone.show .center{color:var(--color-white)}:host .drop-zone.show .center,:host .drop-zone.empty .center{visibility:visible}:host .drop-zone.show .center *,:host .drop-zone.empty .center *{transform:none;opacity:1}
export default css`.card{background:var(--card-bg, var(--color-white, #fff));border:var(--card-border, );border-radius:var(--card-border-radius, var(--border-radius, 0.4rem));padding:var(--card-padding, var(--size-main, 4rem));box-shadow:var(--card-shadow, var(--shadow-main-soft, 0 var(--size-tiny, 1rem) var(--size-main, 4rem) var(--color-main-soft, rgba(105,58,145,0.1))))}:host,:host *{box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;margin:0;padding:0;border:0;outline:0;font-size:100%;font:inherit;vertical-align:baseline}:host .drop-zone,:host .drop-zone:before,:host .drop-zone:after{border-radius:var(--border-radius)}:host ul li .details span{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host zen-loading,:host ul li zen-icon.tick,:host ul li ui-upload-progress,:host .drop-zone .center{position:absolute;left:50%;top:50%;transform:translate(-50%, -50%)}:host ul li:before,:host ul li.active:after,:host ul li .img ui-image,:host .drop-zone:before,:host .drop-zone:after{position:absolute;top:0;left:0;width:100%;height:100%}:host ul li .details ui-avatar{width:var(--size-medium);height:var(--size-medium)}.card.hover{background-color:transparent;box-shadow:none;transition:all var(--transition-time)}.card.hover:hover{background-color:var(--card-bg);box-shadow:var(--card-shadow)}@keyframes fade-slide-up{from{margin-top:var(--size-large);opacity:0}to{margin-top:0;opacity:1}}:host{display:block;--media-grid-columns: 5;--media-grid-gap: var(--size-medium)}:host zen-loading{--loading-size: var(--size-large)}:host ui-search{display:block;margin:auto;margin-bottom:var(--size-small);max-width:60rem}:host ul{--card-padding: 0;display:grid;grid-template-columns:repeat(var(--media-grid-columns), 1fr);grid-gap:var(--media-grid-gap);list-style:none}:host ul li{position:relative;background:var(--color-white);overflow:hidden}:host ul li zen-icon.tick{width:40%;height:40%;transform:translate(-50%, -50%) scale(0);transition:all calc(var(--transition-time) / 2) var(--ease-out-back);z-index:1}:host ul li ui-upload-progress{transform:translate(-50%, -50%) scale(1.1);opacity:0;margin-top:calc(-1 * 5rem / 2);transition:all calc(var(--transition-time) / 2) ease-out}:host ul li:before{content:'';border:2px solid var(--color-active);opacity:0;transition:opacity calc(var(--transition-time) / 2) ease-out;box-sizing:border-box;z-index:1;visibility:hidden}:host ul li:after{content:'';opacity:0;background:var(--color-active);transition:opacity calc(var(--transition-time) / 2) ease-out}:host ul li.active zen-icon.tick{transform:translate(-50%, -50%) scale(1)}:host ul li.active:before{visibility:visible;opacity:1}:host ul li.active:after{opacity:0.5}:host ul li .img{position:relative}:host ul li .img:before{content:'';display:block;padding-bottom:56.25%}:host ul li .img ui-image{transition:all var(--transition-time) ease-out}:host ul li.uploading ui-image{transform:scale(1.2);filter:brightness(0.5)}:host ul li.uploading ui-upload-progress{transform:translate(-50%, -50%) scale(1);opacity:1}:host ul li .details{position:relative;display:flex;align-items:center;padding:var(--size-tiny) var(--size-small);height:5rem;background:var(--color-white)}:host ul li .details span{display:block;flex:1;margin-right:var(--size-tiny);font-size:var(--font-size-small);color:var(--color-grey-300)}:host ul li .details ui-avatar{flex-grow:0}:host .drop-zone{width:0;height:0}:host .drop-zone:before,:host .drop-zone:after{box-sizing:border-box;content:'';visibility:hidden;opacity:0;transition:all calc(var(--transition-time)) var(--ease-out-back)}:host .drop-zone:before{background:var(--color-active)}:host .drop-zone:after{border:2px dashed var(--color-active)}:host .drop-zone .center{visibility:hidden;font-size:var(--font-size-large)}:host .drop-zone .center *{display:block;margin:auto;transform:translateY(var(--size-main));transition:transform var(--transition-time) ease-out;opacity:0}:host .drop-zone .center span{transition-delay:calc(var(--transition-time) / 8)}:host .drop-zone.show:before{visibility:visible;opacity:0.5}:host .drop-zone.show:after{visibility:visible;opacity:1}:host .drop-zone.empty .center{color:var(--color-grey-200)}:host .drop-zone.show .center{color:var(--color-white)}:host .drop-zone.show .center,:host .drop-zone.empty .center{visibility:visible}:host .drop-zone.show .center *,:host .drop-zone.empty .center *{transform:none;opacity:1}
`;
7 changes: 7 additions & 0 deletions packages/admin/src/components/ui/MediaGrid/media-grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
--loading-size: var(--size-large);
}

ui-search {
display: block;
margin: auto;
margin-bottom: var(--size-small);
max-width: 60rem;
}

ul {
--card-padding: 0;
display: grid;
Expand Down
97 changes: 97 additions & 0 deletions packages/admin/src/components/ui/Search/Search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { ZenInput } from '@origami/zen';
import Fuse from 'fuse.js';
import { customElement, html, LitElement, property } from 'lit-element';
import CSS from './search-css';

export const SEARCH_EVENT_FILTERED = 'filtered';

@customElement('ui-search')
export class Search<FilterItem> extends LitElement {
public static styles = [CSS];


/**
* The items to filter
*/
@property()
public set items(v) {
this._items = v;
}
public get items() {
return this._items;
}

/**
* The text entered to filter the items
*/
@property()
get filter() {
return this._filter;
}
set filter(v) {
this._filter = v;
if (!this._fuse || !v) this._filtered = [];
else this._filtered = this._fuse.search(v);

this.dispatchEvent(new CustomEvent(
SEARCH_EVENT_FILTERED,
{detail: this.filter ? this._filtered : this.items}
));
}


get filtered() {
if (!this.filter) return this.items;
return this._filtered;
}


@property({type: String})
public placeholder: string = 'Search…';

/**
* Keys to pass into Fuse.js to lookup on the items
*/
@property({type: Array})
public keys: (keyof FilterItem)[] = [];

@property()
private _filtered: FilterItem[] = [];

private _items: FilterItem[] = [];

@property()
private _fuse?: Fuse<FilterItem>;
private _filter: string | null = null;


public render() {
return html`
<zen-input
icon="search"
iconColor="red"
placeholder=${this.placeholder}
@change=${this._onChange}
></zen-input>`;
}


public updated(props: Map<keyof Search<FilterItem>, string>) {
if (props.has('items') || props.has('keys')) {
this._fuse = new Fuse(this.items, {
keys: this.keys
});
}
}

private _onChange(e: Event) {
this.filter = (e.target as ZenInput).value;
}
}


declare global {
interface HTMLElementTagNameMap {
'ui-search': Search<any>;
}
}
4 changes: 4 additions & 0 deletions packages/admin/src/components/ui/Search/search-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {css} from 'lit-element';
// tslint:disable-next-line no-default-export export-name
export default css`:host{display:block}:host zen-input{width:100%}
`;
7 changes: 7 additions & 0 deletions packages/admin/src/components/ui/Search/search.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
:host {
display: block;

zen-input {
width: 100%;
}
}
1 change: 1 addition & 0 deletions packages/admin/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './Modal/Modal';
export * from './Modal/ModalContainer';
export * from './ResourcePage/ResourcePage';
export * from './ResourceTable/ResourceTable';
export * from './Search/Search';
export * from './Sidebar/Sidebar';
export * from './SideMenu/SideMenu';
export * from './UploadProgress/UploadProgress';
Expand Down
8 changes: 7 additions & 1 deletion packages/admin/src/reducers/UploadingMedia.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnyAction } from 'redux';
// tslint:disable-next-line match-default-export-name
import immutable, { ImmutableObjectMixin } from 'seamless-immutable';
import { UPLOADING_MEDIA_PREVIEW, UPLOADING_MEDIA_PROGRESS, UPLOADING_MEDIA_START } from '../actions/UploadMedia';
import { UPLOADING_MEDIA_END, UPLOADING_MEDIA_PREVIEW, UPLOADING_MEDIA_PROGRESS, UPLOADING_MEDIA_START } from '../actions/UploadMedia';
import { UploadingMedia as StateUploadingMedia, UploadingResource } from '../store/state';

const initialState = immutable.from<StateUploadingMedia>({
Expand Down Expand Up @@ -39,6 +39,12 @@ export const UploadingMedia = (

return state.setIn(['uploading', index2, 'preview'], action.preview);

case UPLOADING_MEDIA_END:
return state.set(
'uploading',
state.asMutable().uploading.filter((u) => u.id !== action.id)
);


default:
return state;
Expand Down
10 changes: 7 additions & 3 deletions packages/admin/src/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { applyMiddleware } from 'redux';
import { applyMiddleware, compose } from 'redux';
// tslint:disable-next-line match-default-export-name
import thunkMiddleware from 'redux-thunk';
import { createInjectStore } from '../lib/reduxInjector';
Expand All @@ -7,8 +7,12 @@ import { reducers } from '../reducers';

export const store = createInjectStore(
reducers,
applyMiddleware(
thunkMiddleware
compose(
applyMiddleware(
thunkMiddleware,
),
// @ts-ignore
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);

Expand Down
Loading

0 comments on commit 4991b8c

Please sign in to comment.