FEATURE temporarely keeping this demonsrtration block
This commit is contained in:
parent
d99a296280
commit
bf417b4e59
71
plugins/carhop-blocks/src/company-timeline/block.json
Normal file
71
plugins/carhop-blocks/src/company-timeline/block.json
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schemas.wp.org/trunk/block.json",
|
||||||
|
"apiVersion": 3,
|
||||||
|
"name": "telex/block-company-timeline",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"title": "Company Timeline",
|
||||||
|
"category": "design",
|
||||||
|
"icon": "calendar-alt",
|
||||||
|
"description": "Display company milestones with a fixed sidebar navigation and scroll-based highlighting",
|
||||||
|
"keywords": [
|
||||||
|
"timeline",
|
||||||
|
"history",
|
||||||
|
"milestones",
|
||||||
|
"chronology",
|
||||||
|
"events"
|
||||||
|
],
|
||||||
|
"attributes": {
|
||||||
|
"entries": {
|
||||||
|
"type": "array",
|
||||||
|
"default": [
|
||||||
|
{
|
||||||
|
"year": "2020",
|
||||||
|
"title": "Company Founded",
|
||||||
|
"description": "Our journey began with a vision to make a difference.",
|
||||||
|
"imageUrl": "",
|
||||||
|
"imageId": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"attributes": {
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"year": "2010",
|
||||||
|
"title": "The Beginning",
|
||||||
|
"description": "Founded with a mission to innovate.",
|
||||||
|
"imageUrl": "",
|
||||||
|
"imageId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"year": "2015",
|
||||||
|
"title": "Major Milestone",
|
||||||
|
"description": "Reached 1 million customers worldwide.",
|
||||||
|
"imageUrl": "",
|
||||||
|
"imageId": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"year": "2020",
|
||||||
|
"title": "Global Expansion",
|
||||||
|
"description": "Opened offices in 25 countries.",
|
||||||
|
"imageUrl": "",
|
||||||
|
"imageId": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports": {
|
||||||
|
"html": false,
|
||||||
|
"anchor": true,
|
||||||
|
"align": [
|
||||||
|
"wide",
|
||||||
|
"full"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"textdomain": "company-timeline",
|
||||||
|
"editorScript": "file:./index.js",
|
||||||
|
"editorStyle": "file:./index.css",
|
||||||
|
"style": "file:./style-index.css",
|
||||||
|
"viewScript": "file:./view.js"
|
||||||
|
}
|
||||||
219
plugins/carhop-blocks/src/company-timeline/edit.js
Normal file
219
plugins/carhop-blocks/src/company-timeline/edit.js
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
import { __ } from "@wordpress/i18n";
|
||||||
|
|
||||||
|
import {
|
||||||
|
useBlockProps,
|
||||||
|
InspectorControls,
|
||||||
|
MediaUpload,
|
||||||
|
MediaUploadCheck,
|
||||||
|
RichText,
|
||||||
|
} from "@wordpress/block-editor";
|
||||||
|
|
||||||
|
import {
|
||||||
|
PanelBody,
|
||||||
|
Button,
|
||||||
|
TextControl,
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
IconButton,
|
||||||
|
} from "@wordpress/components";
|
||||||
|
|
||||||
|
import { useState } from "@wordpress/element";
|
||||||
|
|
||||||
|
import "./editor.scss";
|
||||||
|
|
||||||
|
export default function Edit({ attributes, setAttributes }) {
|
||||||
|
const { entries } = attributes;
|
||||||
|
const [selectedYear, setSelectedYear] = useState(null);
|
||||||
|
|
||||||
|
const addEntry = () => {
|
||||||
|
const newEntries = [
|
||||||
|
...entries,
|
||||||
|
{
|
||||||
|
year: new Date().getFullYear().toString(),
|
||||||
|
title: "",
|
||||||
|
description: "",
|
||||||
|
imageUrl: "",
|
||||||
|
imageId: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setAttributes({ entries: newEntries });
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateEntry = (index, field, value) => {
|
||||||
|
const newEntries = [...entries];
|
||||||
|
newEntries[index] = {
|
||||||
|
...newEntries[index],
|
||||||
|
[field]: value,
|
||||||
|
};
|
||||||
|
setAttributes({ entries: newEntries });
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeEntry = (index) => {
|
||||||
|
const newEntries = entries.filter((_, i) => i !== index);
|
||||||
|
setAttributes({ entries: newEntries });
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedEntries = [...entries].sort(
|
||||||
|
(a, b) => parseInt(a.year) - parseInt(b.year)
|
||||||
|
);
|
||||||
|
|
||||||
|
const years = [...new Set(sortedEntries.map((entry) => entry.year))];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InspectorControls>
|
||||||
|
<PanelBody title={__("Timeline Entries", "company-timeline")}>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
onClick={addEntry}
|
||||||
|
style={{ marginBottom: "16px" }}
|
||||||
|
>
|
||||||
|
{__("Add Timeline Entry", "company-timeline")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{sortedEntries.map((entry, index) => {
|
||||||
|
const originalIndex = entries.findIndex(
|
||||||
|
(e) =>
|
||||||
|
e.year === entry.year &&
|
||||||
|
e.title === entry.title &&
|
||||||
|
e.description === entry.description
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card key={index} style={{ marginBottom: "12px" }}>
|
||||||
|
<CardHeader>
|
||||||
|
<strong>
|
||||||
|
{entry.year || __("New Entry", "company-timeline")}
|
||||||
|
</strong>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<TextControl
|
||||||
|
label={__("Year", "company-timeline")}
|
||||||
|
value={entry.year}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateEntry(originalIndex, "year", value)
|
||||||
|
}
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
isDestructive
|
||||||
|
onClick={() => removeEntry(originalIndex)}
|
||||||
|
style={{ marginTop: "8px" }}
|
||||||
|
>
|
||||||
|
{__("Remove Entry", "company-timeline")}
|
||||||
|
</Button>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</PanelBody>
|
||||||
|
</InspectorControls>
|
||||||
|
|
||||||
|
<div {...useBlockProps()}>
|
||||||
|
<div className="wp-block-telex-company-timeline">
|
||||||
|
<div className="timeline-sidebar">
|
||||||
|
<div className="timeline-years">
|
||||||
|
<h3>{__("Timeline", "company-timeline")}</h3>
|
||||||
|
{years.map((year) => (
|
||||||
|
<button
|
||||||
|
key={year}
|
||||||
|
className={`year-link ${
|
||||||
|
selectedYear === year ? "active" : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedYear(year)}
|
||||||
|
>
|
||||||
|
{year}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="timeline-content">
|
||||||
|
{sortedEntries.map((entry, index) => {
|
||||||
|
const originalIndex = entries.findIndex(
|
||||||
|
(e) =>
|
||||||
|
e.year === entry.year &&
|
||||||
|
e.title === entry.title &&
|
||||||
|
e.description === entry.description
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="timeline-entry"
|
||||||
|
data-year={entry.year}
|
||||||
|
>
|
||||||
|
<div className="timeline-year-marker">
|
||||||
|
<h2>{entry.year}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="timeline-entry-content">
|
||||||
|
<RichText
|
||||||
|
tagName="h3"
|
||||||
|
value={entry.title}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateEntry(originalIndex, "title", value)
|
||||||
|
}
|
||||||
|
placeholder={__(
|
||||||
|
"Enter milestone title...",
|
||||||
|
"company-timeline"
|
||||||
|
)}
|
||||||
|
className="timeline-title"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RichText
|
||||||
|
tagName="p"
|
||||||
|
value={entry.description}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateEntry(originalIndex, "description", value)
|
||||||
|
}
|
||||||
|
placeholder={__(
|
||||||
|
"Enter milestone description...",
|
||||||
|
"company-timeline"
|
||||||
|
)}
|
||||||
|
className="timeline-description"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MediaUploadCheck>
|
||||||
|
<MediaUpload
|
||||||
|
onSelect={(media) => {
|
||||||
|
updateEntry(originalIndex, "imageUrl", media.url);
|
||||||
|
updateEntry(originalIndex, "imageId", media.id);
|
||||||
|
}}
|
||||||
|
allowedTypes={["image"]}
|
||||||
|
value={entry.imageId}
|
||||||
|
render={({ open }) => (
|
||||||
|
<div className="timeline-media">
|
||||||
|
{entry.imageUrl ? (
|
||||||
|
<>
|
||||||
|
<img src={entry.imageUrl} alt={entry.title} />
|
||||||
|
<Button
|
||||||
|
isDestructive
|
||||||
|
onClick={() => {
|
||||||
|
updateEntry(originalIndex, "imageUrl", "");
|
||||||
|
updateEntry(originalIndex, "imageId", 0);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{__("Remove Image", "company-timeline")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Button onClick={open} variant="secondary">
|
||||||
|
{__("Add Image", "company-timeline")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</MediaUploadCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
43
plugins/carhop-blocks/src/company-timeline/editor.scss
Normal file
43
plugins/carhop-blocks/src/company-timeline/editor.scss
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* The following styles get applied inside the editor only.
|
||||||
|
*
|
||||||
|
* Replace them with your own styles or remove the file completely.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.wp-block-telex-company-timeline {
|
||||||
|
.timeline-entry-content {
|
||||||
|
.timeline-title,
|
||||||
|
.timeline-description {
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid #3b82f6;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-media {
|
||||||
|
button {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-panel__body {
|
||||||
|
.components-card {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.components-card__header {
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-card__body {
|
||||||
|
.components-base-control {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
plugins/carhop-blocks/src/company-timeline/index.js
Normal file
39
plugins/carhop-blocks/src/company-timeline/index.js
Normal 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,
|
||||||
|
} );
|
||||||
88
plugins/carhop-blocks/src/company-timeline/save.js
Normal file
88
plugins/carhop-blocks/src/company-timeline/save.js
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* 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, RichText } 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
|
||||||
|
*
|
||||||
|
* @param {Object} props Block properties
|
||||||
|
* @return {Element} Element to render.
|
||||||
|
*/
|
||||||
|
export default function save( { attributes } ) {
|
||||||
|
const { entries } = attributes;
|
||||||
|
|
||||||
|
const sortedEntries = [ ...entries ].sort( ( a, b ) =>
|
||||||
|
parseInt( a.year ) - parseInt( b.year )
|
||||||
|
);
|
||||||
|
|
||||||
|
const years = [ ...new Set( sortedEntries.map( entry => entry.year ) ) ];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div { ...useBlockProps.save() }>
|
||||||
|
<div className="wp-block-telex-company-timeline">
|
||||||
|
<div className="timeline-sidebar">
|
||||||
|
<div className="timeline-years">
|
||||||
|
<h3>Timeline</h3>
|
||||||
|
{ years.map( ( year ) => (
|
||||||
|
<a
|
||||||
|
key={ year }
|
||||||
|
href={ `#year-${ year }` }
|
||||||
|
className="year-link"
|
||||||
|
data-year={ year }
|
||||||
|
>
|
||||||
|
{ year }
|
||||||
|
</a>
|
||||||
|
) ) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="timeline-content">
|
||||||
|
{ sortedEntries.map( ( entry, index ) => (
|
||||||
|
<div
|
||||||
|
key={ index }
|
||||||
|
className="timeline-entry"
|
||||||
|
id={ `year-${ entry.year }` }
|
||||||
|
data-year={ entry.year }
|
||||||
|
>
|
||||||
|
<div className="timeline-year-marker">
|
||||||
|
<h2>{ entry.year }</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="timeline-entry-content">
|
||||||
|
{ entry.title && (
|
||||||
|
<RichText.Content
|
||||||
|
tagName="h3"
|
||||||
|
value={ entry.title }
|
||||||
|
className="timeline-title"
|
||||||
|
/>
|
||||||
|
) }
|
||||||
|
|
||||||
|
{ entry.description && (
|
||||||
|
<RichText.Content
|
||||||
|
tagName="p"
|
||||||
|
value={ entry.description }
|
||||||
|
className="timeline-description"
|
||||||
|
/>
|
||||||
|
) }
|
||||||
|
|
||||||
|
{ entry.imageUrl && (
|
||||||
|
<div className="timeline-media">
|
||||||
|
<img src={ entry.imageUrl } alt={ entry.title || '' } />
|
||||||
|
</div>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) ) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
198
plugins/carhop-blocks/src/company-timeline/style.scss
Normal file
198
plugins/carhop-blocks/src/company-timeline/style.scss
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
/**
|
||||||
|
* 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-telex-company-timeline {
|
||||||
|
display: flex;
|
||||||
|
gap: 60px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40px 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.timeline-sidebar {
|
||||||
|
flex: 0 0 200px;
|
||||||
|
position: sticky;
|
||||||
|
top: 100px;
|
||||||
|
height: fit-content;
|
||||||
|
align-self: flex-start;
|
||||||
|
|
||||||
|
.timeline-years {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e293b;
|
||||||
|
border-bottom: 2px solid #e2e8f0;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-link {
|
||||||
|
display: block;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e2e8f0;
|
||||||
|
color: #1e293b;
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.timeline-entry {
|
||||||
|
margin-bottom: 80px;
|
||||||
|
scroll-margin-top: 100px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-year-marker {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 40px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #3b82f6;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #1e293b;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-entry-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 32px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
|
||||||
|
.timeline-title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-description {
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-media {
|
||||||
|
margin-top: 24px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30px;
|
||||||
|
padding: 20px 15px;
|
||||||
|
|
||||||
|
.timeline-sidebar {
|
||||||
|
position: static;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.timeline-years {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-link {
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
.timeline-entry {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
|
||||||
|
.timeline-year-marker {
|
||||||
|
padding-left: 30px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-entry-content {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.timeline-title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-description {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
plugins/carhop-blocks/src/company-timeline/view.js
Normal file
67
plugins/carhop-blocks/src/company-timeline/view.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* Use this file for JavaScript code that you want to run in the front-end
|
||||||
|
* on posts/pages that contain this block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.addEventListener( 'DOMContentLoaded', function() {
|
||||||
|
const timeline = document.querySelector( '.wp-block-telex-company-timeline' );
|
||||||
|
|
||||||
|
if ( ! timeline ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebar = timeline.querySelector( '.timeline-sidebar' );
|
||||||
|
const yearLinks = timeline.querySelectorAll( '.year-link' );
|
||||||
|
const entries = timeline.querySelectorAll( '.timeline-entry' );
|
||||||
|
|
||||||
|
if ( ! sidebar || yearLinks.length === 0 || entries.length === 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth scroll to year when clicking sidebar link
|
||||||
|
yearLinks.forEach( link => {
|
||||||
|
link.addEventListener( 'click', function( e ) {
|
||||||
|
e.preventDefault();
|
||||||
|
const year = this.getAttribute( 'data-year' ) || this.getAttribute( 'href' ).replace( '#year-', '' );
|
||||||
|
const targetEntry = timeline.querySelector( `[data-year="${ year }"]` );
|
||||||
|
|
||||||
|
if ( targetEntry ) {
|
||||||
|
targetEntry.scrollIntoView( {
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Update active year on scroll
|
||||||
|
const observerOptions = {
|
||||||
|
root: null,
|
||||||
|
rootMargin: '-20% 0px -70% 0px',
|
||||||
|
threshold: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const observerCallback = ( entries ) => {
|
||||||
|
entries.forEach( entry => {
|
||||||
|
if ( entry.isIntersecting ) {
|
||||||
|
const year = entry.target.getAttribute( 'data-year' );
|
||||||
|
|
||||||
|
// Remove active class from all links
|
||||||
|
yearLinks.forEach( link => link.classList.remove( 'active' ) );
|
||||||
|
|
||||||
|
// Add active class to current year
|
||||||
|
const activeLink = timeline.querySelector( `.year-link[data-year="${ year }"]` );
|
||||||
|
if ( activeLink ) {
|
||||||
|
activeLink.classList.add( 'active' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver( observerCallback, observerOptions );
|
||||||
|
|
||||||
|
// Observe all timeline entries
|
||||||
|
entries.forEach( entry => {
|
||||||
|
observer.observe( entry );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
Loading…
Reference in New Issue
Block a user