Compare commits
4 Commits
21f8096636
...
cacad4ed61
| Author | SHA1 | Date |
|---|---|---|
|
|
cacad4ed61 | |
|
|
51859ae136 | |
|
|
ee4591e60a | |
|
|
3c54dc752a |
|
|
@ -28,3 +28,6 @@ node_modules/
|
||||||
|
|
||||||
# dotenv environment variables file
|
# dotenv environment variables file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# component build files
|
||||||
|
build
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is generated. Do not modify it manually.
|
|
||||||
return array(
|
|
||||||
'theatergf-gallery' => array(
|
|
||||||
'$schema' => 'https://schemas.wp.org/trunk/block.json',
|
|
||||||
'apiVersion' => 3,
|
|
||||||
'name' => 'theatergf/theatergf-gallery',
|
|
||||||
'version' => '0.1.0',
|
|
||||||
'title' => 'Gallery',
|
|
||||||
'category' => 'media',
|
|
||||||
'example' => array(
|
|
||||||
|
|
||||||
),
|
|
||||||
'supports' => array(
|
|
||||||
'html' => false
|
|
||||||
),
|
|
||||||
'textdomain' => 'theatergf-gallery',
|
|
||||||
'editorScript' => 'file:./index.js',
|
|
||||||
'editorStyle' => 'file:./index.css',
|
|
||||||
'style' => 'file:./style-index.css',
|
|
||||||
'render' => 'file:./render.php',
|
|
||||||
'viewScript' => 'file:./view.js'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://schemas.wp.org/trunk/block.json",
|
|
||||||
"apiVersion": 3,
|
|
||||||
"name": "theatergf/theatergf-gallery",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"title": "Gallery",
|
|
||||||
"category": "media",
|
|
||||||
"example": {},
|
|
||||||
"supports": {
|
|
||||||
"html": false
|
|
||||||
},
|
|
||||||
"textdomain": "theatergf-gallery",
|
|
||||||
"editorScript": "file:./index.js",
|
|
||||||
"editorStyle": "file:./index.css",
|
|
||||||
"style": "file:./style-index.css",
|
|
||||||
"render": "file:./render.php",
|
|
||||||
"viewScript": "file:./view.js"
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.wp-block-theatergf-theatergf-gallery{border:1px dotted red}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-i18n'), 'version' => 'cf9890b5e4d67862718c');
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.wp-block-theatergf-theatergf-gallery{border:1px dotted red}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(()=>{"use strict";var r,e={640(){const r=window.wp.blocks,e=window.wp.i18n,t=window.wp.blockEditor,o=window.ReactJSXRuntime,l=JSON.parse('{"UU":"theatergf/theatergf-gallery"}');(0,r.registerBlockType)(l.UU,{edit:function(){return(0,o.jsx)("p",{...(0,t.useBlockProps)(),children:(0,e.__)("Gallery – hello from the editor!","theatergf-gallery")})}})}},t={};function o(r){var l=t[r];if(void 0!==l)return l.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,o),a.exports}o.m=e,r=[],o.O=(e,t,l,a)=>{if(!t){var i=1/0;for(p=0;p<r.length;p++){for(var[t,l,a]=r[p],n=!0,s=0;s<t.length;s++)(!1&a||i>=a)&&Object.keys(o.O).every(r=>o.O[r](t[s]))?t.splice(s--,1):(n=!1,a<i&&(i=a));if(n){r.splice(p--,1);var h=l();void 0!==h&&(e=h)}}return e}a=a||0;for(var p=r.length;p>0&&r[p-1][2]>a;p--)r[p]=r[p-1];r[p]=[t,l,a]},o.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r={53:0,513:0};o.O.j=e=>0===r[e];var e=(e,t)=>{var l,a,[i,n,s]=t,h=0;if(i.some(e=>0!==r[e])){for(l in n)o.o(n,l)&&(o.m[l]=n[l]);if(s)var p=s(o)}for(e&&e(t);h<i.length;h++)a=i[h],o.o(r,a)&&r[a]&&r[a][0](),r[a]=0;return o.O(p)},t=globalThis.webpackChunktheatergf_gallery=globalThis.webpackChunktheatergf_gallery||[];t.forEach(e.bind(null,0)),t.push=e.bind(null,t.push.bind(t))})();var l=o.O(void 0,[513],()=>o(640));l=o.O(l)})();
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP file to use when rendering the block type on the server to show on the front end.
|
|
||||||
*
|
|
||||||
* The following variables are exposed to the file:
|
|
||||||
* $attributes (array): The block attributes.
|
|
||||||
* $content (string): The block default content.
|
|
||||||
* $block (WP_Block): The block instance.
|
|
||||||
*
|
|
||||||
* @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
|
|
||||||
*/
|
|
||||||
?>
|
|
||||||
<p <?php echo get_block_wrapper_attributes(); ?>>
|
|
||||||
<?php esc_html_e( 'Gallery – hello from a dynamic block!', 'theatergf-gallery' ); ?>
|
|
||||||
</p>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.wp-block-theatergf-theatergf-gallery{background-color:#21759b;color:#fff;padding:2px}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.wp-block-theatergf-theatergf-gallery{background-color:#21759b;color:#fff;padding:2px}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<?php return array('dependencies' => array(), 'version' => '58813c89e11ed1cf4a36');
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
console.log("Hello World! (from theatergf-theatergf-gallery block)");
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,6 +18,6 @@
|
||||||
"@wordpress/i18n": "latest"
|
"@wordpress/i18n": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@wordpress/scripts": "^31.6.0"
|
"@wordpress/scripts": "latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ function gallery_app_init($hook) {
|
||||||
|
|
||||||
$gallery_app_dir = plugin_dir_url(__FILE__) . 'gallery';
|
$gallery_app_dir = plugin_dir_url(__FILE__) . 'gallery';
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dependencies
|
* Dependencies
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,26 @@ function admin_gallery_html($args) {
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1>Gallery</h1>
|
<h1>Gallery</h1>
|
||||||
|
|
||||||
|
<div id="notice-container"></div>
|
||||||
|
|
||||||
<div class="theatergf gallery_app">
|
<div class="theatergf gallery_app">
|
||||||
|
|
||||||
|
<div class="theatergf gallery-container">
|
||||||
|
<div class="gallery"></div>
|
||||||
|
<div class="spinner-container">
|
||||||
|
<span class="spinner"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="theatergf-save-selected" class="button button-primary">Save Selected</button>
|
||||||
|
|
||||||
|
<div style="height: 2px; background-color: black; margin: 5px 0;"></div>
|
||||||
|
|
||||||
<div id="theatergf-editor" class="theatergf image-editor">
|
<div id="theatergf-editor" class="theatergf image-editor">
|
||||||
<img class="editor-image">
|
<img class="editor-image">
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-button-row">
|
<div class="editor-button-row">
|
||||||
<button id="theatergf-select-image" class="button button-primary">Select image</button>
|
<button id="theatergf-select-image" class="button button-primary">Select image</button>
|
||||||
<button id="theatergf-edit-save" class="button button-primary">Save</button
|
<button id="theatergf-edit-save" class="button button-primary">Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,25 @@ function getRelativeClientRect(element) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNotice(type, message) {
|
||||||
|
const element = document.createElement('div')
|
||||||
|
|
||||||
|
element.className = `notice notice-${type} is-dismissible`;
|
||||||
|
element.innerHTML = `<p>${message}</p>`;
|
||||||
|
|
||||||
|
document.querySelector('#notice-container').appendChild(element);
|
||||||
|
|
||||||
|
jQuery(document).trigger('wp-updates-notice-added');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSuccessNotice(message) {
|
||||||
|
return createNotice("success", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createErrorNotice(message) {
|
||||||
|
return createNotice("error", message);
|
||||||
|
}
|
||||||
|
|
||||||
class ResizableBox {
|
class ResizableBox {
|
||||||
constructor(parent, bounding, resolution, aspectRatio = null) {
|
constructor(parent, bounding, resolution, aspectRatio = null) {
|
||||||
|
|
||||||
|
|
@ -38,6 +57,11 @@ class ResizableBox {
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
this.aspectRatio = aspectRatio;
|
this.aspectRatio = aspectRatio;
|
||||||
|
|
||||||
|
this.scale = {
|
||||||
|
x: resolution.width / bounding.width,
|
||||||
|
y: resolution.height / bounding.height
|
||||||
|
}
|
||||||
|
|
||||||
this.mouseStart = { x: 0, y: 0 };
|
this.mouseStart = { x: 0, y: 0 };
|
||||||
this.maxDelta = { x: 0, y: 0 };
|
this.maxDelta = { x: 0, y: 0 };
|
||||||
this.minDelta = { x: 0, y: 0 };
|
this.minDelta = { x: 0, y: 0 };
|
||||||
|
|
@ -128,8 +152,6 @@ class ResizableBox {
|
||||||
|
|
||||||
resize(delta, direction, handle_aspect_ratio = true) {
|
resize(delta, direction, handle_aspect_ratio = true) {
|
||||||
|
|
||||||
console.log(delta)
|
|
||||||
|
|
||||||
const sign = this.DIRECTIONS[direction].sign;
|
const sign = this.DIRECTIONS[direction].sign;
|
||||||
|
|
||||||
const normalized = {
|
const normalized = {
|
||||||
|
|
@ -137,12 +159,7 @@ class ResizableBox {
|
||||||
y: delta.y * sign.y
|
y: delta.y * sign.y
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(normalized)
|
|
||||||
console.log(this.minDelta)
|
|
||||||
console.log(this.maxDelta);
|
|
||||||
|
|
||||||
if (handle_aspect_ratio) {
|
if (handle_aspect_ratio) {
|
||||||
//delta = this._applyAspectRatio(delta.x, delta.y, direction)
|
|
||||||
|
|
||||||
if (Math.abs(normalized.x) > Math.abs(normalized.y * this.aspectRatio)) {
|
if (Math.abs(normalized.x) > Math.abs(normalized.y * this.aspectRatio)) {
|
||||||
normalized.x = clamp(normalized.x,
|
normalized.x = clamp(normalized.x,
|
||||||
|
|
@ -166,30 +183,53 @@ class ResizableBox {
|
||||||
delta.x = normalized.x * sign.x;
|
delta.x = normalized.x * sign.x;
|
||||||
delta.y = normalized.y * sign.y;
|
delta.y = normalized.y * sign.y;
|
||||||
|
|
||||||
console.log(delta)
|
let left = this.elemStart.left;
|
||||||
|
let top = this.elemStart.top;
|
||||||
|
let right = this.elemStart.right;
|
||||||
|
let bottom = this.elemStart.bottom;
|
||||||
|
|
||||||
switch(this.direction) {
|
switch(this.direction) {
|
||||||
case 'nw':
|
case 'nw':
|
||||||
this.element.style.left = (this.elemStart.left + delta.x) + "px";
|
left += delta.x;
|
||||||
this.element.style.top = (this.elemStart.top + delta.y) + "px";
|
top += delta.y;
|
||||||
this.element.style.width = (this.elemStart.width - delta.x) + "px";
|
|
||||||
this.element.style.height = (this.elemStart.height - delta.y) + "px";
|
|
||||||
break;
|
break;
|
||||||
case 'ne':
|
case 'ne':
|
||||||
this.element.style.top = (this.elemStart.top + delta.y) + "px";
|
right += delta.x;
|
||||||
this.element.style.width = (this.elemStart.width + delta.x) + "px";
|
top += delta.y;
|
||||||
this.element.style.height = (this.elemStart.height - delta.y) + "px";
|
|
||||||
break;
|
break;
|
||||||
case 'sw':
|
case 'sw':
|
||||||
this.element.style.left = (this.elemStart.left + delta.x) + "px";
|
left += delta.x;
|
||||||
this.element.style.width = (this.elemStart.width - delta.x) + "px";
|
bottom += delta.y;
|
||||||
this.element.style.height = (this.elemStart.height + delta.y) + "px";
|
|
||||||
break;
|
break;
|
||||||
case 'se':
|
case 'se':
|
||||||
this.element.style.width = (this.elemStart.width + delta.x) + "px";
|
right += delta.x;
|
||||||
this.element.style.height = (this.elemStart.height + delta.y) + "px";
|
bottom += delta.y;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
left = floor(left, this.scale.x);
|
||||||
|
top = floor(top, this.scale.y);
|
||||||
|
right = ceil(right, this.scale.x);
|
||||||
|
bottom = ceil(bottom, this.scale.y);
|
||||||
|
|
||||||
|
const width = right - left;
|
||||||
|
const height = bottom - top;
|
||||||
|
|
||||||
|
this.element.style.left = left + "px";
|
||||||
|
this.element.style.top = top + "px";
|
||||||
|
this.element.style.width = width + "px";
|
||||||
|
this.element.style.height = height + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
getMappedPosition() {
|
||||||
|
const rect = getRelativeClientRect(this.element);
|
||||||
|
|
||||||
|
return new DOMRect(
|
||||||
|
Math.floor((rect.x - this.bounding.x) * this.scale.x),
|
||||||
|
Math.floor((rect.y - this.bounding.y) * this.scale.y),
|
||||||
|
Math.floor(rect.width * this.scale.x),
|
||||||
|
Math.floor(rect.height * this.scale.y)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_constructElement(parent) {
|
_constructElement(parent) {
|
||||||
|
|
@ -295,9 +335,13 @@ class Editor {
|
||||||
this.aspectRatio = aspectRatio;
|
this.aspectRatio = aspectRatio;
|
||||||
|
|
||||||
this._imgElement = undefined;
|
this._imgElement = undefined;
|
||||||
|
|
||||||
|
this._img_id = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadImage(url) {
|
loadImage(img_id, url) {
|
||||||
|
this.img_id = img_id;
|
||||||
|
|
||||||
this._imgElement = this._editor.querySelector(".editor-image");
|
this._imgElement = this._editor.querySelector(".editor-image");
|
||||||
this._imgElement.src = url;
|
this._imgElement.src = url;
|
||||||
|
|
||||||
|
|
@ -307,48 +351,283 @@ class Editor {
|
||||||
this._imgElement.style.left = (this._editor.clientWidth - this._imgElement.offsetWidth) / 2 + "px";
|
this._imgElement.style.left = (this._editor.clientWidth - this._imgElement.offsetWidth) / 2 + "px";
|
||||||
this._imgElement.style.top = (this._editor.clientHeight - this._imgElement.offsetHeight) / 2 + "px";
|
this._imgElement.style.top = (this._editor.clientHeight - this._imgElement.offsetHeight) / 2 + "px";
|
||||||
|
|
||||||
this._cutArea = new ResizableBox(this._editor, getRelativeClientRect(this._imgElement), {}, 4/3);
|
this._cutSelector = new ResizableBox(
|
||||||
|
this._editor,
|
||||||
|
getRelativeClientRect(this._imgElement),
|
||||||
|
{ width: this._imgElement.naturalWidth, height: this._imgElement.naturalHeight },
|
||||||
|
4/3);
|
||||||
|
|
||||||
document.querySelector("#theatergf-edit-save").style.visibility = "visible";
|
document.querySelector("#theatergf-edit-save").style.visibility = "visible";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
const region = this._cutSelector.getMappedPosition();
|
||||||
|
|
||||||
const relative_position = {
|
console.log(region)
|
||||||
x: (this._cutSelector.offsetLeft + 1 - this._imgElement.offsetLeft) / this._imgElement.clientWidth,
|
|
||||||
y: (this._cutSelector.offsetTop + 1 - this._imgElement.offsetTop) / this._imgElement.clientHeight,
|
|
||||||
width: (this._cutSelector.clientWidth - 2) / this._imgElement.clientWidth,
|
|
||||||
height: (this._cutSelector.clientHeight - 2) / this._imgElement.clientHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapped_region = {
|
fetch(wpAPISettings.root + 'theatergf/gallery/v1/images/new', {
|
||||||
x: relative_position.x * this._imgElement.naturalWidth,
|
|
||||||
y: relative_position.y * this._imgElement.naturalHeight,
|
|
||||||
width: relative_position.width * this._imgElement.naturalWidth,
|
|
||||||
height: relative_position.height * this._imgElement.naturalHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(mapped_region)
|
|
||||||
|
|
||||||
fetch(wpAPISettings.root + 'theatergf/gallery/v1/crop/new', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-WP-Nonce': wpAPISettings.nonce,
|
'X-WP-Nonce': wpAPISettings.nonce,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
img_id: 53,
|
img_id: this.img_id,
|
||||||
x: mapped_region.x,
|
x: region.x,
|
||||||
y: mapped_region.y,
|
y: region.y,
|
||||||
width: mapped_region.width,
|
width: region.width,
|
||||||
height: mapped_region.height
|
height: region.height
|
||||||
})
|
})
|
||||||
}).then((response) => response.json().then((json) => console.log(json))).catch((error) => console.log(error));
|
}).then((response) => {
|
||||||
|
|
||||||
|
response.json().then((json) => {
|
||||||
|
if (response.ok) {
|
||||||
|
console.log("response:", json)
|
||||||
|
|
||||||
|
createSuccessNotice("Successfully created");
|
||||||
|
} else {
|
||||||
|
createErrorNotice(`Failed to create image: ${json?.error ?? "Unknown Error"}`);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
createErrorNotice(`Failed to parse response: ${error}`);
|
||||||
|
})
|
||||||
|
|
||||||
|
}).catch((error) => {
|
||||||
|
createErrorNotice(`Failed to connect to backend: ${error}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GalleryItemElement extends HTMLElement {
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["src", "selected"];
|
||||||
|
}
|
||||||
|
|
||||||
|
get imageId() { return this.getAttribute("image-id"); }
|
||||||
|
set imageId(value) { this.setAttribute("image-id", value); }
|
||||||
|
|
||||||
|
get src() { return this.getAttribute("src"); }
|
||||||
|
set src(value) { this.setAttribute("src", value); }
|
||||||
|
|
||||||
|
get selected() { return this.getAttribute("selected"); }
|
||||||
|
set selected(value) { this.setAttribute("selected", value); }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const shadow = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.container = document.createElement("div");
|
||||||
|
this.container.classList.add("image-container");
|
||||||
|
|
||||||
|
this.numbering = document.createElement("div");
|
||||||
|
this.numbering.classList.add("numbering");
|
||||||
|
|
||||||
|
// Spinner
|
||||||
|
this.spinner = document.createElement("span");
|
||||||
|
this.spinner.classList.add("spinner", "is-active");
|
||||||
|
|
||||||
|
// Image
|
||||||
|
this.image = document.createElement("img");
|
||||||
|
|
||||||
|
this.image.addEventListener("load", () => {
|
||||||
|
this.spinner.classList.remove("is-active");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.image.addEventListener("error", () => {
|
||||||
|
this.spinner.classList.remove("is-active");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.appendChild(this.spinner);
|
||||||
|
this.container.appendChild(this.numbering);
|
||||||
|
this.container.appendChild(this.image);
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
|
.image-container {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
|
border: black solid 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container.selected {
|
||||||
|
border: blue solid 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numbering {
|
||||||
|
position: absolute;
|
||||||
|
right: -3px;
|
||||||
|
bottom: -3px;
|
||||||
|
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.image-container.selected .numbering {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner:not(.is-active) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
shadow.appendChild(style);
|
||||||
|
shadow.appendChild(this.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.updateImage();
|
||||||
|
this.updateSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (oldValue === newValue) return;
|
||||||
|
|
||||||
|
if (name === "src") {
|
||||||
|
this.updateImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === "selected") {
|
||||||
|
this.updateSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImage() {
|
||||||
|
const src = this.getAttribute("src");
|
||||||
|
|
||||||
|
if (!src) return;
|
||||||
|
|
||||||
|
this.image.src = src;
|
||||||
|
this.spinner.classList.add("is-active");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelected() {
|
||||||
|
if (this.hasAttribute("selected") && this.getAttribute("selected")) {
|
||||||
|
this.container.classList.add("selected");
|
||||||
|
this.numbering.innerHTML = this.getAttribute("selected");
|
||||||
|
} else {
|
||||||
|
this.container.classList.remove("selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ttgf-gallery-item", GalleryItemElement)
|
||||||
|
|
||||||
|
/*function create_gallery_element(image_src) {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
element.classList.add("image-container")
|
||||||
|
|
||||||
|
const spinner = document.createElement("span");
|
||||||
|
spinner.classList.add("spinner", "is-active");
|
||||||
|
element.appendChild(spinner);
|
||||||
|
|
||||||
|
const image_element = new Image();
|
||||||
|
|
||||||
|
image_element.addEventListener('load', (_) => {
|
||||||
|
spinner.classList.remove("is-active")
|
||||||
|
})
|
||||||
|
image_element.addEventListener('error', (_) => {
|
||||||
|
spinner.classList.remove("is-active")
|
||||||
|
})
|
||||||
|
|
||||||
|
image_element.src = image_src
|
||||||
|
element.appendChild(image_element)
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
function create_gallery_element(id, image_src, selected) {
|
||||||
|
const e = document.createElement("ttgf-gallery-item");
|
||||||
|
e.imageId = id;
|
||||||
|
e.src = image_src;
|
||||||
|
e.selected = selected;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load_gallery() {
|
||||||
|
|
||||||
|
const spinner = document.querySelector('.gallery-container .spinner');
|
||||||
|
spinner.classList.add("is-active");
|
||||||
|
|
||||||
|
const gallery = document.querySelector('.gallery');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(wpAPISettings.root + 'theatergf/gallery/v1/images/all', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-WP-Nonce': wpAPISettings.nonce
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const json = await response.json()
|
||||||
|
console.log(json);
|
||||||
|
|
||||||
|
if (!Array.isArray(json)) {
|
||||||
|
throw TypeError(`Did not receive an array of posts as a result! Got ${typeof json}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.length == 0) {
|
||||||
|
gallery.innerHTML = "<p>No images yet</p>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json.forEach((v) => {
|
||||||
|
const item = create_gallery_element(v["ID"], v["thumbnail_src"], v["selected"])
|
||||||
|
item.addEventListener("click", (e) => {
|
||||||
|
selected_items = Array.from(gallery.querySelectorAll("ttgf-gallery-item")).filter((v) => v.selected > 0)
|
||||||
|
if (e.target.selected) {
|
||||||
|
|
||||||
|
selected_items.filter((v) => v.selected > e.target.selected).forEach((v) => v.selected = (Number(v.selected)-1).toString())
|
||||||
|
|
||||||
|
e.target.selected = ""
|
||||||
|
} else {
|
||||||
|
selected_items.forEach((v) => v.selected = (Number(v.selected)+1).toString());
|
||||||
|
e.target.selected = "1"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
gallery.appendChild(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
createErrorNotice(`Failed to load available images: ${e}`)
|
||||||
|
|
||||||
|
gallery.innerHTML = "<p>Error loading images</p>";
|
||||||
|
} finally {
|
||||||
|
spinner.classList.remove("is-active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jQuery(document).ready( ($) => {
|
jQuery(document).ready( ($) => {
|
||||||
let wp_file_selector_frame = null;
|
let wp_file_selector_frame = null;
|
||||||
|
|
@ -376,7 +655,7 @@ jQuery(document).ready( ($) => {
|
||||||
|
|
||||||
console.log(file)
|
console.log(file)
|
||||||
|
|
||||||
editor.loadImage(file.url);
|
editor.loadImage(file.id, file.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
wp_file_selector_frame.open();
|
wp_file_selector_frame.open();
|
||||||
|
|
@ -386,5 +665,48 @@ jQuery(document).ready( ($) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
editor.save();
|
editor.save();
|
||||||
|
|
||||||
|
load_gallery();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$("#theatergf-save-selected").on('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
console.log("click");
|
||||||
|
|
||||||
|
const gallery = document.querySelector('.gallery');
|
||||||
|
|
||||||
|
const items = gallery.querySelectorAll("ttgf-gallery-item");
|
||||||
|
const payload = Array.from(items, (v) => ({ ID: v.imageId, selected: v.selected }))
|
||||||
|
|
||||||
|
fetch(wpAPISettings.root + 'theatergf/gallery/v1/images/selected/set', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-WP-Nonce': wpAPISettings.nonce,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
imgs: payload
|
||||||
|
})
|
||||||
|
}).then((response) => {
|
||||||
|
|
||||||
|
response.json().then((json) => {
|
||||||
|
if (response.ok) {
|
||||||
|
console.log("response:", json)
|
||||||
|
|
||||||
|
createSuccessNotice("Saved.");
|
||||||
|
} else {
|
||||||
|
createErrorNotice(`Failed to save selected image: ${json?.code ?? "Unknown Error"}`);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
createErrorNotice(`Failed to parse response: ${error}`);
|
||||||
|
})
|
||||||
|
|
||||||
|
}).catch((error) => {
|
||||||
|
createErrorNotice(`Failed to connect to backend: ${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
load_gallery();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,90 @@
|
||||||
|
|
||||||
|
#theatergf-editor-status {
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.theatergf {
|
.theatergf {
|
||||||
|
|
||||||
|
.gallery-container {
|
||||||
|
width: 75%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.gallery {
|
||||||
|
border: black 1px solid;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-content: top;
|
||||||
|
justify-content: left;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
border: black solid 1px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border: blue solid 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numbering {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
.spinner:not(.is-active) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-container {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.spinner {
|
||||||
|
display: block;
|
||||||
|
float: none;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.image-editor {
|
.image-editor {
|
||||||
aspect-ratio: 2 / 1;
|
aspect-ratio: 2 / 1;
|
||||||
width: 75%;
|
width: 75%;
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,40 @@
|
||||||
|
|
||||||
namespace TheaterGF\Gallery\Backend;
|
namespace TheaterGF\Gallery\Backend;
|
||||||
|
|
||||||
require_once __DIR__ . '/util.php';
|
|
||||||
|
|
||||||
require_once __DIR__ . '/endpoints/crop.php';
|
require_once __DIR__ . '/endpoints/crop.php';
|
||||||
|
|
||||||
add_action( 'rest_api_init', function () {
|
add_action( 'rest_api_init', function () {
|
||||||
$namespace = 'theatergf/gallery/v1';
|
$namespace = 'theatergf/gallery/v1';
|
||||||
|
|
||||||
$crop_controller = new Rest\CROP_Endpoints($namespace, 'crop');
|
$crop_controller = new Rest\CROP_Endpoints($namespace, 'images');
|
||||||
$crop_controller->register_routes();
|
$crop_controller->register_routes();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_action( 'delete_attachment', function ( $post_id ) {
|
||||||
|
$selected = get_post_meta($post_id, '_ttgf_gallery_selected', true);
|
||||||
|
|
||||||
|
error_log("deleting: " . $post_id . ", selected: " . $selected);
|
||||||
|
|
||||||
|
if ( (! $selected) || empty($selected) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$posts = get_posts(array(
|
||||||
|
'numberposts' => -1,
|
||||||
|
'post_type' => "attachment",
|
||||||
|
'meta_key' => "_ttgf_gallery_selected",
|
||||||
|
'meta_value' => (int)$selected,
|
||||||
|
'meta_type' => 'NUMERIC',
|
||||||
|
'meta_compare' => '>'
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ( $posts as $post ) {
|
||||||
|
$s = get_post_meta($post->ID, '_ttgf_gallery_selected', true);
|
||||||
|
|
||||||
|
if ( (! $selected) || empty($selected) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_post_meta($post->ID, '_ttgf_gallery_selected', ((int)$s - 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,15 @@
|
||||||
|
|
||||||
namespace TheaterGF\Gallery\Backend\Rest;
|
namespace TheaterGF\Gallery\Backend\Rest;
|
||||||
|
|
||||||
class CROP_Endpoints extends \WP_REST_Controller {
|
require_once ABSPATH . "wp-content/plugins/theatergf-core/src/media.php";
|
||||||
|
|
||||||
|
use \WP_Error;
|
||||||
|
use \WP_REST_Controller;
|
||||||
|
use \WP_REST_Server;
|
||||||
|
|
||||||
|
use function \TheaterGF\Core\get_media_manager;
|
||||||
|
|
||||||
|
class CROP_Endpoints extends WP_REST_Controller {
|
||||||
|
|
||||||
public function __construct( $namespace, $base_path ) {
|
public function __construct( $namespace, $base_path ) {
|
||||||
$this->namespace = $namespace;
|
$this->namespace = $namespace;
|
||||||
|
|
@ -11,50 +19,183 @@ class CROP_Endpoints extends \WP_REST_Controller {
|
||||||
|
|
||||||
public function register_routes() {
|
public function register_routes() {
|
||||||
|
|
||||||
|
register_rest_route($this->namespace, '/' . $this->rest_base . '/all', [
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => [ $this, 'get_items' ],
|
||||||
|
'permission_callback' => [ $this, 'get_items_permissions_check'],
|
||||||
|
'args' => []
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route($this->namespace, '/' . $this->rest_base . '/selected/set', [
|
||||||
|
'methods' => WP_REST_Server::CREATABLE,
|
||||||
|
'callback' => [ $this, 'set_selected' ],
|
||||||
|
'permission_callback' => [ $this, 'create_item_permissions_check' ],
|
||||||
|
'args' => [
|
||||||
|
'imgs' => [
|
||||||
|
'required' => true,
|
||||||
|
'validate_callback' => function ( $param, $request, $key) {
|
||||||
|
if ( ! is_array($param) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ( $param as $img ) {
|
||||||
|
if ( ! (wp_attachment_is_image($img['ID']) && (is_numeric($img['selected']) || empty($img['selected']))) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
register_rest_route($this->namespace, '/' . $this->rest_base . '/new', [
|
register_rest_route($this->namespace, '/' . $this->rest_base . '/new', [
|
||||||
'methods' => \WP_REST_Server::CREATABLE,
|
'methods' => WP_REST_Server::CREATABLE,
|
||||||
'callback' => [ $this, 'create_item' ],
|
'callback' => [ $this, 'create_item' ],
|
||||||
'permission_callback' => [ $this, 'create_item_permissions_check' ],
|
'permission_callback' => [ $this, 'create_item_permissions_check' ],
|
||||||
'args' => [
|
'args' => [
|
||||||
'img_id' => [
|
'img_id' => [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'validate_callback' => function ( $param, $request, $key) { return wp_attachment_is_image($param); }
|
'validate_callback' => function ( $param, $request, $key) { return wp_attachment_is_image($param); }
|
||||||
],
|
],
|
||||||
'x' => [
|
'x' => [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param >= 0; }
|
||||||
],
|
],
|
||||||
'y' => [
|
'y' => [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param >= 0; }
|
||||||
],
|
],
|
||||||
'width' => [
|
'width' => [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
||||||
],
|
],
|
||||||
'height' => [
|
'height' => [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
'validate_callback' => function ( $param, $request, $key) { return is_numeric($param) && $param > 0; }
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_items_permissions_check( $request ) {
|
||||||
|
return $this->editable_permission_check($request);
|
||||||
|
}
|
||||||
|
|
||||||
public function create_item_permissions_check( $request ) {
|
public function create_item_permissions_check( $request ) {
|
||||||
|
return $this->editable_permission_check($request);
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! is_user_logged_in()) {
|
|
||||||
return new \WP_Error( 'unauthenticated', 'Log in to interact with this endpoint.', array( 'status' => 401 ));
|
public function get_items( $request ) {
|
||||||
|
|
||||||
|
$posts = get_posts(array(
|
||||||
|
'numberposts' => -1,
|
||||||
|
'post_type' => "attachment",
|
||||||
|
'meta_key' => "_ttgf_is_gallery_crop",
|
||||||
|
'meta_value' => true
|
||||||
|
));
|
||||||
|
|
||||||
|
$return_data = [];
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$thumbnail_src = wp_get_attachment_image_src($post->ID, "thumbnail");
|
||||||
|
if ( is_wp_error($thumbnail_src) ) {
|
||||||
|
error_log($thumbnail_src->get_error_message());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$image_src = wp_get_attachment_image_src($post->ID, "full");
|
||||||
|
if ( is_wp_error($image_src) ) {
|
||||||
|
error_log($image_src->get_error_message());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$selected = get_post_meta($post->ID, '_ttgf_gallery_selected', true);
|
||||||
|
|
||||||
|
$return_data[] = [
|
||||||
|
"post" => $post,
|
||||||
|
"ID" => $post->ID,
|
||||||
|
"thumbnail_src" => $thumbnail_src[0],
|
||||||
|
"image_src" => $image_src[0],
|
||||||
|
"selected" => $selected
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! (is_user_logged_in() && current_user_can( 'edit_pages' )) ) {
|
return $return_data;
|
||||||
return new \WP_Error( 'forbidden', 'No Permission for this endpoint.', array( 'status' => 403 ));
|
}
|
||||||
|
|
||||||
|
public function set_selected( $request ) {
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
|
||||||
|
foreach ( $params["imgs"] as $img ) {
|
||||||
|
update_post_meta($img["ID"], '_ttgf_gallery_selected', $img["selected"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create_item( $request ) {
|
public function create_item( $request ) {
|
||||||
return true;
|
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
|
||||||
|
$image_name = basename(wp_get_original_image_path($params['img_id']));
|
||||||
|
|
||||||
|
// load wp_crop_image
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||||
|
|
||||||
|
$cropped = wp_crop_image(
|
||||||
|
$params["img_id"], // id of base image
|
||||||
|
$params["x"], // coordinates
|
||||||
|
$params["y"],
|
||||||
|
$params["width"], // size of rectangle in source image
|
||||||
|
$params["height"],
|
||||||
|
$params["width"], // size of destination image (resized)
|
||||||
|
$params["height"],
|
||||||
|
false, // coordinates are relative to image
|
||||||
|
\TheaterGF\Gallery\get_uploads_dir() . "/cropped-" . time() . "-" . $image_name
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_wp_error($cropped) ) {
|
||||||
|
return $cropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachment_data = [
|
||||||
|
'guid' => \TheaterGF\Gallery\generate_guid($cropped),
|
||||||
|
'post_mime_type' => wp_get_image_mime($cropped),
|
||||||
|
'post_title' => basename($cropped),
|
||||||
|
/*// optional
|
||||||
|
'post_excerpt' => $caption,
|
||||||
|
'post_content' => $description*/
|
||||||
|
];
|
||||||
|
$new_attachment_id = wp_insert_attachment($attachment_data, $cropped, 0, true);
|
||||||
|
|
||||||
|
if ( is_wp_error($new_attachment_id) ) {
|
||||||
|
return $new_attachment_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = wp_generate_attachment_metadata($new_attachment_id, $cropped);
|
||||||
|
wp_update_attachment_metadata($new_attachment_id, $metadata);
|
||||||
|
|
||||||
|
update_post_meta($new_attachment_id, '_ttgf_is_gallery_crop', true);
|
||||||
|
update_post_meta($new_attachment_id, '_ttgf_original_img_id', $params["img_id"]);
|
||||||
|
|
||||||
|
$tags = get_media_manager()->tags->get_from_post($params["img_id"]);
|
||||||
|
|
||||||
|
get_media_manager()->post_add_tags($new_attachment_id, $tags);
|
||||||
|
get_media_manager()->post_mark_generated($new_attachment_id);
|
||||||
|
|
||||||
|
return [ "params" => $params, "image" => get_post_meta($new_attachment_id) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function editable_permission_check( $request ) {
|
||||||
|
|
||||||
|
if ( ! is_user_logged_in()) {
|
||||||
|
return new WP_Error( 'unauthenticated', 'Log in to interact with this endpoint.', array( 'status' => 401 ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! (is_user_logged_in() && current_user_can( 'edit_pages' )) ) {
|
||||||
|
return new WP_Error( 'forbidden', 'No Permission for this endpoint.', array( 'status' => 403 ));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TheaterGF\Gallery\Backend\Rest;
|
||||||
|
|
||||||
|
use \WP_Error;
|
||||||
|
use \WP_REST_Controller;
|
||||||
|
use \WP_REST_Server;
|
||||||
|
|
||||||
|
class GALLERY_Endpoints extends WP_REST_Controller {
|
||||||
|
|
||||||
|
public function __construct( $namespace, $base_path ) {
|
||||||
|
$this->namespace = $namespace;
|
||||||
|
$this->rest_base = $base_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route($this->namespace, '/' . $this->rest_base, [
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => [ $this, 'get_items' ],
|
||||||
|
'permission_callback' => [ $this, 'get_items_permissions_check'],
|
||||||
|
'args' => []
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_items_permissions_check( $request ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$posts = get_posts(array(
|
||||||
|
'numberposts' => -1,
|
||||||
|
'post_type' => "attachment",
|
||||||
|
'meta_key' => "_ttgf_gallery_selected",
|
||||||
|
));
|
||||||
|
|
||||||
|
$data = []
|
||||||
|
|
||||||
|
foreach ( $posts as $post ) {
|
||||||
|
$data[] = wp_get_attachment_image_src($post->ID, "full");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TheaterGF\Gallery\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether all required fields are present. Does not support duplicate keys.
|
||||||
|
*
|
||||||
|
* @return if a field is missing, its key, otherwise null
|
||||||
|
*/
|
||||||
|
function check_required_fields(WP_REST_Request $request, iterable $fields): string|null {
|
||||||
|
|
||||||
|
foreach ($fields as $key) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schemas.wp.org/trunk/block.json",
|
"$schema": "https://schemas.wp.org/trunk/block.json",
|
||||||
"apiVersion": 3,
|
"apiVersion": 3,
|
||||||
"name": "theatergf/theatergf-gallery",
|
"name": "theatergf/gallery",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"title": "Gallery",
|
"title": "Gallery",
|
||||||
"category": "media",
|
"category": "media",
|
||||||
|
"icon": "dashicons-images",
|
||||||
"example": {},
|
"example": {},
|
||||||
"supports": {
|
"supports": {
|
||||||
"html": false
|
"html": false
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ import './editor.scss';
|
||||||
*/
|
*/
|
||||||
export default function Edit() {
|
export default function Edit() {
|
||||||
return (
|
return (
|
||||||
<p { ...useBlockProps() }>
|
<theatergf-gallery { ...useBlockProps() }>
|
||||||
{ __( 'Gallery – hello from the editor!', 'theatergf-gallery' ) }
|
{ __( 'Gallery', 'theatergf-gallery' ) }
|
||||||
</p>
|
</theatergf-gallery>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,11 @@
|
||||||
*
|
*
|
||||||
* @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
|
* @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$images = rest_do_request(new WP_REST_Request( 'GET', '/theatergf/gallery/v1/gallery'));
|
||||||
?>
|
?>
|
||||||
<p <?php echo get_block_wrapper_attributes(); ?>>
|
<theatergf-gallery <?php echo get_block_wrapper_attributes(); ?>>
|
||||||
<?php esc_html_e( 'Gallery – hello from a dynamic block!', 'theatergf-gallery' ); ?>
|
<?php foreach ( $images->data as $img_src ): ?>
|
||||||
</p>
|
<img src="<?= $img_src ?>">
|
||||||
|
<?php endforeach ?>
|
||||||
|
</theatergf-gallery>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.wp-block-theatergf-theatergf-gallery {
|
.wp-block-theatergf-theatergf-gallery {
|
||||||
background-color: #21759b;
|
|
||||||
color: #fff;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,251 @@
|
||||||
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
|
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
class RoundRobinCounter {
|
||||||
console.log( 'Hello World! (from theatergf-theatergf-gallery block)' );
|
|
||||||
/* eslint-enable no-console */
|
get count() { return this._count; }
|
||||||
|
set count(value) { this._count = value; }
|
||||||
|
|
||||||
|
get min() { return this._min; }
|
||||||
|
set min(value) {
|
||||||
|
if (this.count < value) {
|
||||||
|
this.count = this.max;
|
||||||
|
}
|
||||||
|
this._min = value;
|
||||||
|
}
|
||||||
|
get max() { return this._max; }
|
||||||
|
set max(value) {
|
||||||
|
if (this.count > value) {
|
||||||
|
this.count = this.min;
|
||||||
|
}
|
||||||
|
this._max = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(min = 0, max = 0, value = 0) {
|
||||||
|
this._min = min;
|
||||||
|
this._max = max;
|
||||||
|
|
||||||
|
this._count = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
if (this.count == this.max) {
|
||||||
|
this.count = this.min
|
||||||
|
} else {
|
||||||
|
this.count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decrement() {
|
||||||
|
if (this.count == this.min) {
|
||||||
|
this.count = this.max
|
||||||
|
} else {
|
||||||
|
this.count -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TheaterGFGalleryHTMLElement extends HTMLElement {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.current_slide = new RoundRobinCounter();
|
||||||
|
|
||||||
|
this.observer = new MutationObserver(() => this.update_gallery());
|
||||||
|
|
||||||
|
this.shadow = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
|
this.container = document.createElement("div");
|
||||||
|
this.container.classList.add("container");
|
||||||
|
|
||||||
|
this.slide_container = document.createElement("div");
|
||||||
|
this.slide_container.classList.add("slide-container");
|
||||||
|
|
||||||
|
this.button_l = document.createElement("button");
|
||||||
|
this.button_l.classList.add("slide-button", "left");
|
||||||
|
this.button_l.innerHTML = `
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor">
|
||||||
|
<path d="M13 5l-5 5 5 5-1 2-7-7 7-7z"/>
|
||||||
|
</svg>`;
|
||||||
|
this.button_l.addEventListener("click", () => { this.slide_left(); });
|
||||||
|
|
||||||
|
this.button_r = document.createElement("button");
|
||||||
|
this.button_r.classList.add("slide-button", "right");
|
||||||
|
this.button_r.innerHTML = `
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor">
|
||||||
|
<path d="M7 5l5 5-5 5 1 2 7-7-7-7z"/>
|
||||||
|
</svg>`;
|
||||||
|
this.button_r.addEventListener("click", () => { this.slide_right(); });
|
||||||
|
|
||||||
|
this.slide_control = document.createElement("div");
|
||||||
|
this.slide_control.classList.add("slide-control");
|
||||||
|
|
||||||
|
this.container.appendChild(this.slide_container);
|
||||||
|
this.container.appendChild(this.button_l);
|
||||||
|
this.container.appendChild(this.button_r);
|
||||||
|
this.container.appendChild(this.slide_control);
|
||||||
|
|
||||||
|
this.update_gallery()
|
||||||
|
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: var(--wp--style--global--content-size);
|
||||||
|
aspect-ratio: 2/1;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-container {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide {
|
||||||
|
height: 100%;
|
||||||
|
width: var(--wp--style--global--content-size);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-button {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
|
||||||
|
background: rgba(255, 255, 255, 0.75);
|
||||||
|
border: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.slide-button.left {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
.slide-button.right {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
.slide-button svg {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-control {
|
||||||
|
height: 20px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
background-color: var(--wp--preset--color--base);
|
||||||
|
background: black;
|
||||||
|
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: var(--wp--preset--color--highlight-dark);
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.dot.selected {
|
||||||
|
background-color: var(--wp--preset--color--highlight-light);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.shadow.appendChild(style);
|
||||||
|
this.shadow.appendChild(this.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.observer.observe(this, { childList: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.observer.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
update_gallery() {
|
||||||
|
const children = [...this.children];
|
||||||
|
|
||||||
|
this.slide_container.replaceChildren();
|
||||||
|
|
||||||
|
children.forEach((img, idx) => {
|
||||||
|
const slide = img.cloneNode();
|
||||||
|
slide.classList.add("slide");
|
||||||
|
|
||||||
|
this.slide_container.appendChild(slide);
|
||||||
|
this.slide_control.appendChild(this.__create_control_dot(idx));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.current_slide.max = children.length - 1;
|
||||||
|
|
||||||
|
this.__got_to_current_slide();
|
||||||
|
}
|
||||||
|
|
||||||
|
slide_left() {
|
||||||
|
this.current_slide.decrement();
|
||||||
|
this.__got_to_current_slide();
|
||||||
|
}
|
||||||
|
slide_right() {
|
||||||
|
this.current_slide.increment();
|
||||||
|
this.__got_to_current_slide();
|
||||||
|
}
|
||||||
|
|
||||||
|
__got_to_current_slide() {
|
||||||
|
const target_position = this.offsetWidth * this.current_slide.count;
|
||||||
|
|
||||||
|
this.slide_container.animate([
|
||||||
|
{ transform: `translateX(-${target_position}px)`}
|
||||||
|
], {
|
||||||
|
duration: 300,
|
||||||
|
fill: "forwards"
|
||||||
|
}).addEventListener("finish", () => {
|
||||||
|
this.slide_control.querySelector(".selected")?.classList?.remove("selected");
|
||||||
|
this.slide_control.querySelector(`[slide='${this.current_slide.count}']`).classList.add("selected");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
__create_control_dot(slide_idx) {
|
||||||
|
const div = document.createElement("button");
|
||||||
|
div.classList.add("dot");
|
||||||
|
div.setAttribute("slide", slide_idx);
|
||||||
|
div.addEventListener("click", () => {
|
||||||
|
this.current_slide.count = slide_idx;
|
||||||
|
this.__got_to_current_slide();
|
||||||
|
})
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("theatergf-gallery", TheaterGFGalleryHTMLElement);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,16 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
exit; // Exit if accessed directly.
|
exit; // Exit if accessed directly.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_uploads_dir() {
|
||||||
|
return wp_upload_dir()["basedir"] . "/theatergf/gallery";
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_guid( $filename ) {
|
||||||
|
$upload_dirs = wp_upload_dir();
|
||||||
|
|
||||||
|
return $upload_dirs["baseurl"] . '/' . str_replace($upload_dirs["basedir"] . '/', "", $filename);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the block(s) metadata from the `blocks-manifest.php` and registers the block type(s)
|
* Registers the block(s) metadata from the `blocks-manifest.php` and registers the block type(s)
|
||||||
* based on the registered block metadata. Behind the scenes, it registers also all assets so they can be enqueued
|
* based on the registered block metadata. Behind the scenes, it registers also all assets so they can be enqueued
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue