Added theme archvier and changes to blocks

This commit is contained in:
Jeremy Rangel
2025-01-01 18:09:33 -08:00
parent 21b00e1937
commit d928a0e8fd
21 changed files with 342 additions and 158 deletions

View File

@ -401,3 +401,17 @@ function mytheme_uninstall_icon_set() {
}
add_action('wp_ajax_uninstall_icon_set', 'mytheme_uninstall_icon_set');
/* Add Styles */
function lcp_header_height_style() {
// Fetch the lcp_header_height value from wp_options.
$header_height = get_option('lcp_header_height', 0); // Default to 0 if not found
// Ensure we have a valid value
if ($header_height) {
// Output the inline style tag with the CSS variable for header height
echo "<style>:root { --lcp--header--height: {$header_height}px; }</style>";
}
}
add_action('wp_head', 'lcp_header_height_style');

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n'), 'version' => '92128317eaa465727f6e');
<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n'), 'version' => '5d694dedd074dafba412');

View File

@ -1 +1 @@
(()=>{"use strict";var e,t={717:()=>{const e=window.wp.blocks,t=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,r=window.React,o=window.ReactJSXRuntime,s=JSON.parse('{"UU":"lcp/header-container"}');(0,e.registerBlockType)(s.UU,{edit:function({attributes:e,setAttributes:s}){const[i,a]=(0,r.useState)(!1),[d,p]=(0,r.useState)("10px"),[c,h]=(0,r.useState)("10px"),[x,g]=(0,r.useState)("10px"),[u,v]=(0,r.useState)("10px"),{sticky:f}=e,m=(0,l.useBlockProps)();return(0,o.jsxs)("div",{...m,children:[(0,o.jsxs)(l.InspectorControls,{children:[(0,o.jsx)(n.SelectControl,{label:(0,t.__)("Sticky Behavior","lcp"),value:f,options:[{label:(0,t.__)("Never","lcp"),value:"never"},{label:(0,t.__)("On Scroll","lcp"),value:"onScroll"},{label:(0,t.__)("Always","lcp"),value:"always"}],onChange:e=>{s({sticky:e})}}),(0,o.jsxs)(n.BaseControl,{label:"Padding - Desktop",children:[(0,o.jsxs)("div",{style:{display:"flex",flexDirection:"row"},children:[(0,o.jsx)("span",{style:{marginRight:"10px"},children:"Padding"}),(0,o.jsx)(n.ToggleControl,{label:"Use Independent Padding",checked:i,onChange:()=>{a(!i)}})]}),i?(0,o.jsxs)("div",{style:{display:"grid",padding:"10px",gridTemplateColumns:"1fr 1fr",gap:"10px",justifyItems:"center"},children:[(0,o.jsxs)("fieldset",{style:{gridColumn:"span 2",width:"116px"},children:[(0,o.jsx)("legend",{children:"Top"}),(0,o.jsx)(n.__experimentalUnitControl,{value:d,onChange:e=>{p(e),s({paddingTop:e})}})]}),(0,o.jsxs)("fieldset",{children:[(0,o.jsx)("legend",{children:"Left"}),(0,o.jsx)(n.__experimentalUnitControl,{value:u,onChange:e=>{v(e),s({paddingLeft:e})}})]}),(0,o.jsxs)("fieldset",{children:[(0,o.jsx)("legend",{children:"Right"}),(0,o.jsx)(n.__experimentalUnitControl,{value:c,onChange:e=>{h(e),s({paddingRight:e})}})]}),(0,o.jsxs)("fieldset",{style:{gridColumn:"span 2",width:"116px"},children:[(0,o.jsx)("legend",{children:"Bottom"}),(0,o.jsx)(n.__experimentalUnitControl,{value:x,onChange:e=>{g(e),s({paddingBottom:e})}})]})]}):(0,o.jsx)(n.__experimentalUnitControl,{label:"Padding Value",value:999,onChange:e=>{s({padding:{extraLarge:{top:e,right:e,bottom:e,left:e},large:{top:e,right:e,bottom:e,left:e},medium:{top:e,right:e,bottom:e,left:e},small:{top:e,right:e,bottom:e,left:e}}})}})]})]}),(0,o.jsx)(l.InnerBlocks,{template:[["core/template-part",{slug:"header"}]]})]})},save:function({attributes:e}){const{sticky:t}=e,n=l.useBlockProps.save();let r="";"onScroll"===t?r="lcp-sticky-on-scroll":"always"===t&&(r="lcp-sticky");const s=`${n.className} ${r}`;return(0,o.jsx)("div",{...n,className:s,id:"lcp-header-container",children:(0,o.jsx)(l.InnerBlocks.Content,{})})}})}},l={};function n(e){var r=l[e];if(void 0!==r)return r.exports;var o=l[e]={exports:{}};return t[e](o,o.exports,n),o.exports}n.m=t,e=[],n.O=(t,l,r,o)=>{if(!l){var s=1/0;for(p=0;p<e.length;p++){l=e[p][0],r=e[p][1],o=e[p][2];for(var i=!0,a=0;a<l.length;a++)(!1&o||s>=o)&&Object.keys(n.O).every((e=>n.O[e](l[a])))?l.splice(a--,1):(i=!1,o<s&&(s=o));if(i){e.splice(p--,1);var d=r();void 0!==d&&(t=d)}}return t}o=o||0;for(var p=e.length;p>0&&e[p-1][2]>o;p--)e[p]=e[p-1];e[p]=[l,r,o]},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};n.O.j=t=>0===e[t];var t=(t,l)=>{var r,o,s=l[0],i=l[1],a=l[2],d=0;if(s.some((t=>0!==e[t]))){for(r in i)n.o(i,r)&&(n.m[r]=i[r]);if(a)var p=a(n)}for(t&&t(l);d<s.length;d++)o=s[d],n.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return n.O(p)},l=self.webpackChunklcp_viewport=self.webpackChunklcp_viewport||[];l.forEach(t.bind(null,0)),l.push=t.bind(null,l.push.bind(l))})();var r=n.O(void 0,[350],(()=>n(717)));r=n.O(r)})();
(()=>{"use strict";var e,n={717:()=>{const e=window.wp.blocks,n=window.React,t=window.wp.i18n,r=window.wp.blockEditor,o=window.wp.components,s=window.ReactJSXRuntime,c=JSON.parse('{"UU":"lcp/header-container"}');(0,e.registerBlockType)(c.UU,{edit:function({attributes:e,setAttributes:c}){const[l,i]=(0,n.useState)(!1),{sticky:a}=e,p=(0,n.useRef)(null),h=(0,r.useBlockProps)({ref:p});return(0,n.useEffect)((()=>{const e=p.current;if(!e)return;const n=new ResizeObserver((()=>{const n=e.getBoundingClientRect().height;fetch("/wp-json/lcp/v1/set-header-height",{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":wpApiSettings.nonce},body:JSON.stringify({height:n})}).then((e=>e.json())).then((e=>{console.log("Height saved:",e)})).catch((e=>{console.error("Error saving height:",e)}))}));return n.observe(e),()=>n.disconnect()}),[]),(0,s.jsxs)("div",{...h,children:[(0,s.jsx)(r.InspectorControls,{children:(0,s.jsx)(o.SelectControl,{label:(0,t.__)("Sticky Behavior","lcp"),value:a,options:[{label:(0,t.__)("Never","lcp"),value:"never"},{label:(0,t.__)("On Scroll","lcp"),value:"onScroll"},{label:(0,t.__)("Always","lcp"),value:"always"}],onChange:e=>c({sticky:e})})}),(0,s.jsx)("div",{id:"lcp-header-container",children:(0,s.jsx)(r.InnerBlocks,{})})]})},save:function({attributes:e}){const{sticky:n}=e,t=r.useBlockProps.save();let o="";"onScroll"===n?o="lcp-sticky-on-scroll":"always"===n&&(o="lcp-sticky");const c=`${t.className} ${o}`;return(0,s.jsx)("div",{...t,className:c,id:"lcp-header-container",children:(0,s.jsx)(r.InnerBlocks.Content,{})})}})}},t={};function r(e){var o=t[e];if(void 0!==o)return o.exports;var s=t[e]={exports:{}};return n[e](s,s.exports,r),s.exports}r.m=n,e=[],r.O=(n,t,o,s)=>{if(!t){var c=1/0;for(p=0;p<e.length;p++){t=e[p][0],o=e[p][1],s=e[p][2];for(var l=!0,i=0;i<t.length;i++)(!1&s||c>=s)&&Object.keys(r.O).every((e=>r.O[e](t[i])))?t.splice(i--,1):(l=!1,s<c&&(c=s));if(l){e.splice(p--,1);var a=o();void 0!==a&&(n=a)}}return n}s=s||0;for(var p=e.length;p>0&&e[p-1][2]>s;p--)e[p]=e[p-1];e[p]=[t,o,s]},r.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={57:0,350:0};r.O.j=n=>0===e[n];var n=(n,t)=>{var o,s,c=t[0],l=t[1],i=t[2],a=0;if(c.some((n=>0!==e[n]))){for(o in l)r.o(l,o)&&(r.m[o]=l[o]);if(i)var p=i(r)}for(n&&n(t);a<c.length;a++)s=c[a],r.o(e,s)&&e[s]&&e[s][0](),e[s]=0;return r.O(p)},t=self.webpackChunklcp_viewport=self.webpackChunklcp_viewport||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})();var o=r.O(void 0,[350],(()=>r(717)));o=r.O(o)})();

View File

@ -1 +1 @@
<?php return array('dependencies' => array(), 'version' => 'd4e4a494008d04e1eb42');
<?php return array('dependencies' => array(), 'version' => '187056d57d17d681fe72');

View File

@ -1 +1 @@
console.log("Hello World! (from create-block-lcp-viewport block)");
document.addEventListener("DOMContentLoaded",(function(){var e=document.getElementById("lcp-header-container");if(e){var t=e.offsetHeight;getComputedStyle(document.documentElement).getPropertyValue("--lcp--header--height").trim()!==t+"px"&&document.documentElement.style.setProperty("--lcp--header--height",t+"px")}}));

View File

@ -31,3 +31,33 @@ function lcp_header_container_block_init() {
);
}
add_action( 'init', 'lcp_header_container_block_init' );
/* REST API TO UPDATE OPTIONS */
// Register custom REST API endpoint
function lcp_register_rest_route() {
register_rest_route('lcp/v1', '/set-header-height', array(
'methods' => 'POST',
'callback' => 'lcp_set_header_height',
'permission_callback' => function () {
return current_user_can('manage_options'); // Ensure the user has permission
},
));
}
add_action('rest_api_init', 'lcp_register_rest_route');
// Callback to save the height to wp_options
function lcp_set_header_height(WP_REST_Request $request) {
$height = $request->get_param('height');
if (is_numeric($height)) {
update_option('lcp_header_height', $height);
return new WP_REST_Response(array('message' => 'Height saved successfully'), 200);
}
return new WP_REST_Response(array('message' => 'Invalid height value'), 400);
}

View File

@ -1,73 +1,61 @@
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
import { useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import { useBlockProps, InnerBlocks, InspectorControls } from '@wordpress/block-editor';
import { SelectControl, BaseControl, ToggleControl,__experimentalUnitControl as UnitControl } from '@wordpress/components';
import { useState, useEffect } from 'react';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* Those files can contain any CSS code that gets applied to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import { SelectControl, BaseControl, ToggleControl, __experimentalUnitControl as UnitControl } from '@wordpress/components';
import './editor.scss';
/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {Element} Element to render.
*/
export default function Edit({ attributes, setAttributes }) {
const [isIndependentLarge, setIsIndependentLarge] = useState(false);
const [paddingTop, setPaddingTop] = useState('10px');
const [paddingRight, setPaddingRight] = useState('10px');
const [paddingBottom, setPaddingBottom] = useState('10px');
const [paddingLeft, setPaddingLeft] = useState('10px');
const { sticky } = attributes;
const blockRef = useRef(null); // Ref to the block container element
// Block props for the outer block
const blockProps = useBlockProps();
const blockProps = useBlockProps({
ref: blockRef,
});
// Handle the change of the sticky attribute
const handleStickyChange = (value) => {
setAttributes({ sticky: value });
};
const handleToggleChange = () => {
setIsIndependentLarge(!isIndependentLarge);
};
// Use ResizeObserver to monitor height changes
useEffect(() => {
const blockElement = blockRef.current;
if (!blockElement) return;
const updatePaddingForAllSizes = (newValue) => {
const newPadding = {
extraLarge: { top: newValue, right: newValue, bottom: newValue, left: newValue },
large: { top: newValue, right: newValue, bottom: newValue, left: newValue },
medium: { top: newValue, right: newValue, bottom: newValue, left: newValue },
small: { top: newValue, right: newValue, bottom: newValue, left: newValue }
};
// ResizeObserver to monitor changes in the block's size
const resizeObserver = new ResizeObserver(() => {
// Get the height using getBoundingClientRect (this avoids issues with offsets)
const rect = blockElement.getBoundingClientRect();
const height = rect.height; // Use the height property from getBoundingClientRect()
// Update the padding attribute with the new padding object
setAttributes({ padding: newPadding });
};
// Send the height to a custom REST API endpoint
fetch('/wp-json/lcp/v1/set-header-height', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce, // Ensure you have the nonce for security
},
body: JSON.stringify({
height,
}),
})
.then((response) => response.json())
.then((data) => {
console.log('Height saved:', data);
})
.catch((error) => {
console.error('Error saving height:', error);
});
});
// Observe the block element for resize changes
resizeObserver.observe(blockElement);
// Cleanup the observer when the component unmounts
return () => resizeObserver.disconnect();
}, []);
return (
<div {...blockProps}>
{/* Inspector Controls: Add a SelectControl to change the sticky attribute */}
{/* Inspector Controls */}
<InspectorControls>
<SelectControl
label={__('Sticky Behavior', 'lcp')}
@ -77,80 +65,14 @@ export default function Edit({ attributes, setAttributes }) {
{ label: __('On Scroll', 'lcp'), value: 'onScroll' },
{ label: __('Always', 'lcp'), value: 'always' },
]}
onChange={handleStickyChange}
onChange={(value) => setAttributes({ sticky: value })}
/>
<BaseControl label="Padding - Desktop">
<div style={{ display: 'flex', flexDirection: 'row' }}>
<span style={{ marginRight: '10px' }}>Padding</span>
<ToggleControl
label="Use Independent Padding"
checked={isIndependentLarge}
onChange={handleToggleChange}
/>
</div>
{/* Padding controls */}
{isIndependentLarge ? (
<div style={{ display: 'grid', padding: '10px', gridTemplateColumns: '1fr 1fr', gap: '10px', justifyItems: 'center' }}>
{/* Padding controls for top, left, right, bottom */}
<fieldset style={{ gridColumn: 'span 2', width: '116px' }}>
<legend>Top</legend>
<UnitControl
value={paddingTop}
onChange={(newValue) => {
setPaddingTop(newValue);
setAttributes({ paddingTop: newValue });
}}
/>
</fieldset>
<fieldset>
<legend>Left</legend>
<UnitControl
value={paddingLeft}
onChange={(newValue) => {
setPaddingLeft(newValue);
setAttributes({ paddingLeft: newValue });
}}
/>
</fieldset>
<fieldset>
<legend>Right</legend>
<UnitControl
value={paddingRight}
onChange={(newValue) => {
setPaddingRight(newValue);
setAttributes({ paddingRight: newValue });
}}
/>
</fieldset>
<fieldset style={{ gridColumn: 'span 2', width: '116px' }}>
<legend>Bottom</legend>
<UnitControl
value={paddingBottom}
onChange={(newValue) => {
setPaddingBottom(newValue);
setAttributes({ paddingBottom: newValue });
}}
/>
</fieldset>
</div>
) : (
<UnitControl
label="Padding Value"
value={999}
onChange={updatePaddingForAllSizes}
/>
)}
</BaseControl>
{/* Other controls */}
</InspectorControls>
<InnerBlocks
template= {[
["core/template-part", {slug:'header'}]]
}
/>
<div id="lcp-header-container">
<InnerBlocks />
</div>
</div>
);
}

View File

@ -21,5 +21,22 @@
*/
/* eslint-disable no-console */
console.log( 'Hello World! (from create-block-lcp-viewport block)' );
/* eslint-enable no-console */
document.addEventListener('DOMContentLoaded', function() {
// Get the element with the id "lcp-header-container"
var headerContainer = document.getElementById('lcp-header-container');
// Check if the element exists
if (headerContainer) {
// Get the height of the header container
var headerHeight = headerContainer.offsetHeight;
// Get the current value of the --lcp--header--height custom property
var currentHeaderHeight = getComputedStyle(document.documentElement).getPropertyValue('--lcp--header--height').trim();
// Compare if the current value is different from the new headerHeight (in px)
if (currentHeaderHeight !== headerHeight + 'px') {
// If they are different, update the --lcp--header--height custom property
document.documentElement.style.setProperty('--lcp--header--height', headerHeight + 'px');
}
}
});

View File

@ -19,6 +19,10 @@
"hasSidecontent": {
"type": "boolean",
"default": false
},
"hasStickyHeader": {
"type": "boolean",
"default": false
}
},
"textdomain": "lcp-viewport",

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '6fbadff15d92199f61a3');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'a54c989d69efb5b39b31');

View File

@ -1 +1 @@
(()=>{"use strict";var e,t={771:()=>{const e=window.wp.blocks,t=window.wp.i18n,n=window.wp.blockEditor,o=window.wp.components,r=window.wp.element,c=window.ReactJSXRuntime,i=JSON.parse('{"UU":"lcp/viewport"}');(0,e.registerBlockType)(i.UU,{edit:function({attributes:i,setAttributes:s,clientId:l}){const{hasSidecontent:p}=i,d=(0,n.useBlockProps)({className:p?"has-sidecontent":""}),a=[p?["lcp/sidecontent"]:null,["lcp/header-container"],,["lcp/main-area"],["lcp/footer-container"]].filter(Boolean);return(0,r.useEffect)((()=>{if(p){const t=(0,e.createBlock)("lcp/sidecontent",{});wp.data.select("core/block-editor").getBlocks(l),wp.data.dispatch("core/block-editor").insertBlocks(t,1,l)}}),[p,l]),(0,c.jsxs)("div",{...d,children:[(0,c.jsx)(n.InspectorControls,{children:(0,c.jsx)(o.ToggleControl,{label:(0,t.__)("Include Side Content","lcp-viewport"),checked:p,onChange:e=>s({hasSidecontent:e})})}),(0,c.jsx)("div",{id:"lcp-viewport-outer",children:(0,c.jsx)("div",{id:"lcp-viewport-inner",children:(0,c.jsx)(n.InnerBlocks,{renderAppender:()=>(0,c.jsx)(n.InnerBlocks.ButtonBlockAppender,{}),template:a})})})]})},save:function({attributes:e}){const{hasSidecontent:t}=e,o=(n.useBlockProps.save(),t?"lcp-has-sidecontent":"");return(0,c.jsx)("div",{class:o,id:"lcp-viewport-outer",children:(0,c.jsx)("div",{id:"lcp-viewport-inner",class:o,children:(0,c.jsx)(n.InnerBlocks.Content,{})})})}})}},n={};function o(e){var r=n[e];if(void 0!==r)return r.exports;var c=n[e]={exports:{}};return t[e](c,c.exports,o),c.exports}o.m=t,e=[],o.O=(t,n,r,c)=>{if(!n){var i=1/0;for(d=0;d<e.length;d++){n=e[d][0],r=e[d][1],c=e[d][2];for(var s=!0,l=0;l<n.length;l++)(!1&c||i>=c)&&Object.keys(o.O).every((e=>o.O[e](n[l])))?n.splice(l--,1):(s=!1,c<i&&(i=c));if(s){e.splice(d--,1);var p=r();void 0!==p&&(t=p)}}return t}c=c||0;for(var d=e.length;d>0&&e[d-1][2]>c;d--)e[d]=e[d-1];e[d]=[n,r,c]},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};o.O.j=t=>0===e[t];var t=(t,n)=>{var r,c,i=n[0],s=n[1],l=n[2],p=0;if(i.some((t=>0!==e[t]))){for(r in s)o.o(s,r)&&(o.m[r]=s[r]);if(l)var d=l(o)}for(t&&t(n);p<i.length;p++)c=i[p],o.o(e,c)&&e[c]&&e[c][0](),e[c]=0;return o.O(d)},n=self.webpackChunklcp_viewport=self.webpackChunklcp_viewport||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var r=o.O(void 0,[350],(()=>o(771)));r=o.O(r)})();
(()=>{"use strict";var e,t={199:()=>{const e=window.wp.blocks,t=window.wp.i18n,o=window.wp.blockEditor,c=window.wp.components,n=window.wp.element,r=window.wp.data,i=window.ReactJSXRuntime,s=JSON.parse('{"UU":"lcp/viewport"}');(0,e.registerBlockType)(s.UU,{edit:function({attributes:s,setAttributes:l,clientId:a}){const{hasSidecontent:d,hasStickyHeader:p}=s,[u,w]=(0,n.useState)(!1),[k,h]=(0,n.useState)(!1),[v,f]=(0,n.useState)(!1),[b,B]=(0,n.useState)(null),x=(0,o.useBlockProps)({className:d?"has-sidecontent":""}),j=[d?["lcp/sidecontent"]:null,["lcp/header-container"],["lcp/main-area"],["lcp/footer-container"]].filter(Boolean);(0,n.useEffect)((()=>{if(d){const t=(0,e.createBlock)("lcp/sidecontent",{});wp.data.dispatch("core/block-editor").insertBlocks(t,1,a)}}),[d,a]),(0,n.useEffect)((()=>{const e=(0,r.subscribe)((()=>{const e=wp.data.select("core/block-editor").getBlocks();if(k||v)return;const t=e.find((e=>e.clientId!==a&&!wp.data.select("core/block-editor").getBlockParents(e.clientId).includes(a)));t&&(B(t),w(!0),f(!0))}));return()=>{e()}}),[a,k,v]);const m=()=>{if(b){const e=wp.data.select("core/block-editor").getBlocks(a).find((e=>"lcp/main-area"===e.name));e&&(console.log(e),wp.data.dispatch("core/block-editor").insertBlocks(b,e.clientId,0))}w(!1)};return(0,n.useEffect)((()=>{v&&h(!1)}),[v]),(0,r.useSelect)((e=>e("core/block-editor").getBlocks(x.clientId)),[x.clientId]),(0,i.jsxs)("div",{...x,children:[(0,i.jsx)(o.InspectorControls,{children:(0,i.jsx)(c.ToggleControl,{label:(0,t.__)("Include Side Content","lcp-viewport"),checked:d,onChange:e=>l({hasSidecontent:e})})}),(0,i.jsx)("div",{id:"lcp-viewport-outer",children:(0,i.jsx)("div",{id:"lcp-viewport-inner",children:(0,i.jsx)(o.InnerBlocks,{renderAppender:()=>(0,i.jsx)(o.InnerBlocks.ButtonBlockAppender,{}),template:j})})}),u&&!k&&(0,i.jsxs)(c.Modal,{title:(0,t.__)("Block Outside Viewport","lcp-viewport"),onRequestClose:m,className:"block-outside-popup",children:[(0,i.jsx)("p",{children:(0,t.__)("You added a block outside of the lcp/viewport container. Please make sure the block is inside the appropriate area.","lcp-viewport")}),(0,i.jsx)(c.Button,{isPrimary:!0,onClick:m,children:(0,t.__)("Close","lcp-viewport")}),(0,i.jsx)(c.Button,{isSecondary:!0,onClick:()=>{h(!0),w(!1)},children:(0,t.__)("Ignore","lcp-viewport")})]})]})},save:function({attributes:e}){const{hasSidecontent:t,hasStickyHeader:c}=e;o.useBlockProps.save();let n="";return t&&(n+="lcp-has-sidecontent "),c&&(n+="lcp-has-sticky-header"),(0,i.jsx)("div",{className:n,id:"lcp-viewport-outer",children:(0,i.jsx)("div",{id:"lcp-viewport-inner",className:n,children:(0,i.jsx)(o.InnerBlocks.Content,{})})})}})}},o={};function c(e){var n=o[e];if(void 0!==n)return n.exports;var r=o[e]={exports:{}};return t[e](r,r.exports,c),r.exports}c.m=t,e=[],c.O=(t,o,n,r)=>{if(!o){var i=1/0;for(d=0;d<e.length;d++){o=e[d][0],n=e[d][1],r=e[d][2];for(var s=!0,l=0;l<o.length;l++)(!1&r||i>=r)&&Object.keys(c.O).every((e=>c.O[e](o[l])))?o.splice(l--,1):(s=!1,r<i&&(i=r));if(s){e.splice(d--,1);var a=n();void 0!==a&&(t=a)}}return t}r=r||0;for(var d=e.length;d>0&&e[d-1][2]>r;d--)e[d]=e[d-1];e[d]=[o,n,r]},c.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};c.O.j=t=>0===e[t];var t=(t,o)=>{var n,r,i=o[0],s=o[1],l=o[2],a=0;if(i.some((t=>0!==e[t]))){for(n in s)c.o(s,n)&&(c.m[n]=s[n]);if(l)var d=l(c)}for(t&&t(o);a<i.length;a++)r=i[a],c.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return c.O(d)},o=self.webpackChunklcp_viewport=self.webpackChunklcp_viewport||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})();var n=c.O(void 0,[350],(()=>c(199)));n=c.O(n)})();

View File

@ -1 +1 @@
<?php return array('dependencies' => array(), 'version' => 'd4e4a494008d04e1eb42');
<?php return array('dependencies' => array(), 'version' => '31d6cfe0d16ae931b73c');

View File

@ -1 +0,0 @@
console.log("Hello World! (from create-block-lcp-viewport block)");

View File

@ -19,6 +19,10 @@
"hasSidecontent":{
"type":"boolean",
"default":false
},
"hasStickyHeader":{
"type":"boolean",
"default":false
}
},
"textdomain": "lcp-viewport",

View File

@ -1,13 +1,20 @@
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks, InspectorControls } from '@wordpress/block-editor';
import { ToggleControl } from '@wordpress/components';
import { useEffect } from '@wordpress/element';
import { useEffect, useState } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks'; // Used for creating new blocks
import { useSelect, subscribe } from '@wordpress/data'; // Import subscribe
import { Modal, Button } from '@wordpress/components'; // Import Modal for popup
import './editor.scss';
export default function Edit({ attributes, setAttributes, clientId }) {
const { hasSidecontent } = attributes;
const { hasSidecontent, hasStickyHeader } = attributes;
// Popup visibility state
const [isPopupVisible, setIsPopupVisible] = useState(false);
const [hasIgnoredPopup, setHasIgnoredPopup] = useState(false); // Track if user has ignored the popup
const [blockAdded, setBlockAdded] = useState(false); // Track whether a block has been added
const [addedBlock, setAddedBlock] = useState(null); // Track the block that was added outside of viewport
// Block props with custom className management
const blockProps = useBlockProps({
@ -18,7 +25,6 @@ export default function Edit({ attributes, setAttributes, clientId }) {
const template = [
hasSidecontent ? ['lcp/sidecontent'] : null, // Only include 'lcp/sidecontent' if `hasSidecontent` is true
['lcp/header-container'], // Add initial content to header-container
, // Always include 'lcp/header-container' and add a paragraph block inside it
['lcp/main-area'], // Always include 'lcp/main-area'
['lcp/footer-container'] // Always include 'lcp/footer-container'
].filter(Boolean); // Remove any null or undefined values from the array
@ -32,18 +38,84 @@ export default function Edit({ attributes, setAttributes, clientId }) {
useEffect(() => {
if (hasSidecontent) {
// Create a new block and insert it in the block's inner blocks
const blockToAdd = createBlock('lcp/sidecontent', {
});
// Insert the block programmatically into the block
const innerBlocks = wp.data.select('core/block-editor').getBlocks(clientId);
const blockToAdd = createBlock('lcp/sidecontent', {});
wp.data.dispatch('core/block-editor').insertBlocks(blockToAdd, 1, clientId); // Insert at position 0
}
}, [hasSidecontent, clientId]); // Only runs when `hasSidecontent` or `clientId` changes
// Track block additions and show popup if block is outside the lcp/viewport
useEffect(() => {
const unsubscribe = subscribe(() => {
const blocks = wp.data.select('core/block-editor').getBlocks(); // Get all blocks
// If the popup is ignored, don't check for outside blocks
if (hasIgnoredPopup || blockAdded) return;
// Check if any block added is outside the lcp/viewport (excluding lcp/viewport itself)
const addedBlockOutside = blocks.find((block) => {
if (block.clientId === clientId) {
// Skip the current block (lcp/viewport)
return false;
}
const parent = wp.data.select('core/block-editor').getBlockParents(block.clientId);
// If the block is not inside lcp/viewport or its children, show popup
return !parent.includes(clientId);
});
if (addedBlockOutside) {
setAddedBlock(addedBlockOutside); // Store the added block
setIsPopupVisible(true); // Show the popup if block is outside
setBlockAdded(true); // Mark that a block has been added
}
});
// Cleanup the subscription on component unmount
return () => {
unsubscribe();
};
}, [clientId, hasIgnoredPopup, blockAdded]); // Reset check when block is added or state changes
// Handle the popup close
const closePopup = () => {
// Check if an added block exists
if (addedBlock) {
// Retrieve the parent block (main area)
const mainAreaBlock = wp.data.select('core/block-editor').getBlocks(clientId)
.find(block => block.name === 'lcp/main-area'); // Find the 'lcp/main-area' block
if (mainAreaBlock) {
console.log(mainAreaBlock)
// Insert the block into the 'lcp/main-area'
wp.data.dispatch('core/block-editor').insertBlocks(
addedBlock, // The block to insert
mainAreaBlock.clientId, // Specify the clientId of the main-area block
0 // Insert at the start (index 0) of the main-area block's InnerBlocks
);
}
}
// Close the popup after moving the block
setIsPopupVisible(false);
};
// Handle the "Ignore" button click to dismiss popup and not show again until next block addition
const ignorePopup = () => {
setHasIgnoredPopup(true); // Set the state to indicate the user has ignored the popup
setIsPopupVisible(false); // Close the popup
};
// Reset the "Ignore" state when a new block is added (for the next block addition)
useEffect(() => {
if (blockAdded) {
// Reset ignore state after a new block has been added
setHasIgnoredPopup(false);
}
}, [blockAdded]); // Trigger when blockAdded state changes
// Use useSelect to subscribe to the state of inner blocks
const innerBlocks = useSelect((select) => {
return select('core/block-editor').getBlocks(blockProps.clientId);
}, [blockProps.clientId]);
return (
<div {...blockProps}>
{/* Inspector Controls: Add a toggle for the `hasSidecontent` attribute */}
@ -64,6 +136,19 @@ export default function Edit({ attributes, setAttributes, clientId }) {
/>
</div>
</div>
{/* Modal for Popup */}
{isPopupVisible && !hasIgnoredPopup && (
<Modal
title={__('Block Outside Viewport', 'lcp-viewport')}
onRequestClose={closePopup} // Close on clicking outside
className="block-outside-popup"
>
<p>{__('You added a block outside of the lcp/viewport container. Please make sure the block is inside the appropriate area.', 'lcp-viewport')}</p>
<Button isPrimary onClick={closePopup}>{__('Close', 'lcp-viewport')}</Button>
<Button isSecondary onClick={ignorePopup}>{__('Ignore', 'lcp-viewport')}</Button>
</Modal>
)}
</div>
);
}

View File

@ -10,15 +10,21 @@ import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
* @return {Element} Element to render.
*/
export default function Save({ attributes }) {
const { hasSidecontent } = attributes; // Retrieve the hasSidecontent attribute
const { hasSidecontent, hasStickyHeader } = attributes; // Retrieve the hasSidecontent and hasStickyHeader attributes
const blockProps = useBlockProps.save();
// Conditionally add the 'has-sidecontent' class if hasSidecontent is true
const className = hasSidecontent ? 'lcp-has-sidecontent' : '';
// Conditionally add the 'has-sidecontent' and 'lcp-has-sticky-header' classes
let classNames = '';
if (hasSidecontent) {
classNames += 'lcp-has-sidecontent ';
}
if (hasStickyHeader) {
classNames += 'lcp-has-sticky-header';
}
return (
<div class={className} id="lcp-viewport-outer">
<div id="lcp-viewport-inner" class={className}>
<div className={classNames} id="lcp-viewport-outer">
<div id="lcp-viewport-inner" className={classNames}>
<InnerBlocks.Content />
</div>
</div>

View File

@ -21,5 +21,4 @@
*/
/* eslint-disable no-console */
console.log( 'Hello World! (from create-block-lcp-viewport block)' );
/* eslint-enable no-console */

10
package.json Normal file
View File

@ -0,0 +1,10 @@
{
"name": "localcontentpro",
"version": "0.0.1",
"scripts": {
"zip-theme": "node zip-theme.js"
},
"devDependencies": {
"archiver": "^5.3.2"
}
}

View File

@ -29,6 +29,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Function to handle the scroll event
function handleScroll() {
document.documentElement.style.setProperty('--lcp--full-header--height', fullHeaderHeight + "px");
const scrolled = window.scrollY || document.documentElement.scrollTop;
@ -36,7 +37,7 @@ document.addEventListener('DOMContentLoaded', function () {
if (scrolled >= headerHeight) {
// Add the 'lcp-fixed' class and set 'top' to 0 for side content
sideContent.classList.add('lcp-fixed');
sideContent.style.top = fullHeaderHeight + 'px';
// sideContent.style.top = fullHeaderHeight + 'px';
// If the header has 'lcp-sticky-on-scroll', adjust height of side content to be 100vh - fullHeaderHeight
if (headerIsStickyOnScroll) {
@ -44,7 +45,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Add 'lcp-fixed' to the header
header.classList.add('lcp-fixed');
// Set the 'top' of the sideContent to the height of the header
sideContent.style.top = `${fullHeaderHeight}px`;
// sideContent.style.top = `${fullHeaderHeight}px`;
} else if (headerIsSticky) {
// If the header is sticky but not 'sticky-on-scroll', keep the side content height adjusted
sideContent.style.height = `calc(100vh - ${fullHeaderHeight}px)`;
@ -52,13 +53,13 @@ document.addEventListener('DOMContentLoaded', function () {
// Set side content height to 100vh when not sticky
sideContent.style.height = 'calc(100vh - 32px)';
sideContent.style.top = '32px';
// sideContent.style.top = '32px';
}
} else {
// Remove the 'lcp-fixed' class from side content and header if scrolled back above the header
sideContent.classList.remove('lcp-fixed');
sideContent.style.top = ''; // Reset the 'top' style
// sideContent.style.top = ''; // Reset the 'top' style
// Reset height to 100vh when not fixed
sideContent.style.height = `calc(100vh - ${fullHeaderHeight}px)` ;

View File

@ -88,6 +88,10 @@ Version: 1.0
z-index: 3;
width: 100%
}
#lcp-sidecontent {top:var(--lcp---header--height)}
.admin-bar #lcp-sidecontent {
top: calc(var(--lcp--header--height) + 32px);
}
#lcp-header-container.lcp-sticky {
position: fixed;

89
zip-theme.js Normal file
View File

@ -0,0 +1,89 @@
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
// Path to the theme's style.css
const styleCssPath = path.join(__dirname, 'style.css');
// Directories to exclude
const excludedDirs = [
'node_modules',
'src',
'.gitignore',
'package.json',
'package-lock.json',
'theme.zip',
'.git'
];
// Helper function to check if a file or directory should be excluded
const shouldExclude = (filePath) => {
return excludedDirs.some(exclude => filePath.includes(exclude));
};
// Read the style.css file and extract the theme name
fs.readFile(styleCssPath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading style.css:', err);
return;
}
// Regex to match the theme name in the header
const themeNameMatch = data.match(/\/\*[\s\S]*?Theme Name:\s*(.*?)\s*\*\//);
// Fallback to 'theme' if no theme name is found
const themeName = themeNameMatch && themeNameMatch[1] ? themeNameMatch[1].trim() : 'theme';
// Sanitize the theme name (remove spaces and convert to lowercase)
const sanitizedThemeName = themeName.replace(/\s+/g, '').toLowerCase();
// Define the output zip file path using the sanitized theme name
const output = fs.createWriteStream(path.join(__dirname, `${sanitizedThemeName}.zip`));
// Create an archiver instance
const archive = archiver('zip', {
zlib: { level: 9 } // Compression level
});
// Pipe the archive to the output file
archive.pipe(output);
// Recursively append all files in the theme directory except excluded ones
const addFilesRecursively = (directory) => {
const files = fs.readdirSync(directory);
files.forEach((file) => {
const filePath = path.join(directory, file);
// Exclude the file or directory if it matches the exclusion criteria
if (shouldExclude(filePath)) {
console.log(`Excluding: ${filePath}`);
return; // Skip this file/directory
}
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
// If it's a directory, recurse into it
addFilesRecursively(filePath);
} else {
// If it's a file, add it to the archive
archive.file(filePath, { name: path.relative(__dirname, filePath) });
}
});
};
// Start adding files from the theme directory
addFilesRecursively(__dirname);
// Finalize the archive
archive.finalize();
output.on('close', () => {
console.log(`Theme has been zipped into ${sanitizedThemeName}.zip`);
});
output.on('error', (err) => {
console.error('Error creating zip file:', err);
});
});