Compare commits

...

33 Commits

Author SHA1 Message Date
ae31fec647 Changes to LCP Gallery Meta 2025-01-09 17:50:32 -08:00
ceb37fc5af Changes to repeater ui, and completed MVP for custom image sizes 2025-01-04 14:58:58 -08:00
63e181109c Changes to backend and tabs UI 2025-01-03 16:33:20 -08:00
b79a4ece03 Fixed icon importer 2025-01-02 22:31:19 -08:00
3a205a53cf Added delete feature to post importer 2025-01-02 21:31:25 -08:00
4447e50bcf Added Demo posts inserter 2025-01-02 03:07:43 -08:00
1ce1a08442 Added demo posts importer 2025-01-02 03:06:30 -08:00
7bba517b48 Changes to blocks and added basic framework for Newsletters 2025-01-02 01:29:51 -08:00
d928a0e8fd Added theme archvier and changes to blocks 2025-01-01 18:09:33 -08:00
21b00e1937 Changes to blocks and styles 2025-01-01 03:08:58 -08:00
741d39a962 Changes to lcp-button 2024-12-31 18:53:46 -08:00
2df16e37a8 Basic integration of tabs UI on theme settings page 2024-12-30 19:41:59 -08:00
4394776735 Add tabs to lcp-ui js and css 2024-12-30 19:23:48 -08:00
a8b4dc6b70 Changed location of react components to .../assets/js/react/components 2024-12-30 18:59:57 -08:00
4b78f9f571 Added lcp-ui.js and changes to blocks 2024-12-30 17:39:30 -08:00
b9e2660318 Added support for all Material Icons 2024-12-30 03:00:46 -08:00
28139d2455 Changes to icon importer 2024-12-30 02:31:16 -08:00
0d59719440 changes to lcp-button and icon uploader 2024-12-29 22:52:17 -08:00
372d5aa2c1 Changes to lcp button and icons 2024-12-29 01:47:56 -08:00
d65992a169 Changes to lcp button and icon selector 2024-12-29 01:47:33 -08:00
462cffdddc changes to blocks and styles 2024-12-27 22:56:39 -08:00
93cc7be3bf Changes to styles 2024-12-23 04:10:45 -08:00
44c621e0da Changes to styles 2024-12-23 03:52:21 -08:00
216d108289 Changes to styles 2024-12-23 03:51:13 -08:00
1ccc6f0031 Changes to script.js and moved backend code to /includes/classes/backend.php 2024-12-23 02:50:12 -08:00
f3fbe0fa32 Changes to blocks 2024-12-22 15:20:12 -08:00
cfbb860bf9 Changes to theme.json and gallery 2024-12-19 01:23:40 -08:00
94d2c7c8a2 Changes to directory structure 2024-12-18 02:27:09 -08:00
d5a5f4e87b Changes to directory structure 2024-12-18 02:20:47 -08:00
961081128a Changes to gallery block and custom code inserter 2024-12-17 17:33:30 -08:00
f19e779946 Minor changes 2024-12-17 01:37:55 -08:00
4936a3a742 Minor changes to filenames 2024-12-16 20:15:22 -08:00
1234341241 New dev branch 2024-12-16 20:12:25 -08:00
296 changed files with 96715 additions and 18 deletions

1
.gitignore vendored
View File

@ -6,7 +6,6 @@ wp-includes/
.DS_Store
node_modules/
.env
build/
dist/
package-lock.json
yarn.lock

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 KiB

View File

@ -0,0 +1,32 @@
function lcpUpdateImageSizes(imageSizes) {
// Validate the input to ensure it's an array
if (!Array.isArray(imageSizes)) {
console.error('Invalid image sizes data');
return;
}
// Prepare the form data to send to WordPress to update the theme settings
const updateData = new FormData();
updateData.append('action', 'update_lcp_theme_settings'); // WordPress action hook
updateData.append('image_sizes', JSON.stringify(imageSizes)); // Send image sizes as a JSON string
updateData.append('nonce', customImageSizeAjax.nonce); // Send the nonce
console.log('Sending data to AJAX:', updateData); // Log the data for debugging
// Send the AJAX request
fetch(customImageSizeAjax.ajax_url, {
method: 'POST',
body: updateData, // Send form data directly
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('Theme settings updated successfully.');
} else {
console.error('Failed to update theme settings:', data.message);
}
})
.catch(error => {
console.error('Error updating theme settings:', error);
});
}

View File

@ -0,0 +1,85 @@
document.addEventListener('DOMContentLoaded', function () {
// Check if the 'Import Demo Posts' button exists
const importButton = document.getElementById('import-demo-posts');
console.log(importButton); // This will log the button element to the console
if (importButton) {
// Check if the lcp_demo_posts option exists
if (lcp_ajax_obj.lcp_demo_posts && lcp_ajax_obj.lcp_demo_posts.post_ids && lcp_ajax_obj.lcp_demo_posts.post_ids.length > 0) {
// Disable the 'Import Demo Posts' button if demo posts have already been imported
importButton.disabled = true;
importButton.textContent = 'Demo Posts Already Imported'; // Optional: Change button text
} else {
// Add event listener to import demo posts if the button is enabled
importButton.addEventListener('click', function () {
console.log("Clicked"); // This should be triggered on button click
const formData = new FormData();
// Append action and nonce to the form data
formData.append('action', 'lcp_import_demo_posts');
formData.append('lcp_import_nonce', lcp_ajax_obj.nonce);
// Send the AJAX request to import demo posts
fetch(lcp_ajax_obj.ajax_url, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(data.data); // Success message
importButton.disabled = true;
importButton.textContent = 'Demo Posts Imported'; // Optional: Change button text
} else {
alert('Error: ' + (data.data || 'Unknown error')); // Error message
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while processing your request.');
});
});
}
}
// Check if the 'Delete Demo Posts' button exists
const deleteButton = document.getElementById('delete-demo-posts');
if (deleteButton) {
deleteButton.addEventListener('click', function () {
const formData = new FormData();
// Append action and nonce to the form data for deletion
formData.append('action', 'lcp_delete_demo_posts'); // Use the correct action here
formData.append('lcp_import_nonce', lcp_ajax_obj.nonce); // Pass nonce for security
// Send the AJAX request to delete demo posts
fetch(lcp_ajax_obj.ajax_url, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(data.data); // Success message
deleteButton.disabled = true;
deleteButton.textContent = 'Demo Posts Deleted';
// Re-enable the "Import Demo Posts" button and reset its text
const importButton = document.getElementById('import-demo-posts');
if (importButton) {
importButton.disabled = false;
importButton.textContent = 'Import Demo Posts';
}
} else {
alert('Error: ' + (data.data || 'Unknown error'));
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while processing your request.');
});
});
}
});

View File

@ -0,0 +1,71 @@
document.addEventListener("DOMContentLoaded", function () {
// Get reference to the popup element
const popup = document.getElementById('popup');
const facebookShareLink = document.getElementById('facebook-share');
const twitterShareLink = document.getElementById('twitter-share');
// Function to check if selection intersects with any <p> tag
function isSelectionInParagraph(selection) {
const range = selection.getRangeAt(0); // Get the selected range
const paragraphs = document.querySelectorAll('p'); // Get all <p> elements
for (let p of paragraphs) {
// Check if the selected range intersects with this <p> tag
const pRect = p.getBoundingClientRect();
const rangeRect = range.getBoundingClientRect();
// Check if the range and the <p> element's bounding boxes overlap
if (rangeRect.top < pRect.bottom && rangeRect.bottom > pRect.top &&
rangeRect.left < pRect.right && rangeRect.right > pRect.left) {
return true; // Selection intersects with this <p> tag
}
}
return false; // No intersection with any <p> tag
}
// Function to show the popup near the selected text
function showPopup(e) {
const selection = window.getSelection();
// Check if any text is selected and if it is inside or intersects with a <p> tag
if (selection.toString().length > 0 && isSelectionInParagraph(selection)) {
// Get the bounding rectangle of the selected text
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
// Position the popup above the selection (adjust for spacing)
popup.style.left = `${rect.left + window.scrollX}px`;
popup.style.top = `${rect.top + window.scrollY - popup.offsetHeight - 5}px`;
// Show the popup
popup.style.display = 'block';
// Set up share links with the selected text and page URL
const selectedText = selection.toString();
const pageUrl = window.location.href;
// Facebook share link
facebookShareLink.setAttribute('href', `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(pageUrl)}&quote=${encodeURIComponent(selectedText)}`);
// Twitter/X share link
twitterShareLink.setAttribute('href', `https://twitter.com/intent/tweet?text=${encodeURIComponent(selectedText)}&url=${encodeURIComponent(pageUrl)}`);
} else {
// Hide the popup if no text is selected or it's not inside or overlapping a <p> tag
popup.style.display = 'none';
}
}
// Listen for mouse-up event (when selection ends)
document.addEventListener('mouseup', showPopup);
// Optionally, listen for touchend event for mobile devices
document.addEventListener('touchend', showPopup);
// Hide the popup if user clicks anywhere else
document.addEventListener('click', function(event) {
if (!popup.contains(event.target) && !window.getSelection().toString()) {
popup.style.display = 'none';
}
});
});

77
assets/js/icon-import.js Normal file
View File

@ -0,0 +1,77 @@
document.addEventListener('DOMContentLoaded', function () {
console.log('DOMContentLoaded event triggered');
const installButtons = document.querySelectorAll('.install-btn');
const uninstallButtons = document.querySelectorAll('.uninstall-btn');
console.log('Install buttons:', installButtons);
console.log('Uninstall buttons:', uninstallButtons);
// Handle Install button click
installButtons.forEach(function (button) {
console.log('Adding install listener to button', button);
button.addEventListener('click', function (event) {
event.preventDefault(); // Prevent default form submission or page reload
console.log('Install button clicked');
const iconSetId = this.getAttribute('data-icon-set-id');
console.log('Icon Set ID:', iconSetId);
const formData = new FormData();
formData.append('action', 'install_icon_set');
formData.append('icon_set_id', iconSetId);
const xhr = new XMLHttpRequest();
xhr.open('POST', lcp_ajax.ajax_url, true);
xhr.onload = function () {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
if (response.success) {
alert('Icon set installed successfully!');
} else {
alert('Failed to install icon set: ' + response.data.message);
}
} else {
alert('There was an error with the request.');
}
};
xhr.send(formData);
});
});
// Handle Uninstall button click
uninstallButtons.forEach(function (button) {
console.log('Adding uninstall listener to button', button);
button.addEventListener('click', function (event) {
event.preventDefault(); // Prevent default form submission or page reload
console.log('Uninstall button clicked');
const iconSetId = this.getAttribute('data-icon-set-id');
console.log('Icon Set ID for uninstall:', iconSetId);
const formData = new FormData();
formData.append('action', 'uninstall_icon_set');
formData.append('icon_set_id', iconSetId);
const xhr = new XMLHttpRequest();
xhr.open('POST', lcp_ajax.ajax_url, true);
xhr.onload = function () {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
if (response.success) {
alert('Icon set uninstalled successfully!');
} else {
alert('Failed to uninstall icon set: ' + response.data.message);
}
} else {
alert('There was an error with the request.');
}
};
xhr.send(formData);
});
});
});

250
assets/js/lcp-ui.js Normal file
View File

@ -0,0 +1,250 @@
/* ACCORDION */
document.addEventListener('DOMContentLoaded', function () {
// Find all elements with the class "lcp-accordion"
var accordions = document.querySelectorAll('.lcp-accordion');
accordions.forEach(function (accordion) {
// Find all tabs within the current accordion
var tabs = accordion.querySelectorAll('.lcp-accordion-tab');
tabs.forEach(function (tab) {
var header = tab.querySelector('h3');
var content = tab.querySelector('.lcp-accordion-content');
// Add click event to each header to toggle the content
header.addEventListener('click', function () {
// Toggle the 'active' class on the tab to show/hide content
tab.classList.toggle('active');
// Optional: Close other open tabs (if only one tab should be open at a time)
tabs.forEach(function (otherTab) {
if (otherTab !== tab) {
otherTab.classList.remove('active');
}
});
});
});
});
});
/* TABS */
document.addEventListener('DOMContentLoaded', function () {
const tabs = document.querySelectorAll('.tab-link');
const panes = document.querySelectorAll('.tab-pane');
const tabContainer = document.querySelector('.lcp-tab-container'); // The parent container
// Only enable the hash functionality if the container has the class '.lcp-support-hash'
if (tabContainer && tabContainer.classList.contains('lcp-support-hash')) {
// Function to set the active tab based on the hash in the URL
function setActiveTabFromHash() {
const hash = window.location.hash; // Get the current URL hash
if (hash) {
const targetTab = document.querySelector(`.tab-link[data-tab="${hash.substring(1)}"]`); // Remove '#' from hash
const targetPane = document.getElementById(hash.substring(1));
// If both tab and pane exist, make them active
if (targetTab && targetPane) {
tabs.forEach(link => link.classList.remove('active'));
panes.forEach(pane => pane.classList.remove('active'));
targetTab.classList.add('active');
targetPane.classList.add('active');
} else {
console.error(`Tab or pane with ID '${hash}' not found.`);
}
}
}
// Set the active tab from the hash when the page loads
setActiveTabFromHash();
}
// Handle tab clicks
tabs.forEach(tab => {
tab.addEventListener('click', function (event) {
event.preventDefault();
// Remove active class from all tabs and panes
tabs.forEach(link => link.classList.remove('active'));
panes.forEach(pane => pane.classList.remove('active'));
// Add active class to the clicked tab
tab.classList.add('active');
// Get the target pane and update URL hash
const targetPaneId = tab.dataset.tab;
const targetPane = document.getElementById(targetPaneId);
// Check if targetPane exists before trying to manipulate it
if (targetPane) {
targetPane.classList.add('active');
if (tabContainer && tabContainer.classList.contains('lcp-support-hash')){
window.location.hash = targetPaneId; // Update the URL hash
}
} else {
console.error(`Tab pane with ID '${targetPaneId}' not found.`);
}
});
});
});
/* REPEATER */
document.addEventListener('DOMContentLoaded', function() {
// Function to initialize each repeater instance
function initRepeater(repeater) {
const firstRow = repeater.querySelector('.lcp-repeater-row');
// Function to check if all required fields are filled
function isRequiredFieldsFilled(row) {
const requiredFields = row.querySelectorAll('[data-lcp-required="true"], [data-lcp-required-for-new-row="true"]');
let valid = true;
requiredFields.forEach(input => {
// Check input type and value for validation
if (input.type === 'number' && (input.value === '' || isNaN(input.value))) {
valid = false;
} else if (input.type === 'checkbox' && !input.checked) {
valid = false;
}
});
return valid;
}
// Function to create a new row by cloning the first row
function addRepeaterRow() {
const newRow = firstRow.cloneNode(true); // true means deep cloning (includes children)
const inputs = newRow.querySelectorAll('input');
inputs.forEach(input => input.value = ''); // Reset value for input fields
newRow.querySelector('input[type="checkbox"]').checked = false; // Uncheck the checkbox
repeater.insertBefore(newRow, repeater.querySelector('.lcp-repeater-add-row'));
toggleAddRowButton();
toggleSubmitButton();
}
// Function to toggle the "Add Row" button based on the required field for new rows
function toggleAddRowButton() {
const addRowButton = repeater.querySelector('.lcp-repeater-add-row');
const rows = repeater.querySelectorAll('.lcp-repeater-row');
let validForNewRow = true;
rows.forEach(row => {
const requiredForNewRowFields = row.querySelectorAll('[data-lcp-required-for-new-row="true"]');
requiredForNewRowFields.forEach(input => {
if (input.value === '' || (input.type === 'checkbox' && !input.checked)) {
validForNewRow = false;
}
});
});
addRowButton.disabled = !validForNewRow;
}
// Function to toggle the "Submit" button based on the validation of all rows
function toggleSubmitButton() {
const submitButton = repeater.querySelector('.lcp-repeater-submit');
const rows = repeater.querySelectorAll('.lcp-repeater-row');
let allValid = true;
rows.forEach(row => {
if (!isRequiredFieldsFilled(row)) {
allValid = false;
}
});
submitButton.disabled = !allValid;
}
// Function to handle form submission, ensuring all required fields are filled
function handleSubmit(event) {
const submitButton = event.target;
const actionType = submitButton.getAttribute('data-lcp-action'); // This will be used to pass data to the handler
const rows = repeater.querySelectorAll('.lcp-repeater-row');
let allValid = true;
rows.forEach(row => {
if (!isRequiredFieldsFilled(row)) {
allValid = false;
}
});
if (allValid) {
const repeaterData = [];
rows.forEach(function(row) {
const rowData = {};
const inputs = row.querySelectorAll('input');
inputs.forEach(function(input) {
const key = input.getAttribute('data-lcp-repeater-key');
let value;
if (input.type === 'checkbox') {
value = input.checked; // Boolean value for checkbox
} else {
value = input.value; // Text or number inputs
}
rowData[key] = value; // Add key-value pair to rowData
});
repeaterData.push(rowData); // Add rowData to the repeaterData array
});
// Now just pass the data off to the handler
if (typeof window[actionType] === 'function') {
window[actionType](repeaterData); // Calls the function (e.g., `lcpUpdateImageSizes`)
}
} else {
alert("Please fill in all required fields.");
}
}
// Event listener for adding a new row when a user clicks the "Add Row" button
const addRowButtons = repeater.querySelectorAll('.lcp-repeater-add-row');
addRowButtons.forEach(button => {
button.addEventListener('click', function() {
if (isRequiredFieldsFilled(firstRow)) {
addRepeaterRow();
} else {
alert('Please fill in the required fields to add a new row.');
}
});
});
// Event listener for submitting the repeater form
const submitButton = repeater.querySelector('.lcp-repeater-submit');
submitButton.addEventListener('click', handleSubmit);
// Initial validation for the add row button
toggleAddRowButton();
// Initial validation for the submit button
toggleSubmitButton();
// Added event listeners to handle any changes in input and trigger validation
repeater.addEventListener('input', function() {
toggleAddRowButton(); // Revalidate Add Row button
toggleSubmitButton(); // Revalidate Submit button
});
}
// Initialize each repeater on the page
const repeaters = document.querySelectorAll('.lcp-repeater');
repeaters.forEach(repeater => {
initRepeater(repeater);
});
});

122
assets/js/lcp.js Normal file
View File

@ -0,0 +1,122 @@
document.addEventListener('DOMContentLoaded', function () {
// Get references to the DOM elements
const header = document.getElementById('lcp-header-container');
const sideContent = document.getElementById('lcp-sidecontent');
// Ensure elements exist before proceeding
if (!header || !sideContent) return;
// Check if the header has the 'lcp-sticky' and 'lcp-sticky-on-scroll' class
const headerIsSticky = header.classList.contains('lcp-sticky');
const headerIsStickyOnScroll = header.classList.contains('lcp-sticky-on-scroll');
// Measure the height of the header once the DOM is loaded
const headerHeight = header.offsetHeight;
let fullHeaderHeight;
// If the admin-bar is present, the fullHeaderHeight is the headerHeight + 32px
if (document.body.classList.contains('admin-bar')) {
const adminBarHeight = document.getElementById('wpadminbar').offsetHeight;
fullHeaderHeight = headerHeight + adminBarHeight;
} else {
fullHeaderHeight = headerHeight;
}
// Function to handle the scroll event
function handleScroll() {
document.documentElement.style.setProperty('--lcp--full-header--height', fullHeaderHeight + "px");
if (!headerIsSticky){
const scrolled = window.scrollY || document.documentElement.scrollTop;
// Check if the page has scrolled past the height of the header
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';
// If the header has 'lcp-sticky-on-scroll', adjust height of side content to be 100vh - fullHeaderHeight
if (headerIsStickyOnScroll) {
sideContent.style.height = `calc(100vh - ${fullHeaderHeight}px)`;
// 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`;
} 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)`;
} else {
// Set side content height to 100vh when not sticky
// sideContent.style.height = 'calc(100vh - 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
// Reset height to 100vh when not fixed
// sideContent.style.height = `calc(100vh - ${fullHeaderHeight}px)` ;
// If header has the 'lcp-sticky-on-scroll' class, remove 'lcp-fixed' from the header
if (headerIsStickyOnScroll) {
header.classList.remove('lcp-fixed');
}
}
}
}
// Also trigger the scroll handler once on load in case the page is already scrolled
handleScroll();
function debounce(func, delay) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, delay);
};
}
// Add debounced scroll event listener
window.addEventListener('scroll', debounce(handleScroll, 0)); // 200ms debounce
});
const sidecontent = document.getElementById("lcp-sidecontent");
const innerContent = document.getElementById("lcp-sidecontent-inner");
const scrollTrack = document.getElementById("lcp-scroll-track");
const scrollBar = document.getElementById("lcp-scroll-bar");

View File

@ -0,0 +1,96 @@
import { __ } from '@wordpress/i18n';
import { BaseControl, __experimentalNumberControl as NumberControl, SelectControl, __experimentalHStack as HStack } from '@wordpress/components';
import { useState, useEffect } from 'react';
/**
* Control component with a number input and a select dropdown for units (px, rem, em, % etc.).
* Accepts a default value as a string (e.g., "15px", "10rem", etc.).
* Includes an optional 'auto' unit if includeAuto is true, and handles autoReturnsNull behavior.
*/
export function DimensionValueControl({ value = '10px', onChange, includeAuto = false, autoReturnsNull = false }) {
// Options for select control (CSS units) including 'auto' if enabled
const unitOptions = [
{ label: __('px'), value: 'px' },
{ label: __('%'), value: '%' },
{ label: __('em'), value: 'em' },
{ label: __('rem'), value: 'rem' },
{ label: __('vw'), value: 'vw' },
{ label: __('vh'), value: 'vh' },
...(includeAuto ? [{ label: __('auto'), value: 'auto' }] : []), // Add 'auto' option if includeAuto is true
];
// Parse the value string into a number and unit
const parseValue = (value) => {
const regex = /([0-9]+)([a-zA-Z%]+)?/; // Capture the number and the unit
const match = value.match(regex);
if (match) {
return {
numberValue: parseInt(match[1], 10), // Number part
unitValue: match[2] || 'px' // Unit part (default to 'px' if no unit found)
};
}
return { numberValue: 0, unitValue: 'px' }; // Fallback if invalid format
};
// Use the parsed value to set initial state
const { numberValue: initialNumber, unitValue: initialUnit } = parseValue(value);
const [numberValue, setNumberValue] = useState(initialNumber);
const [unitValue, setUnitValue] = useState(initialUnit);
// Combine the number and unit into a string like "15px"
const dimensionValue = `${numberValue}${unitValue}`;
// Handle number change
const onNumberChange = (newValue) => {
setNumberValue(newValue);
if (onChange && unitValue !== 'auto') {
onChange(`${newValue}${unitValue}`); // Pass updated value back to parent
}
};
// Handle unit change
const onUnitChange = (newUnit) => {
if (newUnit === 'auto') {
setNumberValue(null); // Reset the number value when 'auto' is selected
}
setUnitValue(newUnit);
if (onChange) {
const updatedValue = newUnit === 'auto'
? (autoReturnsNull ? null : 'auto')
: `${numberValue}${newUnit}`; // Pass 'auto' or null or updated value back to parent
onChange(updatedValue);
}
};
// Effect to handle when value prop changes (useful for syncing)
useEffect(() => {
const { numberValue, unitValue } = parseValue(value);
setNumberValue(numberValue);
setUnitValue(unitValue);
}, [value]);
return (
<BaseControl className="lcp-dimension-value-control">
<HStack>
{/* Number input control, disabled when 'auto' is selected */}
<NumberControl
className="lcp-number-control"
value={numberValue || ''}
onChange={onNumberChange}
min={0}
step={0.1}
spinControls={'none'}
disabled={unitValue === 'auto'} // Disable number input if 'auto' is selected
/>
{/* Select dropdown control for units */}
<SelectControl
className="lcp-select-control"
value={unitValue}
options={unitOptions}
onChange={onUnitChange}
/>
</HStack>
</BaseControl>
);
}

View File

@ -0,0 +1,59 @@
import { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { SelectControl } from '@wordpress/components';
export function IconSelectControl(props) {
const { iconSvgId, onIconChange } = props;
const [iconData, setIconData] = useState([]);
useEffect(() => {
const fetchIconData = async () => {
try {
const response = await fetch('/wp-json/lcp/v1/icons');
const data = await response.json();
if (Array.isArray(data) && data.length > 0) {
setIconData(data); // Set the fetched data directly
}
} catch (error) {
console.error('Error fetching icons:', error);
}
};
fetchIconData();
}, []);
const handleIconChange = (selectedIconId) => {
const selectedIcon = iconData.find(icon => icon.iconSvgId === selectedIconId);
if (selectedIcon && onIconChange) {
// Send both icon ID and path (SVG) to the parent component
onIconChange({
iconSvgId: selectedIcon.iconSvgId, // Pass icon ID to parent
iconSvgPath: selectedIcon.iconSvgPaths, // Pass icon paths to parent
viewbox: selectedIcon.selectedIconViewbox // Pass the viewbox to parent
});
console.log("Selected Icon ID:", selectedIcon.iconSvgId); // Debugging output
console.log("Selected Icon Path:", selectedIcon.iconSvgPaths); // Debugging output
console.log("Selected Icon Viewbox:", selectedIcon.selectedIconViewbox); // Debugging output
}
};
if (iconData.length === 0) {
return <p>{__('Loading icons...', 'lcp')}</p>; // Loading state
}
const iconOptions = iconData.map((icon) => ({
value: icon.iconSvgId, // Use icon ID as value for the SelectControl
label: icon.name, // Directly use the icon's name as the label
}));
return (
<SelectControl
label={__('Select Icon', 'lcp')}
value={iconSvgId} // Current selected icon ID
options={iconOptions}
onChange={handleIconChange} // Handle icon change
/>
);
}

View File

@ -0,0 +1,103 @@
import { __ } from '@wordpress/i18n';
import { BaseControl, Button, RangeControl, __experimentalHStack as HStack, __experimentalVStack as VStack } from '@wordpress/components';
import { DimensionValueControl } from './DimensionValueControl';
/**
* Padding Control component to manage padding values for different screen sizes.
*/
export function PaddingControl() {
return (
<BaseControl className="lcp-padding-control">
{/* Padding label and Unlink button */}
<HStack>
<span>{__('Padding')}</span>
<Button
variant="secondary"
aria-label={__('Unlink sides')}
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="M10 17.389H8.444A5.194 5.194 0 1 1 8.444 7H10v1.5H8.444a3.694 3.694 0 0 0 0 7.389H10v1.5ZM14 7h1.556a5.194 5.194 0 0 1 0 10.39H14v-1.5h1.556a3.694 3.694 0 0 0 0-7.39H14V7Zm-4.5 6h5v-1.5h-5V13Z"></path>
</svg>
</Button>
</HStack>
{/* Extra Large Padding Controls */}
{/* Will update all padding values for all screen sizes if updateAllScreenSizes is true */}
{/* Top and Bottom HStack */}
<HStack style={{ flex: 1 }}>
{/* Top and Bottom Icon */}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" className="spacing-sizes-control__icon" aria-hidden="true" focusable="false">
<path d="m7.5 6h9v-1.5h-9zm0 13.5h9v-1.5h-9zm-3-3h1.5v-9h-1.5zm13.5-9v9h1.5v-9z" style={{ opacity: 0.25 }}></path>
<path d="m7.5 6h9v-1.5h-9z"></path>
<path d="m7.5 19.5h9v-1.5h-9z"></path>
</svg>
{/* RangeControl wrapped in HStack with flex: 1 applied to its parent */}
<HStack style={{ flex: 1 }}>
<DimensionValueControl/>
<RangeControl
withInputField={false}
value={10} // Placeholder value
onChange={() => {}}
min={0}
max={50}
/>
</HStack>
{/* Custom Padding Button */}
<Button
style={{padding:0,background:'none',color:'var(--wp-components-color-foreground)'} }
variant="primary"
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="m19 7.5h-7.628c-.3089-.87389-1.1423-1.5-2.122-1.5-.97966 0-1.81309.62611-2.12197 1.5h-2.12803v1.5h2.12803c.30888.87389 1.14231 1.5 2.12197 1.5.9797 0 1.8131-.62611 2.122-1.5h7.628z"></path>
<path d="m19 15h-2.128c-.3089-.8739-1.1423-1.5-2.122-1.5s-1.8131.6261-2.122 1.5h-7.628v1.5h7.628c.3089.8739 1.1423 1.5 2.122 1.5s1.8131-.6261 2.122-1.5h2.128z"></path>
</svg>
</Button>
</HStack>
{/* Left and Right HStack */}
<HStack style={{ flex: 1 }}>
{/* Left and Right Icon */}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" className="spacing-sizes-control__icon" aria-hidden="true" focusable="false">
<path d="m7.5 6h9v-1.5h-9zm0 13.5h9v-1.5h-9zm-3-3h1.5v-9h-1.5zm13.5-9v9h1.5v-9z" style={{ opacity: 0.25 }}></path>
<path d="m7.5 6h9v-1.5h-9z"></path>
<path d="m4.5 7.5v9h1.5v-9z"></path>
<path d="m18 7.5v9h1.5v-9z"></path>
</svg>
{/* RangeControl wrapped in HStack with flex: 1 applied to its parent */}
<RangeControl
withInputField={false}
value={10} // Placeholder value
onChange={() => {}}
min={0}
max={50}
/>
{/* Custom Padding Button */}
<Button
variant="primary"
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="m19 7.5h-7.628c-.3089-.87389-1.1423-1.5-2.122-1.5-.97966 0-1.81309.62611-2.12197 1.5h-2.12803v1.5h2.12803c.30888.87389 1.14231 1.5 2.12197 1.5.9797 0 1.8131-.62611 2.122-1.5h7.628z"></path>
<path d="m19 15h-2.128c-.3089-.8739-1.1423-1.5-2.122-1.5s-1.8131.6261-2.122 1.5h-7.628v1.5h7.628c.3089.8739 1.1423 1.5 2.122 1.5s1.8131-.6261 2.122-1.5h2.128z"></path>
</svg>
</Button>
</HStack>
{/* Additional controls can be added here in a VStack */}
<VStack>
{/* Placeholder for additional components */}
</VStack>
</BaseControl>
);
}

View File

@ -0,0 +1,57 @@
{
"posts": [
{
"title": "Mysterious Weather Patterns Reported in Faketown",
"content": "Faketown, USA — In what can only be described as a strange turn of events, Faketown residents have been experiencing unpredictable weather patterns. Local meteorologists have been baffled as sudden temperature shifts have been occurring at all hours of the day. One moment, the sun is shining brightly, and the next, it's snowing. This bizarre occurrence is leaving many questioning whether this is a sign of something more ominous. 'Ive lived here my entire life, and Ive never seen anything like this,' said local resident Jane Doe.",
"excerpt": "Faketown residents have been experiencing unpredictable weather patterns, baffling local meteorologists.",
"category": "Local News",
"tags": ["weather", "Faketown", "mystery"],
"date": "2024-01-01",
"status": "publish",
"thumbnail": "demo-post-thumbnail-1.jpg"
},
{
"title": "Faketown Mayor Announces New Green Initiative",
"content": "In a recent press conference, Faketowns mayor, John Smith, unveiled an ambitious plan to tackle climate change within the city. The initiative aims to reduce carbon emissions by 40% over the next 10 years, primarily by encouraging the use of electric vehicles and expanding the city's public transportation system. 'We are committed to making Faketown a greener place,' said Mayor Smith. 'Our children and grandchildren deserve a sustainable future.' The plan includes installing charging stations for electric cars across the city and offering tax incentives for green energy solutions.",
"excerpt": "Faketowns mayor unveils a new green initiative to reduce carbon emissions by 40%.",
"category": "Politics",
"tags": ["green", "climate change", "Faketown"],
"date": "2024-01-02",
"status": "publish",
"thumbnail": "demo-post-thumbnail-2.jpg"
},
{
"title": "Local Chef Opens Revolutionary Restaurant in Faketown",
"content": "Faketown, USA — In a culinary first for Faketown, renowned chef Sarah Bellamy has opened a new restaurant that combines traditional American cuisine with exotic flavors from around the world. Located in the heart of Faketown, Bellamys restaurant has already become the talk of the town. The menu features a variety of dishes, including 'Fusion Fries' and 'Sushi Burger.' 'I wanted to create something completely unique, a blend of cultures,' said Bellamy. The restaurant offers both dine-in and delivery services, with plans to expand its menu soon.",
"excerpt": "Faketown's new restaurant is offering a fusion of global flavors with a local twist.",
"category": "Food & Drink",
"tags": ["restaurant", "food", "Faketown"],
"date": "2024-01-03",
"status": "publish",
"thumbnail": "demo-post-thumbnail-3.jpg"
},
{
"title": "Strange Creatures Spotted in Faketown's Forests",
"content": "In the deep forests surrounding Faketown, local hikers have begun reporting sightings of strange creatures. Descriptions vary, but many claim to have seen large, mysterious beings with glowing eyes. Some hikers have even reported hearing unusual sounds that have no obvious explanation. 'I was out for a hike last week when I saw something huge moving through the trees,' said Greg Johnson, a local resident. 'I dont know what it was, but it wasnt a bear.' Authorities are urging hikers to stay on well-trodden paths and report any strange occurrences.",
"excerpt": "Faketown residents report strange creatures spotted in nearby forests, baffling experts.",
"category": "Strange Happenings",
"tags": ["mystery", "Faketown", "creatures"],
"date": "2024-01-04",
"status": "publish",
"thumbnail": "demo-post-thumbnail-1.jpg"
},
{
"title": "Faketown's Annual Festival Breaks Attendance Records",
"content": "This year's annual Faketown Festival was a resounding success, breaking all previous attendance records. Held in the town square, the festival featured local bands, food trucks, and street performances. The highlight of the event was the traditional 'Faketown Parade,' which saw thousands of people lining the streets. 'Weve never seen this many people come out,' said event coordinator Lisa Thompson. The festivals success has sparked excitement about next years event, with many suggesting that Faketown could become a regional hub for arts and culture.",
"excerpt": "Faketowns annual festival saw record-breaking attendance, with thousands of people participating.",
"category": "Community Events",
"tags": ["festival", "community", "Faketown"],
"date": "2024-01-05",
"status": "publish",
"thumbnail": "demo-post-thumbnail-2.jpg"
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,67 @@
[
{
"id": 1,
"setName": "Font Awesome - Regular",
"setFamily": "Font Awesome",
"version": "6.7.1",
"fileName": "/font-awesome/v6.7.1/font-awesome-v6.7.1-regular-svgs.json",
"default": true
},
{
"id": 2,
"setName": "Font Awesome - Solid",
"setFamily": "Font Awesome",
"version": "6.7.1",
"fileName": "/font-awesome/v6.7.1/font-awesome-v6.7.1-solid-svgs.json"
},
{
"id": 3,
"setName": "Font Awesome - Brands",
"setFamily": "Font Awesome",
"version": "6.7.1",
"fileName": "/font-awesome/v6.7.1/font-awesome-v6.7.1-brands-svgs.json"
},
{
"id": 4,
"setName": "Material Icons - Baseline",
"setFamily": "Material Icons",
"version": "1.0.32",
"fileName": "/material-icons/v1.0.32/material-icons-v1.0.32-baseline-svgs.json"
},
{
"id": 5,
"setName": "Material Icons - Outline",
"setFamily": "Material Icons",
"version": "1.0.32",
"fileName": "/material-icons/v1.0.32/material-icons-v1.0.32-outline-svgs.json"
},
{
"id":6,
"setName": "Material Icons - Twotone",
"setFamily": "Material Icons",
"version": "1.0.32",
"fileName": "/material-icons/v1.0.32/material-icons-v1.0.32-twotone-svgs.json"
},
{
"id": 7,
"setName": "Material Icons - Sharp",
"setFamily": "Material Icons",
"version": "1.0.32",
"fileName": "/material-icons/v1.0.32/material-icons-v1.0.32-sharp-svgs.json"
},
{
"id": 8,
"setName": "Material Icons - Round",
"setFamily": "Material Icons",
"version": "1.0.32",
"fileName": "/material-icons/v1.0.32/material-icons-v1.0.32-round-svgs.json"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4206
assets/json/input.svg Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
# WordPress Coding Standards
# https://make.wordpress.org/core/handbook/coding-standards/
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
[*.{yml,yaml}]
indent_style = space
indent_size = 2

30
includes/blocks/lcp-button/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Coverage directory used by tools like istanbul
coverage
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Output of `npm pack`
*.tgz
# Output of `wp-scripts plugin-zip`
*.zip
# dotenv environment variables file
.env

View File

@ -0,0 +1,101 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/button",
"version": "0.1.0",
"title": "Button",
"category": "widgets",
"icon": "smiley",
"description": "A button for various functions or custom urls",
"example": {},
"supports": {
"html": false
},
"attributes": {
"buttonAction": {
"type": "string",
"default": "customUrl"
},
"customUrl": {
"type": "string",
"default": "#"
},
"buttonText": {
"type": "string",
"default": "Button Text"
},
"displayIcon": {
"type": "boolean",
"default": true
},
"iconSource": {
"type": "string",
"default": "manualSvgPath"
},
"iconSvgPath": {
"type": "string",
"default": ""
},
"iconSvgId": {
"type": "string",
"default": ""
},
"iconSvgViewbox": {
"type": "string",
"default": "0 0 510 510"
},
"popUpId": {
"type": "number"
},
"manualIconSvgPath": {
"type": "string",
"default": ""
},
"buttonHeight": {
"type": "string",
"default": ""
},
"buttonPadding": {
"type": "object",
"default": {
"extraLarge": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"large": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"medium": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"small": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
}
}
},
"iconHeight": {
"type": "string",
"default": ""
},
"iconWidth": {
"type": "string",
"default": ""
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}

View File

@ -0,0 +1 @@
.wp-block-create-block-button{border:1px dotted red}.lcp-button{background-color:var(--wp--preset--color--accent);border:none;color:#fff;cursor:pointer;display:inline-block;font-size:var(--wp--preset--font-size--small);font-weight:700;margin:auto 5px;padding:10px;text-decoration:none;white-space:nowrap}.lcp-button .lcp-icon{height:1.2em;margin-left:10px;max-height:100%;vertical-align:middle;width:auto;fill:#fff}

View File

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

View File

@ -0,0 +1 @@
.wp-block-create-block-button{border:1px dotted red}.lcp-button{background-color:var(--wp--preset--color--accent);border:none;color:#fff;cursor:pointer;display:inline-block;font-size:var(--wp--preset--font-size--small);font-weight:700;margin:auto 5px;padding:10px;text-decoration:none;white-space:nowrap}.lcp-button .lcp-icon{height:1.2em;margin-right:10px;max-height:100%;vertical-align:middle;width:auto;fill:#fff}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.wp-block-create-block-button{background-color:#21759b;color:#fff;padding:2px}

View File

@ -0,0 +1 @@
.wp-block-create-block-button{background-color:#21759b;color:#fff;padding:2px}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '39bd3d2fc52e7ff1ed91');

View File

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

View File

@ -0,0 +1,96 @@
import { __ } from '@wordpress/i18n';
import { BaseControl, __experimentalNumberControl as NumberControl, SelectControl, __experimentalHStack as HStack } from '@wordpress/components';
import { useState, useEffect } from 'react';
/**
* Control component with a number input and a select dropdown for units (px, rem, em, % etc.).
* Accepts a default value as a string (e.g., "15px", "10rem", etc.).
* Includes an optional 'auto' unit if includeAuto is true, and handles autoReturnsNull behavior.
*/
export function DimensionValueControl({ value = '10px', onChange, includeAuto = false, autoReturnsNull = false }) {
// Options for select control (CSS units) including 'auto' if enabled
const unitOptions = [
{ label: __('px'), value: 'px' },
{ label: __('%'), value: '%' },
{ label: __('em'), value: 'em' },
{ label: __('rem'), value: 'rem' },
{ label: __('vw'), value: 'vw' },
{ label: __('vh'), value: 'vh' },
...(includeAuto ? [{ label: __('auto'), value: 'auto' }] : []), // Add 'auto' option if includeAuto is true
];
// Parse the value string into a number and unit
const parseValue = (value) => {
const regex = /([0-9]+)([a-zA-Z%]+)?/; // Capture the number and the unit
const match = value.match(regex);
if (match) {
return {
numberValue: parseInt(match[1], 10), // Number part
unitValue: match[2] || 'px' // Unit part (default to 'px' if no unit found)
};
}
return { numberValue: 0, unitValue: 'px' }; // Fallback if invalid format
};
// Use the parsed value to set initial state
const { numberValue: initialNumber, unitValue: initialUnit } = parseValue(value);
const [numberValue, setNumberValue] = useState(initialNumber);
const [unitValue, setUnitValue] = useState(initialUnit);
// Combine the number and unit into a string like "15px"
const dimensionValue = `${numberValue}${unitValue}`;
// Handle number change
const onNumberChange = (newValue) => {
setNumberValue(newValue);
if (onChange && unitValue !== 'auto') {
onChange(`${newValue}${unitValue}`); // Pass updated value back to parent
}
};
// Handle unit change
const onUnitChange = (newUnit) => {
if (newUnit === 'auto') {
setNumberValue(null); // Reset the number value when 'auto' is selected
}
setUnitValue(newUnit);
if (onChange) {
const updatedValue = newUnit === 'auto'
? (autoReturnsNull ? null : 'auto')
: `${numberValue}${newUnit}`; // Pass 'auto' or null or updated value back to parent
onChange(updatedValue);
}
};
// Effect to handle when value prop changes (useful for syncing)
useEffect(() => {
const { numberValue, unitValue } = parseValue(value);
setNumberValue(numberValue);
setUnitValue(unitValue);
}, [value]);
return (
<BaseControl className="lcp-dimension-value-control">
<HStack>
{/* Number input control, disabled when 'auto' is selected */}
<NumberControl
className="lcp-number-control"
value={numberValue || ''}
onChange={onNumberChange}
min={0}
step={0.1}
spinControls={'none'}
disabled={unitValue === 'auto'} // Disable number input if 'auto' is selected
/>
{/* Select dropdown control for units */}
<SelectControl
className="lcp-select-control"
value={unitValue}
options={unitOptions}
onChange={onUnitChange}
/>
</HStack>
</BaseControl>
);
}

View File

@ -0,0 +1,59 @@
import { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { SelectControl } from '@wordpress/components';
export function IconSelectControl(props) {
const { iconSvgId, onIconChange } = props;
const [iconData, setIconData] = useState([]);
useEffect(() => {
const fetchIconData = async () => {
try {
const response = await fetch('/wp-json/lcp/v1/icons');
const data = await response.json();
if (Array.isArray(data) && data.length > 0) {
setIconData(data); // Set the fetched data directly
}
} catch (error) {
console.error('Error fetching icons:', error);
}
};
fetchIconData();
}, []);
const handleIconChange = (selectedIconId) => {
const selectedIcon = iconData.find(icon => icon.iconSvgId === selectedIconId);
if (selectedIcon && onIconChange) {
// Send both icon ID and path (SVG) to the parent component
onIconChange({
iconSvgId: selectedIcon.iconSvgId, // Pass icon ID to parent
iconSvgPath: selectedIcon.iconSvgPaths, // Pass icon paths to parent
viewbox: selectedIcon.selectedIconViewbox // Pass the viewbox to parent
});
console.log("Selected Icon ID:", selectedIcon.iconSvgId); // Debugging output
console.log("Selected Icon Path:", selectedIcon.iconSvgPaths); // Debugging output
console.log("Selected Icon Viewbox:", selectedIcon.selectedIconViewbox); // Debugging output
}
};
if (iconData.length === 0) {
return <p>{__('Loading icons...', 'lcp')}</p>; // Loading state
}
const iconOptions = iconData.map((icon) => ({
value: icon.iconSvgId, // Use icon ID as value for the SelectControl
label: icon.name, // Directly use the icon's name as the label
}));
return (
<SelectControl
label={__('Select Icon', 'lcp')}
value={iconSvgId} // Current selected icon ID
options={iconOptions}
onChange={handleIconChange} // Handle icon change
/>
);
}

View File

@ -0,0 +1,103 @@
import { __ } from '@wordpress/i18n';
import { BaseControl, Button, RangeControl, __experimentalHStack as HStack, __experimentalVStack as VStack } from '@wordpress/components';
import { DimensionValueControl } from './DimensionValueControl';
/**
* Padding Control component to manage padding values for different screen sizes.
*/
export function PaddingControl() {
return (
<BaseControl className="lcp-padding-control">
{/* Padding label and Unlink button */}
<HStack>
<span>{__('Padding')}</span>
<Button
variant="secondary"
aria-label={__('Unlink sides')}
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="M10 17.389H8.444A5.194 5.194 0 1 1 8.444 7H10v1.5H8.444a3.694 3.694 0 0 0 0 7.389H10v1.5ZM14 7h1.556a5.194 5.194 0 0 1 0 10.39H14v-1.5h1.556a3.694 3.694 0 0 0 0-7.39H14V7Zm-4.5 6h5v-1.5h-5V13Z"></path>
</svg>
</Button>
</HStack>
{/* Extra Large Padding Controls */}
{/* Will update all padding values for all screen sizes if updateAllScreenSizes is true */}
{/* Top and Bottom HStack */}
<HStack style={{ flex: 1 }}>
{/* Top and Bottom Icon */}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" className="spacing-sizes-control__icon" aria-hidden="true" focusable="false">
<path d="m7.5 6h9v-1.5h-9zm0 13.5h9v-1.5h-9zm-3-3h1.5v-9h-1.5zm13.5-9v9h1.5v-9z" style={{ opacity: 0.25 }}></path>
<path d="m7.5 6h9v-1.5h-9z"></path>
<path d="m7.5 19.5h9v-1.5h-9z"></path>
</svg>
{/* RangeControl wrapped in HStack with flex: 1 applied to its parent */}
<HStack style={{ flex: 1 }}>
<DimensionValueControl/>
<RangeControl
withInputField={false}
value={10} // Placeholder value
onChange={() => {}}
min={0}
max={50}
/>
</HStack>
{/* Custom Padding Button */}
<Button
style={{padding:0,background:'none',color:'var(--wp-components-color-foreground)'} }
variant="primary"
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="m19 7.5h-7.628c-.3089-.87389-1.1423-1.5-2.122-1.5-.97966 0-1.81309.62611-2.12197 1.5h-2.12803v1.5h2.12803c.30888.87389 1.14231 1.5 2.12197 1.5.9797 0 1.8131-.62611 2.122-1.5h7.628z"></path>
<path d="m19 15h-2.128c-.3089-.8739-1.1423-1.5-2.122-1.5s-1.8131.6261-2.122 1.5h-7.628v1.5h7.628c.3089.8739 1.1423 1.5 2.122 1.5s1.8131-.6261 2.122-1.5h2.128z"></path>
</svg>
</Button>
</HStack>
{/* Left and Right HStack */}
<HStack style={{ flex: 1 }}>
{/* Left and Right Icon */}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" className="spacing-sizes-control__icon" aria-hidden="true" focusable="false">
<path d="m7.5 6h9v-1.5h-9zm0 13.5h9v-1.5h-9zm-3-3h1.5v-9h-1.5zm13.5-9v9h1.5v-9z" style={{ opacity: 0.25 }}></path>
<path d="m7.5 6h9v-1.5h-9z"></path>
<path d="m4.5 7.5v9h1.5v-9z"></path>
<path d="m18 7.5v9h1.5v-9z"></path>
</svg>
{/* RangeControl wrapped in HStack with flex: 1 applied to its parent */}
<RangeControl
withInputField={false}
value={10} // Placeholder value
onChange={() => {}}
min={0}
max={50}
/>
{/* Custom Padding Button */}
<Button
variant="primary"
onClick={() => {}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="m19 7.5h-7.628c-.3089-.87389-1.1423-1.5-2.122-1.5-.97966 0-1.81309.62611-2.12197 1.5h-2.12803v1.5h2.12803c.30888.87389 1.14231 1.5 2.12197 1.5.9797 0 1.8131-.62611 2.122-1.5h7.628z"></path>
<path d="m19 15h-2.128c-.3089-.8739-1.1423-1.5-2.122-1.5s-1.8131.6261-2.122 1.5h-7.628v1.5h7.628c.3089.8739 1.1423 1.5 2.122 1.5s1.8131-.6261 2.122-1.5h2.128z"></path>
</svg>
</Button>
</HStack>
{/* Additional controls can be added here in a VStack */}
<VStack>
{/* Placeholder for additional components */}
</VStack>
</BaseControl>
);
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Plugin Name: Button
* Description: Example block scaffolded with Create Block tool.
* Requires at least: 6.6
* Requires PHP: 7.2
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: button
*
* @package CreateBlock
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function create_block_button_block_init() {
register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'create_block_button_block_init' );
/* REST API */
function register_icons_rest_api_endpoint() {
register_rest_route( 'lcp/v1', '/icons', array(
'methods' => 'GET',
'callback' => 'get_icons_data_from_db',
'permission_callback' => '__return_true', // Public access; modify this if real permissions needed
) );
}
add_action( 'rest_api_init', 'register_icons_rest_api_endpoint' );
function get_icons_data_from_db() {
global $wpdb;
// Query the lcp_icons table
$results = $wpdb->get_results(
"SELECT icon_id, icon_name, paths, viewbox FROM {$wpdb->prefix}lcp_icons"
);
// If no icons are found, return an empty array
if ( empty( $results ) ) {
return [];
}
// Format the results for the frontend
$icons = array_map( function( $icon ) {
return [
'iconSvgId' => $icon->icon_id, // Icon ID
'iconSvgPaths' => $icon->paths, // SVG paths (can be multiple)
'selectedIconViewbox' => $icon->viewbox, // ViewBox for the SVG
'name' => $icon->icon_name, // Add name field
];
}, $results );
return $icons;
}

View File

@ -0,0 +1,20 @@
{
"name": "button",
"version": "0.1.0",
"description": "Example block scaffolded with Create Block tool.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"format": "wp-scripts format",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"packages-update": "wp-scripts packages-update",
"plugin-zip": "wp-scripts plugin-zip",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^30.6.0"
}
}

View File

@ -0,0 +1,55 @@
=== Button ===
Contributors: The WordPress Contributors
Tags: block
Tested up to: 6.6
Stable tag: 0.1.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Example block scaffolded with Create Block tool.
== Description ==
This is the long description. No limit, and you can use Markdown (as well as in the following sections).
For backwards compatibility, if this section is missing, the full length of the short description will be used, and
Markdown parsed.
== Installation ==
This section describes how to install the plugin and get it working.
e.g.
1. Upload the plugin files to the `/wp-content/plugins/button` directory, or install the plugin through the WordPress plugins screen directly.
1. Activate the plugin through the 'Plugins' screen in WordPress
== Frequently Asked Questions ==
= A question that someone might have =
An answer to that question.
= What about foo bar? =
Answer to foo bar dilemma.
== Screenshots ==
1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is taken from
the /assets directory or the directory that contains the stable readme.txt (tags or trunk). Screenshots in the /assets
directory take precedence. For example, `/assets/screenshot-1.png` would win over `/tags/4.3/screenshot-1.png`
(or jpg, jpeg, gif).
2. This is the second screen shot
== Changelog ==
= 0.1.0 =
* Release
== Arbitrary section ==
You may provide arbitrary sections, in the same format as the ones above. This may be of use for extremely complicated
plugins where more information needs to be conveyed that doesn't fit into the categories of "description" or
"installation." Arbitrary sections will be shown below the built-in sections outlined above.

View File

@ -0,0 +1,101 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/button",
"version": "0.1.0",
"title": "Button",
"category": "widgets",
"icon": "smiley",
"description": "A button for various functions or custom urls",
"example": {},
"supports": {
"html": false
},
"attributes": {
"buttonAction": {
"type": "string",
"default": "customUrl"
},
"customUrl": {
"type": "string",
"default": "#"
},
"buttonText": {
"type": "string",
"default": "Button Text"
},
"displayIcon": {
"type": "boolean",
"default": true
},
"iconSource": {
"type": "string",
"default": "manualSvgPath"
},
"iconSvgPath": {
"type": "string",
"default": ""
},
"iconSvgId": {
"type": "string",
"default": ""
},
"iconSvgViewbox": {
"type": "string",
"default": "0 0 510 510"
},
"popUpId": {
"type": "number"
},
"manualIconSvgPath":{
"type": "string",
"default": ""
},
"buttonHeight":{
"type": "string",
"default": ""
},
"buttonPadding":{
"type": "object",
"default": {
"extraLarge": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"large": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"medium": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
},
"small": {
"top": "10px",
"right": "10px",
"bottom": "10px",
"left": "10px"
}
}
},
"iconHeight": {
"type": "string",
"default": ""
},
"iconWidth": {
"type": "string",
"default": ""
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}

View File

@ -0,0 +1,145 @@
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl, TextControl, TextareaControl, ToggleControl } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';
import './editor.scss';
import { IconSelectControl } from '../components/IconSelectControl';
import { PaddingControl } from '../components/PaddingControl';
import { DimensionValueControl } from '../components/DimensionValueControl';
export default function Edit(props) {
const { attributes, setAttributes } = props;
const { buttonAction, iconHeight, buttonText, iconSvgId, iconSvgPath, iconSvgViewbox, displayIcon, iconSource, customUrl, buttonPadding } = attributes;
// Handle icon selection from dropdown
const handleIconChanges = (selectedIcon) => {
console.log("Icon changed:", selectedIcon);
if (selectedIcon && selectedIcon.iconSvgPath) {
setAttributes({
iconSvgPath: selectedIcon.iconSvgPath,
iconSvgId: selectedIcon.iconSvgId,
iconSvgViewbox: selectedIcon.viewbox
});
}
};
const handleToggleChange = (value) => {
setAttributes({ displayIcon: value });
};
const handleCustomUrlChange = (value) => {
setAttributes({ customUrl: value });
};
const handlePaddingChange = (value) => {
if (typeof value === 'number') {
value = `${value}px`;
}
setAttributes({ buttonPadding: value });
};
// Handle changes to icon height (from the DimensionValueControl)
const handleIconHeightChange = (newHeight) => {
console.log("New icon height:", newHeight); // Debugging line
setAttributes({ iconHeight: newHeight }); // Update the iconHeight attribute
};
const iconSourceOptions = [
{ value: 'manualSvgPath', label: 'SVG Path' },
{ value: 'iconSelector', label: 'Icon Library' },
];
const buttonActionOptions = [
{ value: 'customUrl', label: 'Custom URL' },
{ value: 'showLoginForm', label: 'Show Login Form' },
{ value: 'logOut', label: 'Open Popup' },
{ value: 'shareCurrentPost', label: 'Share Current Post' },
{ value: 'displaySidecontent', label: 'Display Sidecontent' }
];
return (
<>
<InspectorControls>
<PanelBody title={__("Button Settings")}>
<PaddingControl
onChange={handlePaddingChange}
/>
<SelectControl
label={__("Button Action")}
value={buttonAction}
options={buttonActionOptions}
onChange={(value) => setAttributes({ buttonAction: value })}
/>
{buttonAction === 'customUrl' && (
<TextControl
label={__("Custom URL")}
value={customUrl}
onChange={handleCustomUrlChange}
/>
)}
<TextControl
label={__("Button Text")}
value={buttonText}
onChange={(value) => setAttributes({ buttonText: value })}
/>
<ToggleControl
label="Display Icon"
checked={displayIcon}
onChange={handleToggleChange}
/>
<DimensionValueControl
value={iconHeight} // Pass the current iconHeight to the DimensionValueControl
onChange={(newHeight) => setAttributes({ iconHeight: newHeight })} // Update iconHeight when it changes
includeAuto={true}
autoReturnsNull={true}
/>
{displayIcon && (
<>
<SelectControl
label={__("Icon Source")}
value={iconSource}
options={iconSourceOptions}
onChange={(value) => setAttributes({ iconSource: value })}
/>
{iconSource === 'manualSvgPath' && (
<TextareaControl
label="Icon SVG Path"
value={iconSvgPath}
onChange={(value) => setAttributes({ iconSvgPath: value })}
/>
)}
{iconSource === 'iconSelector' && (
<IconSelectControl
iconSvgId={iconSvgId}
onIconChange={handleIconChanges}
/>
)}
</>
)}
</PanelBody>
</InspectorControls>
<div {...useBlockProps()}>
{buttonAction === 'customUrl' ? (
<a href={customUrl} className="lcp-button" style={{ padding: buttonPadding || '10px' }}>
{displayIcon && iconSvgPath && (
<svg style={{ height: iconHeight }} className="lcp-icon" xmlns="http://www.w3.org/2000/svg" viewBox={iconSvgViewbox || "0 0 576 576"} dangerouslySetInnerHTML={{ __html: iconSvgPath }} />
)}
<span className="lcp-button-text">
{buttonText || 'Button'}
</span>
</a>
) : (
<button className="lcp-button" style={{ padding: buttonPadding || '10px' }}>
{displayIcon && iconSvgPath && (
<svg style={{ height: iconHeight }} className="lcp-icon" xmlns="http://www.w3.org/2000/svg" viewBox={iconSvgViewbox || "0 0 576 576"} dangerouslySetInnerHTML={{ __html: iconSvgPath }} />
)}
<span className="lcp-button-text">
{buttonText || 'Button'}
</span>
</button>
)}
</div>
</>
);
}

View File

@ -0,0 +1,32 @@
/**
* The following styles get applied inside the editor only.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-button {
border: 1px dotted #f00;
}
.lcp-button {
display: inline-block;
background-color: var(--wp--preset--color--accent);
border: none;
color: #fff;
text-decoration: none;
padding: 10px;
font-weight: bold;
font-size: var(--wp--preset--font-size--small);
white-space: nowrap;
cursor: pointer;
margin:auto 5px;
}
.lcp-button .lcp-icon {
height: 1.2em;
max-height:100%;
width: auto;
vertical-align: middle; /* Aligns the icon with the text vertically */
margin-right: 10px; /* Space between the icon and the text */
fill:white;
}

View File

@ -0,0 +1,39 @@
/**
* Registers a new block provided a unique name and an object defining its behavior.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* All files containing `style` keyword are bundled together. The code used
* gets applied both to the front of your site and to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './style.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
import save from './save';
import metadata from './block.json';
/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
/**
* @see ./save.js
*/
save,
} );

View File

@ -0,0 +1,35 @@
import { useBlockProps } from '@wordpress/block-editor';
export default function save(props) {
const { attributes } = props;
const {displayIcon, buttonText, buttonPadding,iconHeight,iconSvgPath, iconSvgViewbox, buttonAction, customUrl } = attributes; // Destructure buttonText, iconSvgPath, and iconSvgViewbox
// Get the block props for the button
const blockProps = useBlockProps.save();
// Conditionally render the link or button based on buttonAction
return (
<>
{buttonAction === 'customUrl' ? (
<a href={customUrl} className="lcp-button" style={{ padding: buttonPadding || '10px' }}>
{displayIcon && iconSvgPath && (
<svg style={{ height: iconHeight }} className="lcp-icon" xmlns="http://www.w3.org/2000/svg" viewBox={iconSvgViewbox || "0 0 576 576"} dangerouslySetInnerHTML={{ __html: iconSvgPath }} />
)}
<span className="lcp-button-text">
{buttonText || 'Button'}
</span>
</a>
) : (
<button className="lcp-button" style={{ padding: buttonPadding || '10px' }}>
{displayIcon && iconSvgPath && (
<svg style={{ height: iconHeight }} className="lcp-icon" xmlns="http://www.w3.org/2000/svg" viewBox={iconSvgViewbox || "0 0 576 576"} dangerouslySetInnerHTML={{ __html: iconSvgPath }} />
)}
<span className="lcp-button-text">
{buttonText || 'Button'}
</span>
</button>
)}
</>
);
}

View File

@ -0,0 +1,13 @@
/**
* The following styles get applied both on the front of your site
* and in the editor.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-button {
background-color: #21759b;
color: #fff;
padding: 2px;
}

View File

@ -0,0 +1,25 @@
/**
* Use this file for JavaScript code that you want to run in the front-end
* on posts/pages that contain this block.
*
* When this file is defined as the value of the `viewScript` property
* in `block.json` it will be enqueued on the front end of the site.
*
* Example:
*
* ```js
* {
* "viewScript": "file:./view.js"
* }
* ```
*
* If you're not making any changes to this file because your project doesn't need any
* JavaScript running in the front-end, then you should delete this file and remove
* the `viewScript` property from `block.json`.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
*/
/* eslint-disable no-console */
console.log( 'Hello World! (from create-block-button block)' );
/* eslint-enable no-console */

View File

@ -0,0 +1,18 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
# WordPress Coding Standards
# https://make.wordpress.org/core/handbook/coding-standards/
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
[*.{yml,yaml}]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Coverage directory used by tools like istanbul
coverage
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Output of `npm pack`
*.tgz
# Output of `wp-scripts plugin-zip`
*.zip
# dotenv environment variables file
.env

View File

@ -0,0 +1,141 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/dynamic-container",
"version": "0.1.0",
"title": "Dynamic Container",
"category": "widgets",
"icon": "smiley",
"description": "A general purpose container that uses dynamic rendering",
"example": {},
"supports": {
"anchor": true,
"align": [
"left",
"right",
"wide",
"full"
],
"background": {
"backgroundImage": true,
"backgroundSize": true,
"__experimentalDefaultControls": {
"backgroundImage": true
}
},
"color": {
"gradients": true,
"background": true,
"link": true,
"__experimentalDefaultControls": {
"background": true,
"text": true
}
}
},
"attributes": {
"maxWidthExtraLarge": {
"type": "string"
},
"maxWidthLarge": {
"type": "string"
},
"maxWidthMedium": {
"type": "string"
},
"maxWidthSmall": {
"type": "string"
},
"minHeightExtraLarge": {
"type": "string"
},
"minHeightLarge": {
"type": "string"
},
"minHeightMedium": {
"type": "string"
},
"minHeightSmall": {
"type": "string"
},
"isBox": {
"type": "boolean",
"default": false
},
"backgroundColor": {
"type": "string"
},
"entranceAnimation": {
"type": "string"
},
"exitAnimation": {
"type": "string"
},
"overflow": {
"type": "string"
},
"padding": {
"type": "object",
"default": {
"extraLarge": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"large": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"medium": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"small": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
}
}
},
"margin": {
"type": "object",
"default": {
"extraLarge": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"large": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"medium": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"small": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
}
}
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n'), 'version' => '04e9c02e00cfeee61658');

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{background-color:#21759b;color:#fff;padding:2px}.lcp-dynamic-container.lcp-has-background-image{position:relative;width:100%}.lcp-dynamic-container.lcp-has-background-image>.lcp-background-image{height:100%;right:0;-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;position:absolute;top:0;width:100%}

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{background-color:#21759b;color:#fff;padding:2px}.lcp-dynamic-container.lcp-has-background-image{position:relative;width:100%}.lcp-dynamic-container.lcp-has-background-image>.lcp-background-image{height:100%;left:0;-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;position:absolute;top:0;width:100%}

View File

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

View File

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

View File

@ -0,0 +1,221 @@
<?php /**
* Function to generate random class and add dynamic padding styles
*/
if (!function_exists('lcp_random_string')) {
function lcp_random_string($length = 8, $css_compliant = false, $include_character_types = '') {
// Define character sets
$lowercase = 'abcdefghijklmnopqrstuvwxyz';
$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$numbers = '0123456789';
$special_chars = '!@#$%^&*()_+-=[]{}|;:,.<>?';
$all_chars = $lowercase . $uppercase . $numbers . $special_chars;
// Default to using all characters if no specific types are provided
if (empty($include_character_types)) {
$include_character_types = 'all';
}
// Build the allowed character set
$char_set = '';
if ($include_character_types === 'all') {
// Include everything
$char_set = $all_chars;
} else {
// Add specific types requested
if (strpos($include_character_types, 'lowercase') !== false) {
$char_set .= $lowercase;
}
if (strpos($include_character_types, 'uppercase') !== false) {
$char_set .= $uppercase;
}
if (strpos($include_character_types, 'numbers') !== false) {
$char_set .= $numbers;
}
if (strpos($include_character_types, 'special') !== false) {
$char_set .= $special_chars;
}
}
// Ensure that the string is valid for CSS (starts with a letter and can only include letters, digits, hyphens, or underscores)
$css_char_set = $lowercase . $uppercase . $numbers . '-_';
// Random string generation logic
$random_string = '';
$first_char = '';
// If CSS compliant, start with a letter (either lowercase or uppercase)
if ($css_compliant) {
// Ensure the first character is a letter (CSS compliant start)
$first_char = $lowercase[rand(0, strlen($lowercase) - 1)];
$random_string .= $first_char;
$length--; // Decrease length by 1 as we already added the first char
}
// Fill the rest of the string with random characters from the valid set (CSS compliant)
for ($i = 0; $i < $length; $i++) {
// Use only characters that are CSS compliant (letters, digits, hyphens, or underscores)
$random_string .= $css_char_set[rand(0, strlen($css_char_set) - 1)];
}
// Return the string
return $random_string;
}
}
function render_lcp_dynamic_container( $attributes, $content ) {
// Debugging: Check the passed attributes
//var_dump($attributes);
// Generate a random class name (optional, could be customized)
$random_class = lcp_random_string(12,true);
// Get the padding and backgroundColor attributes
$padding = isset( $attributes['padding'] ) ? $attributes['padding'] : array();
$background_color = isset( $attributes['backgroundColor'] ) ? $attributes['backgroundColor'] : '#ffffff'; // Default color
// Debugging: Check padding and background color
error_log(print_r($padding, true));
error_log(print_r($background_color, true));
// Check if all padding values are the same across all sizes
$same_padding = true;
$padding_top = $padding['extraLarge']['top'];
$padding_right = $padding['extraLarge']['right'];
$padding_bottom = $padding['extraLarge']['bottom'];
$padding_left = $padding['extraLarge']['left'];
// Compare the padding for all other sizes (large, medium, small)
foreach ( ['large', 'medium', 'small'] as $size ) {
if (
$padding[$size]['top'] !== $padding_top ||
$padding[$size]['right'] !== $padding_right ||
$padding[$size]['bottom'] !== $padding_bottom ||
$padding[$size]['left'] !== $padding_left
) {
$same_padding = false;
break;
}
}
// Prepare the inline style or media queries
$style = '';
// Add background-color to the inline style
$style .= sprintf( 'background-color: %s;', esc_attr( $background_color ) );
if ( $same_padding ) {
// If all padding values are the same, use inline style
$style .= sprintf(
'padding-top: %s; padding-right: %s; padding-bottom: %s; padding-left: %s;',
esc_attr( $padding_top ),
esc_attr( $padding_right ),
esc_attr( $padding_bottom ),
esc_attr( $padding_left )
);
} else {
// If padding is different, generate media queries for different sizes
$style .= sprintf(
'@media (min-width: 1200px) { .%s { padding-top: %s; padding-right: %s; padding-bottom: %s; padding-left: %s; } }',
esc_attr( $random_class ),
esc_attr( $padding['extraLarge']['top'] ),
esc_attr( $padding['extraLarge']['right'] ),
esc_attr( $padding['extraLarge']['bottom'] ),
esc_attr( $padding['extraLarge']['left'] )
);
$style .= sprintf(
'@media (min-width: 1024px) { .%s { padding-top: %s; padding-right: %s; padding-bottom: %s; padding-left: %s; } }',
esc_attr( $random_class ),
esc_attr( $padding['large']['top'] ),
esc_attr( $padding['large']['right'] ),
esc_attr( $padding['large']['bottom'] ),
esc_attr( $padding['large']['left'] )
);
$style .= sprintf(
'@media (min-width: 768px) { .%s { padding-top: %s; padding-right: %s; padding-bottom: %s; padding-left: %s; } }',
esc_attr( $random_class ),
esc_attr( $padding['medium']['top'] ),
esc_attr( $padding['medium']['right'] ),
esc_attr( $padding['medium']['bottom'] ),
esc_attr( $padding['medium']['left'] )
);
$style .= sprintf(
'@media (max-width: 767px) { .%s { padding-top: %s; padding-right: %s; padding-bottom: %s; padding-left: %s; } }',
esc_attr( $random_class ),
esc_attr( $padding['small']['top'] ),
esc_attr( $padding['small']['right'] ),
esc_attr( $padding['small']['bottom'] ),
esc_attr( $padding['small']['left'] )
);
}
// Generate the <style> tag with the final CSS (either inline or media queries)
$style_tag = '';
if ( ! empty( $style ) ) {
$style_tag = sprintf( '<style>.%s { %s }</style>', esc_attr( $random_class ), $style );
}
// Set the post thumbnail
if ( has_post_thumbnail() ) {
// Get the post thumbnail URL for different sizes
$full_size_url = get_the_post_thumbnail_url( get_the_ID(), 'full' );
$medium_size_url = get_the_post_thumbnail_url( get_the_ID(), 'medium' );
$large_size_url = get_the_post_thumbnail_url( get_the_ID(), 'large' );
// Generate the <picture> element with <source> and <img> tags for responsiveness
$post_thumb = '<picture class="lcp-background-image">';
// Add source for large image (for screens >= 1200px)
$post_thumb .= '<source media="(min-width: 1200px)" srcset="' . esc_url( $large_size_url ) . '">';
// Add source for medium image (for screens >= 768px)
$post_thumb .= '<source media="(min-width: 768px)" srcset="' . esc_url( $medium_size_url ) . '">';
// Add fallback image (for smaller screens or if no match for media queries)
$post_thumb .= '<img src="' . esc_url( $full_size_url ) . '" alt="Responsive Background Image" class="responsive-background">';
$post_thumb .= '</picture>';
}
$has_background_image = (2 + 2 == 4) ? 'lcp-has-background-image' : '';
// Output the content wrapped in the div with the random class and padding styles
return $style_tag . sprintf(
'<div class="lcp-dynamic-container %s %s">%s%s</div>',
esc_attr( $random_class ), // The random class
esc_attr( $has_background_image), // Conditionally add 'lcp-has-background-image' class
$post_thumb, // Add the $post_thumb (responsive image) here,
$content // Keep the original content
);
}
/**
* Function to return the media query breakpoint based on the size
*/
function get_media_query_breakpoint( $size ) {
// Define breakpoints for each size
$breakpoints = array(
'extraLarge' => '1200px',
'large' => '1024px',
'medium' => '768px',
'small' => '480px',
);
return isset( $breakpoints[$size] ) ? $breakpoints[$size] : '480px';
}
/**
* Function to initialize the block
*/
function lcp_dynamic_container_block_init() {
register_block_type( __DIR__ . '/build', array(
'render_callback' => 'render_lcp_dynamic_container', // Add the render callback here
));
}
add_action( 'init', 'lcp_dynamic_container_block_init' );

View File

@ -0,0 +1,20 @@
{
"name": "lcp-viewport",
"version": "0.1.0",
"description": "Example block scaffolded with Create Block tool.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"format": "wp-scripts format",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"packages-update": "wp-scripts packages-update",
"plugin-zip": "wp-scripts plugin-zip",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^30.4.0"
}
}

View File

@ -0,0 +1,55 @@
=== Lcp Viewport ===
Contributors: The WordPress Contributors
Tags: block
Tested up to: 6.6
Stable tag: 0.1.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Example block scaffolded with Create Block tool.
== Description ==
This is the long description. No limit, and you can use Markdown (as well as in the following sections).
For backwards compatibility, if this section is missing, the full length of the short description will be used, and
Markdown parsed.
== Installation ==
This section describes how to install the plugin and get it working.
e.g.
1. Upload the plugin files to the `/wp-content/plugins/lcp-viewport` directory, or install the plugin through the WordPress plugins screen directly.
1. Activate the plugin through the 'Plugins' screen in WordPress
== Frequently Asked Questions ==
= A question that someone might have =
An answer to that question.
= What about foo bar? =
Answer to foo bar dilemma.
== Screenshots ==
1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is taken from
the /assets directory or the directory that contains the stable readme.txt (tags or trunk). Screenshots in the /assets
directory take precedence. For example, `/assets/screenshot-1.png` would win over `/tags/4.3/screenshot-1.png`
(or jpg, jpeg, gif).
2. This is the second screen shot
== Changelog ==
= 0.1.0 =
* Release
== Arbitrary section ==
You may provide arbitrary sections, in the same format as the ones above. This may be of use for extremely complicated
plugins where more information needs to be conveyed that doesn't fit into the categories of "description" or
"installation." Arbitrary sections will be shown below the built-in sections outlined above.

View File

@ -0,0 +1,136 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/dynamic-container",
"version": "0.1.0",
"title": "Dynamic Container",
"category": "widgets",
"icon": "smiley",
"description": "A general purpose container that uses dynamic rendering",
"example": {},
"supports": {
"anchor": true,
"align": [ "left", "right", "wide", "full" ],
"background": {
"backgroundImage": true,
"backgroundSize": true,
"__experimentalDefaultControls": {
"backgroundImage": true
}
},
"color": {
"gradients": true,
"background": true,
"link": true,
"__experimentalDefaultControls": {
"background": true,
"text": true
}
}
},
"attributes": {
"maxWidthExtraLarge": {
"type": "string"
},
"maxWidthLarge": {
"type": "string"
},
"maxWidthMedium": {
"type": "string"
},
"maxWidthSmall": {
"type": "string"
},
"minHeightExtraLarge": {
"type": "string"
},
"minHeightLarge": {
"type": "string"
},
"minHeightMedium": {
"type": "string"
},
"minHeightSmall": {
"type": "string"
},
"isBox": {
"type": "boolean",
"default": false
},
"backgroundColor": {
"type": "string"
},
"entranceAnimation": {
"type": "string"
},
"exitAnimation": {
"type": "string"
},
"overflow": {
"type": "string"
},
"padding": {
"type": "object",
"default": {
"extraLarge": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"large": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"medium": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
},
"small": {
"top": "0",
"right": "0",
"bottom": "0",
"left": "0"
}
}
},
"margin": {
"type": "object",
"default": {
"extraLarge": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"large": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"medium": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
},
"small": {
"top": "12px",
"right": "0",
"bottom": "12px",
"left": "0"
}
}
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"}

View File

@ -0,0 +1,211 @@
import { useBlockProps, InnerBlocks, InspectorControls } from '@wordpress/block-editor';
import { __experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOption as ToggleGroupControlOption, __experimentalUnitControl as UnitControl, BaseControl, ToggleControl, ColorPalette, Popover, Button } from '@wordpress/components';
import { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
/**
* 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.
*
* @param {Object} props - The props for the block.
* @return {Element} Element to render.
*/
export default function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
const { padding, maxWidthLarge, maxWidthMedium, maxWidthSmall, minHeightLarge, minHeightMedium, minHeightSmall, backgroundColor } = attributes; // Destructure necessary attributes
// State for independent padding toggle
const [isIndependentLarge, setIsIndependentLarge] = useState(false);
// Toggle to show or hide the color picker popover
const [showColorPopover, setShowColorPopover] = useState(false);
const [selectedColor, setSelectedColor] = useState(backgroundColor || '#ffffff'); // Default color
// Toggle padding for individual sizes
const handleToggleChange = () => {
setIsIndependentLarge(!isIndependentLarge);
};
// Update padding for all sizes
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 }
};
setAttributes({ padding: newPadding });
};
// Handle individual padding changes
const handlePaddingChange = (size, position, newValue) => {
const updatedPadding = { ...padding };
updatedPadding[size][position] = newValue;
setAttributes({ padding: updatedPadding });
};
// Handle color change
const handleColorChange = (color) => {
setSelectedColor(color);
setAttributes({ backgroundColor: color }); // Update background color
};
// Handle maxWidth change for different sizes
const handleMaxWidthChange = (value, size) => {
setAttributes({ [`maxWidth${size}`]: value });
};
// Handle minHeight change for different sizes
const handleMinHeightChange = (value, size) => {
setAttributes({ [`minHeight${size}`]: value });
};
useEffect(() => {
console.log("Updated attributes:", attributes);
}, [attributes]);
return (
<>
<InspectorControls>
<ToggleGroupControl
label="my label"
value="vertical"
isBlock
__nextHasNoMarginBottom
__next40pxDefaultSize
>
<ToggleGroupControlOption value="horizontal" label="Horizontal" />
<ToggleGroupControlOption value="vertical" label="Vertical" />
</ToggleGroupControl>
{/* BaseControl for padding */}
<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={padding.extraLarge?.top || '10px'}
onChange={(newValue) => handlePaddingChange('extraLarge', 'top', newValue)}
/>
</fieldset>
<fieldset>
<legend>Left</legend>
<UnitControl
value={padding.extraLarge?.left || '10px'}
onChange={(newValue) => handlePaddingChange('extraLarge', 'left', newValue)}
/>
</fieldset>
<fieldset>
<legend>Right</legend>
<UnitControl
value={padding.extraLarge?.right || '10px'}
onChange={(newValue) => handlePaddingChange('extraLarge', 'right', newValue)}
/>
</fieldset>
<fieldset style={{ gridColumn: 'span 2', width: '116px' }}>
<legend>Bottom</legend>
<UnitControl
value={padding.extraLarge?.bottom || '10px'}
onChange={(newValue) => handlePaddingChange('extraLarge', 'bottom', newValue)}
/>
</fieldset>
</div>
) : (
<UnitControl
label="Padding Value"
value={999} // You can replace this with an attribute value
onChange={updatePaddingForAllSizes}
/>
)}
</BaseControl>
{/* Max Width Controls */}
<BaseControl label="Max Width">
<div style={{ display: 'flex', flexDirection: 'column' }}>
<UnitControl
label="Large"
value={maxWidthLarge}
onChange={(value) => handleMaxWidthChange(value, 'Large')}
defaultUnit="px"
/>
<UnitControl
label="Medium"
value={maxWidthMedium}
onChange={(value) => handleMaxWidthChange(value, 'Medium')}
defaultUnit="px"
/>
<UnitControl
label="Small"
value={maxWidthSmall}
onChange={(value) => handleMaxWidthChange(value, 'Small')}
defaultUnit="px"
/>
</div>
</BaseControl>
{/* Min Height Controls */}
<BaseControl label="Min Height">
<div style={{ display: 'flex', flexDirection: 'column' }}>
<UnitControl
label="Large"
value={minHeightLarge}
onChange={(value) => handleMinHeightChange(value, 'Large')}
defaultUnit="px"
/>
<UnitControl
label="Medium"
value={minHeightMedium}
onChange={(value) => handleMinHeightChange(value, 'Medium')}
defaultUnit="px"
/>
<UnitControl
label="Small"
value={minHeightSmall}
onChange={(value) => handleMinHeightChange(value, 'Small')}
defaultUnit="px"
/>
</div>
</BaseControl>
{/* Background color picker */}
<BaseControl label={__('Background Color')}>
<div>
<Button onClick={() => setShowColorPopover(!showColorPopover)}>
{__('Choose Background Color')}
</Button>
{showColorPopover && (
<Popover position="bottom center">
<ColorPalette
value={selectedColor}
onChange={handleColorChange}
colors={[{ name: "Primary", slug: "primary", color: "#3E5062" }, { name: "Secondary", slug: "secondary", color: "#2B3843" }, { name: "Light Color - 1", slug: "light-color-1", color: "#ECF0F5" }, { name: "Dark Gray", slug: "dark-gray", color: "#333333" }, { name: "Accent", slug: "accent", color: "#61FFB6" }]}
disableCustomColors={false}
/>
</Popover>
)}
{/* Color indicator */}
<div style={{ backgroundColor: selectedColor, width: '30px', height: '30px', marginTop: '10px', borderRadius: '50%' }}></div>
</div>
</BaseControl>
</InspectorControls>
<div {...blockProps} style={{ maxWidth: maxWidthLarge, minHeight: minHeightLarge }}>
<InnerBlocks />
</div>
</>
);
}

View File

@ -0,0 +1,9 @@
/**
* The following styles get applied inside the editor only.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-lcp-viewport {
border: 1px dotted #f00;
}

View File

@ -0,0 +1,39 @@
/**
* Registers a new block provided a unique name and an object defining its behavior.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* All files containing `style` keyword are bundled together. The code used
* gets applied both to the front of your site and to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './style.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
import save from './save';
import metadata from './block.json';
/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
/**
* @see ./save.js
*/
save,
} );

View File

@ -0,0 +1,4 @@
import { InnerBlocks } from '@wordpress/block-editor';
export default function save() {
return (<InnerBlocks.Content/>); // No content is saved in the database for dynamic blocks
}

View File

@ -0,0 +1,29 @@
/**
* The following styles get applied both on the front of your site
* and in the editor.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-lcp-viewport {
background-color: #21759b;
color: #fff;
padding: 2px;
}
.lcp-dynamic-container.lcp-has-background-image {
position:relative;
width:100%;
}
.lcp-dynamic-container.lcp-has-background-image > .lcp-background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* Cover the container like a background */
object-position: center center; /* Center the image */
}

View File

@ -0,0 +1,25 @@
/**
* Use this file for JavaScript code that you want to run in the front-end
* on posts/pages that contain this block.
*
* When this file is defined as the value of the `viewScript` property
* in `block.json` it will be enqueued on the front end of the site.
*
* Example:
*
* ```js
* {
* "viewScript": "file:./view.js"
* }
* ```
*
* If you're not making any changes to this file because your project doesn't need any
* JavaScript running in the front-end, then you should delete this file and remove
* the `viewScript` property from `block.json`.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
*/
/* eslint-disable no-console */
console.log( 'Hello World! (from create-block-lcp-viewport block)' );
/* eslint-enable no-console */

View File

@ -0,0 +1,18 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
# WordPress Coding Standards
# https://make.wordpress.org/core/handbook/coding-standards/
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
[*.{yml,yaml}]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Coverage directory used by tools like istanbul
coverage
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Output of `npm pack`
*.tgz
# Output of `wp-scripts plugin-zip`
*.zip
# dotenv environment variables file
.env

View File

@ -0,0 +1,29 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/footer-container",
"version": "0.1.0",
"title": "Footer Container",
"category": "widgets",
"icon": "smiley",
"description": "A container for inserting the footer",
"example": {},
"supports": {
"color": {
"background": true,
"text": false,
"link": false
}
},
"attributes": {
"sticky": {
"type": "string",
"default": "never"
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{border:1px dotted red}

View File

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

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{border:1px dotted red}

View File

@ -0,0 +1 @@
(()=>{"use strict";var e,t={717:()=>{const e=window.wp.blocks,t=window.wp.i18n,n=window.wp.blockEditor,l=window.wp.components,o=window.React,r=window.ReactJSXRuntime,i=JSON.parse('{"UU":"lcp/footer-container"}');(0,e.registerBlockType)(i.UU,{edit:function({attributes:e,setAttributes:i}){const[s,a]=(0,o.useState)(!1),[d,p]=(0,o.useState)("10px"),[c,x]=(0,o.useState)("10px"),[g,h]=(0,o.useState)("10px"),[u,v]=(0,o.useState)("10px"),{sticky:f}=e,j=(0,n.useBlockProps)();return(0,r.jsxs)("div",{...j,children:[(0,r.jsxs)(n.InspectorControls,{children:[(0,r.jsx)(l.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=>{i({sticky:e})}}),(0,r.jsxs)(l.BaseControl,{label:"Padding - Desktop",children:[(0,r.jsxs)("div",{style:{display:"flex",flexDirection:"row"},children:[(0,r.jsx)("span",{style:{marginRight:"10px"},children:"Padding"}),(0,r.jsx)(l.ToggleControl,{label:"Use Independent Padding",checked:s,onChange:()=>{a(!s)}})]}),s?(0,r.jsxs)("div",{style:{display:"grid",padding:"10px",gridTemplateColumns:"1fr 1fr",gap:"10px",justifyItems:"center"},children:[(0,r.jsxs)("fieldset",{style:{gridColumn:"span 2",width:"116px"},children:[(0,r.jsx)("legend",{children:"Top"}),(0,r.jsx)(l.__experimentalUnitControl,{value:d,onChange:e=>{p(e),i({paddingTop:e})}})]}),(0,r.jsxs)("fieldset",{children:[(0,r.jsx)("legend",{children:"Left"}),(0,r.jsx)(l.__experimentalUnitControl,{value:u,onChange:e=>{v(e),i({paddingLeft:e})}})]}),(0,r.jsxs)("fieldset",{children:[(0,r.jsx)("legend",{children:"Right"}),(0,r.jsx)(l.__experimentalUnitControl,{value:c,onChange:e=>{x(e),i({paddingRight:e})}})]}),(0,r.jsxs)("fieldset",{style:{gridColumn:"span 2",width:"116px"},children:[(0,r.jsx)("legend",{children:"Bottom"}),(0,r.jsx)(l.__experimentalUnitControl,{value:g,onChange:e=>{h(e),i({paddingBottom:e})}})]})]}):(0,r.jsx)(l.__experimentalUnitControl,{label:"Padding Value",value:999,onChange:e=>{i({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,r.jsx)(n.InnerBlocks,{template:[["core/template-part",{slug:"footer"}]]})]})},save:function({attributes:e}){const{sticky:t}=e,l=n.useBlockProps.save();return(0,r.jsx)("div",{...l,className:"lcp",id:"lcp-footer-container",children:(0,r.jsx)(n.InnerBlocks.Content,{})})}})}},n={};function l(e){var o=n[e];if(void 0!==o)return o.exports;var r=n[e]={exports:{}};return t[e](r,r.exports,l),r.exports}l.m=t,e=[],l.O=(t,n,o,r)=>{if(!n){var i=1/0;for(p=0;p<e.length;p++){n=e[p][0],o=e[p][1],r=e[p][2];for(var s=!0,a=0;a<n.length;a++)(!1&r||i>=r)&&Object.keys(l.O).every((e=>l.O[e](n[a])))?n.splice(a--,1):(s=!1,r<i&&(i=r));if(s){e.splice(p--,1);var d=o();void 0!==d&&(t=d)}}return t}r=r||0;for(var p=e.length;p>0&&e[p-1][2]>r;p--)e[p]=e[p-1];e[p]=[n,o,r]},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var o,r,i=n[0],s=n[1],a=n[2],d=0;if(i.some((t=>0!==e[t]))){for(o in s)l.o(s,o)&&(l.m[o]=s[o]);if(a)var p=a(l)}for(t&&t(n);d<i.length;d++)r=i[d],l.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return l.O(p)},n=self.webpackChunklcp_viewport=self.webpackChunklcp_viewport||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var o=l.O(void 0,[350],(()=>l(717)));o=l.O(o)})();

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{background-color:#21759b;color:#fff;padding:2px}

View File

@ -0,0 +1 @@
.wp-block-create-block-lcp-viewport{background-color:#21759b;color:#fff;padding:2px}

View File

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

View File

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

View File

@ -0,0 +1,20 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function lcp_footer_container_block_init() {
register_block_type( __DIR__ . '/build' , array(
'parent' => array( 'lcp/viewport', 'lcp/main-area' ),
)
);
}
add_action( 'init', 'lcp_footer_container_block_init' );

View File

@ -0,0 +1,20 @@
{
"name": "lcp-viewport",
"version": "0.1.0",
"description": "Example block scaffolded with Create Block tool.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"format": "wp-scripts format",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"packages-update": "wp-scripts packages-update",
"plugin-zip": "wp-scripts plugin-zip",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^30.4.0"
}
}

View File

@ -0,0 +1,55 @@
=== Lcp Viewport ===
Contributors: The WordPress Contributors
Tags: block
Tested up to: 6.6
Stable tag: 0.1.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Example block scaffolded with Create Block tool.
== Description ==
This is the long description. No limit, and you can use Markdown (as well as in the following sections).
For backwards compatibility, if this section is missing, the full length of the short description will be used, and
Markdown parsed.
== Installation ==
This section describes how to install the plugin and get it working.
e.g.
1. Upload the plugin files to the `/wp-content/plugins/lcp-viewport` directory, or install the plugin through the WordPress plugins screen directly.
1. Activate the plugin through the 'Plugins' screen in WordPress
== Frequently Asked Questions ==
= A question that someone might have =
An answer to that question.
= What about foo bar? =
Answer to foo bar dilemma.
== Screenshots ==
1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is taken from
the /assets directory or the directory that contains the stable readme.txt (tags or trunk). Screenshots in the /assets
directory take precedence. For example, `/assets/screenshot-1.png` would win over `/tags/4.3/screenshot-1.png`
(or jpg, jpeg, gif).
2. This is the second screen shot
== Changelog ==
= 0.1.0 =
* Release
== Arbitrary section ==
You may provide arbitrary sections, in the same format as the ones above. This may be of use for extremely complicated
plugins where more information needs to be conveyed that doesn't fit into the categories of "description" or
"installation." Arbitrary sections will be shown below the built-in sections outlined above.

View File

@ -0,0 +1,30 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "lcp/footer-container",
"version": "0.1.0",
"title": "Footer Container",
"category": "widgets",
"icon": "smiley",
"description": "A container for inserting the footer",
"example": {},
"supports": {
"color": {
"background": true,
"text": false,
"link": false
}
},
"attributes": {
"sticky": {
"type": "string",
"default": "never"
}
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}

View File

@ -0,0 +1,153 @@
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
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 './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;
// Block props for the outer block
const blockProps = useBlockProps();
// Handle the change of the sticky attribute
const handleStickyChange = (value) => {
setAttributes({ sticky: value });
};
const handleToggleChange = () => {
setIsIndependentLarge(!isIndependentLarge);
};
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 }
};
// Update the padding attribute with the new padding object
setAttributes({ padding: newPadding });
};
return (
<div {...blockProps}>
{/* Inspector Controls: Add a SelectControl to change the sticky attribute */}
<InspectorControls>
<SelectControl
label={__('Sticky Behavior', 'lcp')}
value={sticky}
options={[
{ label: __('Never', 'lcp'), value: 'never' },
{ label: __('On Scroll', 'lcp'), value: 'onScroll' },
{ label: __('Always', 'lcp'), value: 'always' },
]}
onChange={handleStickyChange}
/>
<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>
</InspectorControls>
<InnerBlocks
template= {[
["core/template-part", {slug:'footer'}]]
}
/>
</div>
);
}

View File

@ -0,0 +1,9 @@
/**
* The following styles get applied inside the editor only.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-lcp-viewport {
border: 1px dotted #f00;
}

View File

@ -0,0 +1,39 @@
/**
* Registers a new block provided a unique name and an object defining its behavior.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* All files containing `style` keyword are bundled together. The code used
* gets applied both to the front of your site and to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './style.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
import save from './save';
import metadata from './block.json';
/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
/**
* @see ./save.js
*/
save,
} );

View File

@ -0,0 +1,33 @@
/**
* 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 } from '@wordpress/block-editor';
/**
* The save function defines the way in which the different attributes should
* be combined into the final markup, which is then serialized by the block
* editor into `post_content`.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
*
* @return {Element} Element to render.
*/
export default function Save({ attributes }) {
// Destructure the 'sticky' attribute from the block attributes
const { sticky } = attributes;
// Block props
const blockProps = useBlockProps.save();
return (
<div {...blockProps} className={"lcp"} id="lcp-footer-container">
<InnerBlocks.Content />
</div>
);
}

View File

@ -0,0 +1,12 @@
/**
* The following styles get applied both on the front of your site
* and in the editor.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-create-block-lcp-viewport {
background-color: #21759b;
color: #fff;
padding: 2px;
}

View File

@ -0,0 +1,25 @@
/**
* Use this file for JavaScript code that you want to run in the front-end
* on posts/pages that contain this block.
*
* When this file is defined as the value of the `viewScript` property
* in `block.json` it will be enqueued on the front end of the site.
*
* Example:
*
* ```js
* {
* "viewScript": "file:./view.js"
* }
* ```
*
* If you're not making any changes to this file because your project doesn't need any
* JavaScript running in the front-end, then you should delete this file and remove
* the `viewScript` property from `block.json`.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
*/
/* eslint-disable no-console */
console.log( 'Hello World! (from create-block-lcp-viewport block)' );
/* eslint-enable no-console */

Some files were not shown because too many files have changed in this diff Show More