react-editable-photo-grid 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/LICENSE +675 -0
- package/README.md +1 -0
- package/dist/index.js +1 -0
- package/package.json +49 -0
- package/src/PhotoGrid.tsx +94 -0
- package/src/components/PhotoControls.tsx +65 -0
- package/src/components/RowControls.tsx +35 -0
- package/src/index.js +3 -0
- package/src/styles.css +89 -0
- package/src/types.ts +48 -0
- package/src/utils.tsx +366 -0
- package/tsconfig.json +18 -0
- package/webpack.config.js +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var n={365:(n,t,e)=>{e.d(t,{A:()=>l});var o=e(601),r=e.n(o),a=e(314),i=e.n(a)()(r());i.push([n.id,".photogrid {\n padding: 0 0.125rem;\n}\n\n.photogrid--row__controls {\n display: block;\n position: absolute;\n left: 0.25rem;\n top: 50%;\n transform: translateY(-50%);\n z-index: 20;\n}\n\n.photogrid--row__controls > li {\n display: block;\n}\n\n.photogrid--photo__controls {\n display: block;\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n bottom: 0.5rem;\n text-align: center;\n}\n\n.photogrid--photo__controls > li {\n display: inline-block;\n}\n\n.photogrid--photo__controls > li + li {\n margin-left: 0.25rem;\n}\n\n.photogrid--photo__controls .photo__control, .photogrid--row__controls .row__control {\n width: 36px;\n height: 36px;\n display: block;\n background: #ddd;\n color: #333;\n font-size: 1.5rem;\n line-height: 2rem;\n}\n\n.photogrid--photo__row {\n display: block;\n background: transparent;\n min-height: 0;\n position: relative;\n z-index: 10;\n padding: 0;\n}\n\n.photogrid--photo__row.editing {\n padding-left: 2.75rem;\n}\n\n.photogrid--photo__column {\n position: relative;\n vertical-align: middle;\n display: block;\n margin: 0.125rem;\n}\n\n.photogrid--photo__column > img {\n display: block;\n max-width: 100%;\n max-height: 700px;\n height: auto;\n margin: 0;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n@media only screen and (min-width: 768px) {\n .photogrid--photo__row {\n display: flex;\n flex: 1 1 auto;\n }\n\n .photogrid--photo__column > img {\n display: inline-block;\n }\n}\n\n",""]);const l=i},314:n=>{n.exports=function(n){var t=[];return t.toString=function(){return this.map((function(t){var e="",o=void 0!==t[5];return t[4]&&(e+="@supports (".concat(t[4],") {")),t[2]&&(e+="@media ".concat(t[2]," {")),o&&(e+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),e+=n(t),o&&(e+="}"),t[2]&&(e+="}"),t[4]&&(e+="}"),e})).join("")},t.i=function(n,e,o,r,a){"string"==typeof n&&(n=[[null,n,void 0]]);var i={};if(o)for(var l=0;l<this.length;l++){var c=this[l][0];null!=c&&(i[c]=!0)}for(var u=0;u<n.length;u++){var s=[].concat(n[u]);o&&i[s[0]]||(void 0!==a&&(void 0===s[5]||(s[1]="@layer".concat(s[5].length>0?" ".concat(s[5]):""," {").concat(s[1],"}")),s[5]=a),e&&(s[2]?(s[1]="@media ".concat(s[2]," {").concat(s[1],"}"),s[2]=e):s[2]=e),r&&(s[4]?(s[1]="@supports (".concat(s[4],") {").concat(s[1],"}"),s[4]=r):s[4]="".concat(r)),t.push(s))}},t}},601:n=>{n.exports=function(n){return n[1]}},72:n=>{var t=[];function e(n){for(var e=-1,o=0;o<t.length;o++)if(t[o].identifier===n){e=o;break}return e}function o(n,o){for(var a={},i=[],l=0;l<n.length;l++){var c=n[l],u=o.base?c[0]+o.base:c[0],s=a[u]||0,p="".concat(u," ").concat(s);a[u]=s+1;var d=e(p),h={css:c[1],media:c[2],sourceMap:c[3],supports:c[4],layer:c[5]};if(-1!==d)t[d].references++,t[d].updater(h);else{var m=r(h,o);o.byIndex=l,t.splice(l,0,{identifier:p,updater:m,references:1})}i.push(p)}return i}function r(n,t){var e=t.domAPI(t);return e.update(n),function(t){if(t){if(t.css===n.css&&t.media===n.media&&t.sourceMap===n.sourceMap&&t.supports===n.supports&&t.layer===n.layer)return;e.update(n=t)}else e.remove()}}n.exports=function(n,r){var a=o(n=n||[],r=r||{});return function(n){n=n||[];for(var i=0;i<a.length;i++){var l=e(a[i]);t[l].references--}for(var c=o(n,r),u=0;u<a.length;u++){var s=e(a[u]);0===t[s].references&&(t[s].updater(),t.splice(s,1))}a=c}}},659:n=>{var t={};n.exports=function(n,e){var o=function(n){if(void 0===t[n]){var e=document.querySelector(n);if(window.HTMLIFrameElement&&e instanceof window.HTMLIFrameElement)try{e=e.contentDocument.head}catch(n){e=null}t[n]=e}return t[n]}(n);if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(e)}},540:n=>{n.exports=function(n){var t=document.createElement("style");return n.setAttributes(t,n.attributes),n.insert(t,n.options),t}},56:(n,t,e)=>{n.exports=function(n){var t=e.nc;t&&n.setAttribute("nonce",t)}},825:n=>{n.exports=function(n){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var t=n.insertStyleElement(n);return{update:function(e){!function(n,t,e){var o="";e.supports&&(o+="@supports (".concat(e.supports,") {")),e.media&&(o+="@media ".concat(e.media," {"));var r=void 0!==e.layer;r&&(o+="@layer".concat(e.layer.length>0?" ".concat(e.layer):""," {")),o+=e.css,r&&(o+="}"),e.media&&(o+="}"),e.supports&&(o+="}");var a=e.sourceMap;a&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),t.styleTagTransform(o,n,t.options)}(t,n,e)},remove:function(){!function(n){if(null===n.parentNode)return!1;n.parentNode.removeChild(n)}(t)}}}},113:n=>{n.exports=function(n,t){if(t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}}},t={};function e(o){var r=t[o];if(void 0!==r)return r.exports;var a=t[o]={id:o,exports:{}};return n[o](a,a.exports,e),a.exports}e.n=n=>{var t=n&&n.__esModule?()=>n.default:()=>n;return e.d(t,{a:t}),t},e.d=(n,t)=>{for(var o in t)e.o(t,o)&&!e.o(n,o)&&Object.defineProperty(n,o,{enumerable:!0,get:t[o]})},e.o=(n,t)=>Object.prototype.hasOwnProperty.call(n,t),e.r=n=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.nc=void 0;var o={};e.r(o),e.d(o,{PhotoGrid:()=>j});const r=require("react");var a=e.n(r),i=function(){return i=Object.assign||function(n){for(var t,e=1,o=arguments.length;e<o;e++)for(var r in t=arguments[e])Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n},i.apply(this,arguments)},l=function(n){return n.sort((function(n,t){return Math.floor(n.column)-Math.floor(t.column)}))},c=function(n,t){var e=n.find((function(n){return n.id===t}));if(void 0===e)throw new TypeError("No photo was found in this row for that id");return e},u=function(n,t){var e=n.find((function(n){return n.column===t}));if(void 0===e)throw new TypeError("No photo was found in this row for that column");return e},s=function(n,t){return n.filter((function(n){return n.column!==t}))},p=function(n,t,e){return void 0===n&&(n=[]),t.column=e,n.push(t),n},d=function(n,t,e){var o=t.column,r=e.column;return n=s(n,o),n=s(n,r),n=p(n,t,r),p(n,e,o)},h=function(n){return"string"==typeof n&&(n=parseInt(n)),n},m=function(n){var t=n.currentTarget,e=t.dataset.id,o=t.dataset.row;if(!e||!o)throw new TypeError("id or row key missing from photo control");return{id:e,rowKey:parseInt(o)}},f=function(n,t,e){for(var o=t;o<=e;o++){var r=u(n,o);n=s(n,o),n=p(n,r,r.column-1)}return n},v=function(n,t,e,o,r){return delete n[r],delete n[e],n[r]=t,n[e]=o,n},w=e(72),g=e.n(w),y=e(825),_=e.n(y),b=e(659),E=e.n(b),C=e(56),k=e.n(C),x=e(540),T=e.n(x),K=e(113),N=e.n(K),P=e(365),M={};M.styleTagTransform=N(),M.setAttributes=k(),M.insert=E().bind(null,"head"),M.domAPI=_(),M.insertStyleElement=T(),g()(P.A,M),P.A&&P.A.locals&&P.A.locals;const R=function(n){return a().createElement("ul",{className:"photogrid--row__controls"},h(n.rowKey)>1&&a().createElement("li",null,a().createElement("button",{className:"row__control",onClick:n.moveRowUp,"data-row":n.rowKey},"↑")),h(n.rowKey)<n.rowCount&&a().createElement("li",null,a().createElement("button",{className:"row__control",onClick:n.moveRowDown,"data-row":n.rowKey},"↓")))},O=function(n){return a().createElement("ul",{className:"photogrid--photo__controls"},n.photo.column>1&&a().createElement("li",null,a().createElement("button",{type:"button",className:"photo__control",onClick:n.movePhotoLeft,"data-id":n.photo.id,"data-row":n.rowKey},"←")),h(n.rowKey)>1&&a().createElement("li",null,a().createElement("button",{type:"button",className:"photo__control",onClick:n.movePhotoUp,"data-id":n.photo.id,"data-row":n.rowKey},"↑")),h(n.rowKey)<n.rowCount||h(n.rowKey)===n.rowCount&&n.photoCount>1?a().createElement("li",null,a().createElement("button",{type:"button",className:"photo__control",onClick:n.movePhotoDown,"data-id":n.photo.id,"data-row":n.rowKey},"↓")):null,n.photo.column<n.photoCount&&a().createElement("li",null,a().createElement("button",{type:"button",className:"photo__control",onClick:n.movePhotoRight,"data-id":n.photo.id,"data-row":n.rowKey},"→")))},j=function(n){var t=function(t){!function(n,t){n.preventDefault();var e=m(n),o=e.id,r=e.rowKey,a=i({},t.rows),u=l(a[r]),d=r-1,h=[];null!=a[d]&&(h=l(a[d]));var v=c(u,o),w=v.column+1,g=u.length;u=s(u,v.column),delete a[r],u.length&&(f(u,w,g),a[r]=u),h=p(h,v,h.length+1),delete a[d],a[d]=h,t.updateRows(a),t.increaseChanges()}(t,n)},e=function(t){!function(n,t){n.preventDefault();var e=m(n),o=e.id,r=e.rowKey,a=i({},t.rows),d=l(a[r]),h=r+1,v=c(d,o),w=v.column+1,g=d.length;d=s(d,v.column),delete a[r],d.length&&(d=f(d,w,g));var y=null;null==a[h]?(v.column=1,y=[v]):(y=function(n,t){for(var e=t;e>0;e--){var o=u(n,e);n=s(n,e),n=p(n,o,o.column+1)}return n}(y=l(a[h]),y.length),y=p(y,v,1),delete a[h]),a[h]=y,d.length&&(a[r]=d),t.updateRows(a),t.increaseChanges()}(t,n)},o=function(t){!function(n,t){n.preventDefault();var e=m(n),o=e.id,r=e.rowKey,a=i({},t.rows),s=l(a[r]),p=c(s,o);if(1!==p.column){var h=u(s,p.column-1);s=d(s,h,p),delete a[r],a[r]=s,t.updateRows(a),t.increaseChanges()}}(t,n)},h=function(t){!function(n,t){n.preventDefault();var e=m(n),o=e.id,r=e.rowKey,a=i({},t.rows),s=l(a[r]),p=c(s,o);if(p.column!==s.length){var h=u(s,p.column+1);s=d(s,p,h),delete a[r],a[r]=s,t.updateRows(a),t.increaseChanges()}}(t,n)},w=function(t){!function(n,t){n.preventDefault();var e=n.currentTarget.dataset.row;if(!e)throw new TypeError("row missing from row control");var o=parseInt(e),r=o-1,a=i({},t.rows),c=l(a[o]),u=l(a[r]);a=v(a,c,o,u,r),t.updateRows(a),t.increaseChanges()}(t,n)},g=function(t){!function(n,t){n.preventDefault();var e=n.currentTarget.dataset.row;if(!e)throw new TypeError("row missing from row control");var o=parseInt(e),r=o+1,a=i({},t.rows),c=l(a[o]),u=l(a[r]);a=v(a,c,o,u,r),t.updateRows(a),t.increaseChanges()}(t,n)};return 0===Object.keys(n.rows).length?null:a().createElement("div",{className:"photogrid"},Object.entries(n.rows).map((function(i,c){return i[1].length&&a().createElement("div",{key:"row-"+c,className:n.isEditing?"photogrid--photo__row editing":"photogrid--photo__row"},a().createElement(a().Fragment,null,n.isEditing&&a().createElement(R,{rowKey:i[0],moveRowUp:w,moveRowDown:g,rowCount:Object.keys(n.rows).length}),l(i[1]).map((function(l,u){return a().createElement("div",{key:"photo-"+c+u,className:"photogrid--photo__column"},a().createElement("img",{width:l.width,height:l.height,"data-id":l.id,src:"/api/photos/"+l.thumbnail_path,alt:l.thumbnail_path}),n.isEditing&&a().createElement(a().Fragment,null,n.photoMenu?(0,r.cloneElement)(n.photoMenu,{photo:l}):null,a().createElement(O,{rowKey:i[0],photo:l,movePhotoDown:e,movePhotoLeft:o,movePhotoUp:t,movePhotoRight:h,rowCount:Object.keys(n.rows).length,photoCount:i[1].length})))}))))})))};module.exports=o})();
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-editable-photo-grid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "An editable photo grid built with React and Typescript",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "webpack"
|
|
8
|
+
},
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"@types/node": "^20.14.10",
|
|
11
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
12
|
+
"react-dom": "^17.0.0 || ^18.0.0",
|
|
13
|
+
"typescript": "^5.5.3"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@babel/core": "^7.24.9",
|
|
17
|
+
"@babel/preset-env": "^7.24.8",
|
|
18
|
+
"@babel/preset-react": "^7.24.7",
|
|
19
|
+
"@babel/preset-typescript": "^7.24.7",
|
|
20
|
+
"babel-loader": "^9.1.3",
|
|
21
|
+
"css-loader": "^7.1.2",
|
|
22
|
+
"style-loader": "^4.0.0",
|
|
23
|
+
"ts-loader": "^9.5.1",
|
|
24
|
+
"typescript": "^5.5.3",
|
|
25
|
+
"webpack": "^5.93.0",
|
|
26
|
+
"webpack-cli": "^5.1.4"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+ssh://git@github.com/DominicHart/react-photo-grid.git"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"photo",
|
|
34
|
+
"grid",
|
|
35
|
+
"react",
|
|
36
|
+
"typescript"
|
|
37
|
+
],
|
|
38
|
+
"author": "Dominic Hart",
|
|
39
|
+
"license": "GPL-3.0",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/DominicHart/react-photo-grid/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/DominicHart/react-photo-grid#readme",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@types/react": "^18.3.3",
|
|
46
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
47
|
+
"react-dom": "^17.0.0 || ^18.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { cloneElement } from 'react';
|
|
2
|
+
import { PhotoGridProps } from './types';
|
|
3
|
+
import { sortRow, movePhotoLeft, movePhotoUp, movePhotoDown, movePhotoRight, moveRowUp, moveRowDown } from "./utils";
|
|
4
|
+
import RowControls from './components/RowControls';
|
|
5
|
+
import PhotoControls from './components/PhotoControls';
|
|
6
|
+
import './styles.css';
|
|
7
|
+
|
|
8
|
+
const PhotoGrid = (props: PhotoGridProps) => {
|
|
9
|
+
const handleMovePhotoUp = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
10
|
+
movePhotoUp(e, props);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const handleMovePhotoDown = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
14
|
+
movePhotoDown(e, props);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const handleMovePhotoLeft = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
18
|
+
movePhotoLeft(e, props);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const handleMovePhotoRight = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
22
|
+
movePhotoRight(e, props);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const handleMoveRowUp = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
26
|
+
moveRowUp(e, props);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const handleMoveRowDown = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
30
|
+
moveRowDown(e, props);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (Object.keys(props.rows).length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="photogrid">
|
|
39
|
+
{Object.entries(props.rows).map((row, i) =>
|
|
40
|
+
row[1].length &&
|
|
41
|
+
<div
|
|
42
|
+
key={'row-' + i}
|
|
43
|
+
className={props.isEditing ? "photogrid--photo__row editing" : "photogrid--photo__row"}
|
|
44
|
+
>
|
|
45
|
+
<>
|
|
46
|
+
{props.isEditing &&
|
|
47
|
+
<RowControls
|
|
48
|
+
rowKey={row[0]}
|
|
49
|
+
moveRowUp={handleMoveRowUp}
|
|
50
|
+
moveRowDown={handleMoveRowDown}
|
|
51
|
+
rowCount={Object.keys(props.rows).length}
|
|
52
|
+
/>
|
|
53
|
+
}
|
|
54
|
+
{sortRow(row[1]).map((photo, i2) =>
|
|
55
|
+
<div
|
|
56
|
+
key={'photo-' + i + i2}
|
|
57
|
+
className="photogrid--photo__column"
|
|
58
|
+
>
|
|
59
|
+
<img
|
|
60
|
+
width={photo.width}
|
|
61
|
+
height={photo.height}
|
|
62
|
+
data-id={photo.id}
|
|
63
|
+
src={"/api/photos/" + photo.thumbnail_path}
|
|
64
|
+
alt={photo.thumbnail_path}
|
|
65
|
+
/>
|
|
66
|
+
{props.isEditing &&
|
|
67
|
+
<>
|
|
68
|
+
{props.photoMenu ?
|
|
69
|
+
cloneElement(props.photoMenu, {
|
|
70
|
+
photo: photo
|
|
71
|
+
})
|
|
72
|
+
: null}
|
|
73
|
+
<PhotoControls
|
|
74
|
+
rowKey={row[0]}
|
|
75
|
+
photo={photo}
|
|
76
|
+
movePhotoDown={handleMovePhotoDown}
|
|
77
|
+
movePhotoLeft={handleMovePhotoLeft}
|
|
78
|
+
movePhotoUp={handleMovePhotoUp}
|
|
79
|
+
movePhotoRight={handleMovePhotoRight}
|
|
80
|
+
rowCount={Object.keys(props.rows).length}
|
|
81
|
+
photoCount={row[1].length}
|
|
82
|
+
/>
|
|
83
|
+
</>
|
|
84
|
+
}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</>
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default PhotoGrid;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PhotoControlsProps } from '../types';
|
|
3
|
+
import { castRowKey } from '../utils';
|
|
4
|
+
import'../styles.css';
|
|
5
|
+
|
|
6
|
+
const PhotoControls = (props: PhotoControlsProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<ul className="photogrid--photo__controls">
|
|
9
|
+
{props.photo.column > 1 &&
|
|
10
|
+
<li>
|
|
11
|
+
<button
|
|
12
|
+
type="button"
|
|
13
|
+
className="photo__control"
|
|
14
|
+
onClick={props.movePhotoLeft}
|
|
15
|
+
data-id={props.photo.id}
|
|
16
|
+
data-row={props.rowKey}
|
|
17
|
+
>
|
|
18
|
+
←
|
|
19
|
+
</button>
|
|
20
|
+
</li>
|
|
21
|
+
}
|
|
22
|
+
{castRowKey(props.rowKey) > 1 &&
|
|
23
|
+
<li>
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
className="photo__control"
|
|
27
|
+
onClick={props.movePhotoUp}
|
|
28
|
+
data-id={props.photo.id}
|
|
29
|
+
data-row={props.rowKey}
|
|
30
|
+
>
|
|
31
|
+
↑
|
|
32
|
+
</button>
|
|
33
|
+
</li>
|
|
34
|
+
}
|
|
35
|
+
{castRowKey(props.rowKey) < props.rowCount || castRowKey(props.rowKey) === props.rowCount && props.photoCount > 1 ?
|
|
36
|
+
<li>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
className="photo__control"
|
|
40
|
+
onClick={props.movePhotoDown}
|
|
41
|
+
data-id={props.photo.id}
|
|
42
|
+
data-row={props.rowKey}
|
|
43
|
+
>
|
|
44
|
+
↓
|
|
45
|
+
</button>
|
|
46
|
+
</li> : null
|
|
47
|
+
}
|
|
48
|
+
{props.photo.column < props.photoCount &&
|
|
49
|
+
<li>
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
className="photo__control"
|
|
53
|
+
onClick={props.movePhotoRight}
|
|
54
|
+
data-id={props.photo.id}
|
|
55
|
+
data-row={props.rowKey}
|
|
56
|
+
>
|
|
57
|
+
→
|
|
58
|
+
</button>
|
|
59
|
+
</li>
|
|
60
|
+
}
|
|
61
|
+
</ul>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default PhotoControls;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { RowControlsProps } from "../types";
|
|
3
|
+
import { castRowKey } from '../utils';
|
|
4
|
+
import'../styles.css';
|
|
5
|
+
|
|
6
|
+
const RowControls = (props: RowControlsProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<ul className='photogrid--row__controls'>
|
|
9
|
+
{castRowKey(props.rowKey) > 1 &&
|
|
10
|
+
<li>
|
|
11
|
+
<button
|
|
12
|
+
className="row__control"
|
|
13
|
+
onClick={props.moveRowUp}
|
|
14
|
+
data-row={props.rowKey}
|
|
15
|
+
>
|
|
16
|
+
↑
|
|
17
|
+
</button>
|
|
18
|
+
</li>
|
|
19
|
+
}
|
|
20
|
+
{castRowKey(props.rowKey) < props.rowCount &&
|
|
21
|
+
<li>
|
|
22
|
+
<button
|
|
23
|
+
className="row__control"
|
|
24
|
+
onClick={props.moveRowDown}
|
|
25
|
+
data-row={props.rowKey}
|
|
26
|
+
>
|
|
27
|
+
↓
|
|
28
|
+
</button>
|
|
29
|
+
</li>
|
|
30
|
+
}
|
|
31
|
+
</ul>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default RowControls;
|
package/src/index.js
ADDED
package/src/styles.css
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.photogrid {
|
|
2
|
+
padding: 0 0.125rem;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.photogrid--row__controls {
|
|
6
|
+
display: block;
|
|
7
|
+
position: absolute;
|
|
8
|
+
left: 0.25rem;
|
|
9
|
+
top: 50%;
|
|
10
|
+
transform: translateY(-50%);
|
|
11
|
+
z-index: 20;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.photogrid--row__controls > li {
|
|
15
|
+
display: block;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.photogrid--photo__controls {
|
|
19
|
+
display: block;
|
|
20
|
+
position: absolute;
|
|
21
|
+
left: 50%;
|
|
22
|
+
transform: translateX(-50%);
|
|
23
|
+
bottom: 0.5rem;
|
|
24
|
+
text-align: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.photogrid--photo__controls > li {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.photogrid--photo__controls > li + li {
|
|
32
|
+
margin-left: 0.25rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.photogrid--photo__controls .photo__control, .photogrid--row__controls .row__control {
|
|
36
|
+
width: 36px;
|
|
37
|
+
height: 36px;
|
|
38
|
+
display: block;
|
|
39
|
+
background: #ddd;
|
|
40
|
+
color: #333;
|
|
41
|
+
font-size: 1.5rem;
|
|
42
|
+
line-height: 2rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.photogrid--photo__row {
|
|
46
|
+
display: block;
|
|
47
|
+
background: transparent;
|
|
48
|
+
min-height: 0;
|
|
49
|
+
position: relative;
|
|
50
|
+
z-index: 10;
|
|
51
|
+
padding: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.photogrid--photo__row.editing {
|
|
55
|
+
padding-left: 2.75rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.photogrid--photo__column {
|
|
59
|
+
position: relative;
|
|
60
|
+
vertical-align: middle;
|
|
61
|
+
display: block;
|
|
62
|
+
margin: 0.125rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.photogrid--photo__column > img {
|
|
66
|
+
display: block;
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
max-height: 700px;
|
|
69
|
+
height: auto;
|
|
70
|
+
margin: 0;
|
|
71
|
+
-webkit-touch-callout: none;
|
|
72
|
+
-webkit-user-select: none;
|
|
73
|
+
-khtml-user-select: none;
|
|
74
|
+
-moz-user-select: none;
|
|
75
|
+
-ms-user-select: none;
|
|
76
|
+
user-select: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media only screen and (min-width: 768px) {
|
|
80
|
+
.photogrid--photo__row {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex: 1 1 auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.photogrid--photo__column > img {
|
|
86
|
+
display: inline-block;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
|
|
3
|
+
export interface PhotoIdAndRowKey {
|
|
4
|
+
id: string;
|
|
5
|
+
rowKey: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface PhotoItem {
|
|
9
|
+
id: string;
|
|
10
|
+
column: number;
|
|
11
|
+
image_path: string;
|
|
12
|
+
thumbnail_path: string;
|
|
13
|
+
carousel_key: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PhotoRows {
|
|
19
|
+
[key: number]: PhotoItem[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PhotoGridProps {
|
|
23
|
+
rows: PhotoRows;
|
|
24
|
+
updateRows: (rows: PhotoRows) => void;
|
|
25
|
+
changes: number;
|
|
26
|
+
increaseChanges: () => void;
|
|
27
|
+
isEditing: boolean;
|
|
28
|
+
selectedPhotos: Array<string>;
|
|
29
|
+
updateSelectedPhotos: (ids: Array<string>) => void;
|
|
30
|
+
photoMenu: ReactElement | undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PhotoControlsProps {
|
|
34
|
+
rowKey: string | number;
|
|
35
|
+
photo: PhotoItem;
|
|
36
|
+
rowCount: number,
|
|
37
|
+
photoCount: number,
|
|
38
|
+
movePhotoLeft: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
39
|
+
movePhotoUp: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
40
|
+
movePhotoDown: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
41
|
+
movePhotoRight: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
42
|
+
}
|
|
43
|
+
export interface RowControlsProps {
|
|
44
|
+
rowKey: string | number;
|
|
45
|
+
moveRowUp: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
46
|
+
moveRowDown: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
47
|
+
rowCount: number
|
|
48
|
+
}
|