diff --git a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/ImageGrid.yaml b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/ImageGrid.yaml
index de82e45f..e26b2a41 100644
--- a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/ImageGrid.yaml
+++ b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/ImageGrid.yaml
@@ -20,7 +20,6 @@ Neos.NeosIo:Content.ImageGrid:
group: content
isMonochrome:
type: boolean
- defaultValue: false
ui:
reloadIfChanged: true
label: 'Monochrome'
diff --git a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Stage.yaml b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Stage.yaml
index efd9bc0e..3dd8b23d 100644
--- a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Stage.yaml
+++ b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Stage.yaml
@@ -33,6 +33,7 @@
'Neos.NeosIo.FeatureList:FeatureList': true
'Neos.NeosIo.CaseStudies:Content.CaseList': true
'Neos.NeosIo:PostArchive': true
+ 'Neos.NeosIo:Tabs': true
ui:
label: Stage
icon: icon-tasks
diff --git a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.Item.yaml b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.Item.yaml
new file mode 100644
index 00000000..f74ee168
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.Item.yaml
@@ -0,0 +1,18 @@
+'Neos.NeosIo:Tabs.Item':
+ superTypes:
+ 'Neos.Neos:Content': true
+ 'Neos.Neos:ContentCollection': true
+ 'Neos.NeosIo:ContentInspectorGroupMixin': true
+ ui:
+ label: 'Tab'
+ icon: icon-square-o
+ position: 200
+ properties:
+ title:
+ type: string
+ ui:
+ reloadIfChanged: true
+ label: 'Tab Title'
+ showInCreationDialog: true
+ inspector:
+ group: 'default'
diff --git a/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.yaml b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.yaml
new file mode 100644
index 00000000..2533877e
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/NodeTypes/Content/Tabs.yaml
@@ -0,0 +1,19 @@
+'Neos.NeosIo:Tabs':
+ superTypes:
+ 'Neos.Neos:Content': true
+ 'Neos.Neos:ContentCollection': true
+ ui:
+ label: 'Tabs'
+ icon: icon-layer-group
+ position: 400
+ constraints:
+ nodeTypes:
+ '*': false
+ 'Neos.NeosIo:Tabs.Item': true
+ options:
+ # `internezzo/childreload`
+ reloadIfChildChanged: true
+ template:
+ childNodes:
+ itemNode1:
+ type: 'Neos.NeosIo:Tabs.Item'
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.Item.fusion b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.Item.fusion
new file mode 100644
index 00000000..b1c729cd
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.Item.fusion
@@ -0,0 +1,11 @@
+prototype(Neos.NeosIo:Tabs.Item) < prototype(Neos.Neos:ContentComponent) {
+ content = Neos.Neos:ContentCollection {
+ nodePath = '.'
+ }
+
+ renderer = afx`
+
+ {props.content}
+
+ `
+}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.fusion b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.fusion
new file mode 100644
index 00000000..d09b499f
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.fusion
@@ -0,0 +1,25 @@
+prototype(Neos.NeosIo:Tabs) < prototype(Neos.Neos:ContentComponent) {
+ renderer = afx`
+
+
+
+
+
+
+
+
+
+
+ `
+}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.scss b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.scss
new file mode 100644
index 00000000..253070f4
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Content/Tabs/Tabs.scss
@@ -0,0 +1,138 @@
+.tabs {
+ position: relative;
+ overflow-x: auto;
+ overflow-y: hidden;
+ height: 48px;
+ width: 100%;
+ margin: 0 auto;
+ white-space: nowrap;
+
+ .tab {
+ display: inline-block;
+ text-align: center;
+ line-height: 48px;
+ height: 48px;
+ flex-shrink: 0;
+ padding: 0;
+ margin: 0;
+ background: none;
+ appearance: none;
+ border: none;
+ cursor: pointer;
+
+ &:focus,
+ &:focus.active {
+ outline: none;
+ }
+
+ &:hover,
+ &.active {
+ background-color: transparent;
+ }
+
+ .tab-link__inner {
+ padding: 0 24px;
+ height: 100%;
+ }
+
+ font-size: 14px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ transition: color .28s ease, background-color .28s ease;
+
+ &.disabled a,
+ &.disabled a:hover {
+ cursor: default;
+ }
+ }
+}
+
+.tabs {
+ display: flex;
+ justify-content: center;
+ padding: 0;
+}
+
+.tabs .indicator {
+ display: none;
+}
+
+.tabs-content__wrapper {
+ position: relative;
+ padding-top: 48px;
+
+ &::after {
+ bottom: -1px;
+ content: "";
+ width: 16px;
+ border-top: 1px solid #e7e7e8;
+ position: absolute;
+ right: 12px;
+ transform-origin: right top;
+ transform: rotateZ(135deg);
+ }
+}
+
+.tabs-content {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-bottom: 15px;
+}
+
+.tab-item:not([hidden]){
+ display: inline-block;
+ width: 100%;
+}
+
+.tabs {
+ .tab {
+ @include clippedForm;
+
+ position: relative;
+ color: $headingsColor;
+
+ .tab-link__inner {
+ pointer-events: none;
+ display: grid;
+ font-weight: bold;
+ font-family: $brand-font-family;
+
+ &:empty::after {
+ content: "Tab title";
+ color: #ccc;
+ }
+ }
+
+ &[aria-selected="true"] {
+ .tab-link__inner {
+ background-color: brand('primary');
+ }
+
+ color: #fff;
+ }
+
+ &:focus, &:active, &:focus[aria-selected="true"] {
+ .tab-link__inner {
+ background-color: brand('primary');
+ }
+ }
+
+ &:focus,
+ &:active,
+ &[aria-selected="true"] {
+ &:before {
+ content: "";
+ width: 18px;
+ border-top: 1px solid #e7e7e8;
+ position: absolute;
+ left: -7px;
+ transform-origin: right top;
+ transform: rotateZ(-45deg);
+ z-index: 1;
+ }
+ }
+ }
+}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Main.scss b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Main.scss
index 8c8de7fc..29a70e72 100644
--- a/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Main.scss
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Fusion/Main.scss
@@ -1 +1,4 @@
@import "../Scss/Main";
+
+// TODO HACKY, as in: they dont follow the Atomic Design Structure
+@import "./Content/Tabs/Tabs";
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/Tabs.js b/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/Tabs.js
new file mode 100644
index 00000000..66d53fef
--- /dev/null
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/Tabs.js
@@ -0,0 +1,54 @@
+import BaseComponent from "./BaseComponent";
+
+/**
+ * Adapted code from https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tab_role#example
+ */
+export default class Tabs extends BaseComponent {
+ constructor(el) {
+ super(el);
+
+ const tabwrapper = this.el;
+
+ const tabList = tabwrapper.querySelector('[role="tablist"]');
+ const tabs = tabwrapper.querySelectorAll('[role="tab"]');
+ const tabPanels = tabwrapper.querySelectorAll('[role="tabpanel"]');
+ tabPanels.forEach((panel, index) => index !== 0 && panel.toggleAttribute("hidden", true));
+
+ // Add a click event handler to each tab
+ tabs.forEach((tab) => {
+ tab.addEventListener("click", () => {
+ tabs.forEach((t) => t.setAttribute("aria-selected", t !== tab ? 'false' : 'true'));
+
+ const controls = tab.getAttribute("aria-controls");
+ tabPanels.forEach((panel) => panel.toggleAttribute("hidden", panel.id !== controls));
+ });
+ });
+
+ // Enable arrow navigation between tabs in the tab list
+
+ let tabFocus = 0;
+ tabList.addEventListener("keydown", (e) => {
+ // Move right
+ if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
+ tabs[tabFocus].setAttribute("tabindex", -1);
+ if (e.key === 'ArrowRight') {
+ tabFocus++;
+ // If we're at the end, go to the start
+ if (tabFocus >= tabs.length) {
+ tabFocus = 0;
+ }
+ // Move left
+ } else if (e.key === 'ArrowLeft') {
+ tabFocus--;
+ // If we're at the start, move to the end
+ if (tabFocus < 0) {
+ tabFocus = tabs.length - 1;
+ }
+ }
+
+ tabs[tabFocus].setAttribute("tabindex", 0);
+ tabs[tabFocus].focus();
+ }
+ });
+ }
+}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/index.js b/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/index.js
index d73ba649..29234e3c 100644
--- a/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/index.js
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/JavaScript/Components/index.js
@@ -7,6 +7,7 @@ import ScrollTo from './ScrollTo';
import ProgressiveImage from './ProgressiveImage';
import ScrollClassToggler from './ScrollClassToggler';
import SentenceSwitcher from './SentenceSwitcher';
+import Tabs from './Tabs';
export {
ClassToggler,
@@ -16,5 +17,6 @@ export {
SentenceSwitcher,
ProgressiveImage,
ScrollClassToggler,
- EmptyClickHandler
+ EmptyClickHandler,
+ Tabs
};
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Atoms/_Button.scss b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Atoms/_Button.scss
index c66cc1f0..3e7f8e24 100644
--- a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Atoms/_Button.scss
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Atoms/_Button.scss
@@ -83,11 +83,7 @@ $btn-horizontal-padding: 40px;
@mixin btnClippedBorder() {
.btn__content {
- $clip-path: polygon(var(--btn-clip-width) 0px, 0 var(--btn-clip-height), 0 100%, calc(100% - var(--btn-clip-width)) 100%, 100% calc(100% - var(--btn-clip-height)), 100% 0);
-
- // Autoprefixer doesn't add the required webkit prefix for some reason
- -webkit-clip-path: #{$clip-path};
- clip-path: #{$clip-path};
+ @include clippedForm;
}
}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Molecules/_ImageGrid.scss b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Molecules/_ImageGrid.scss
index bc6c01f3..8af6fc20 100644
--- a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Molecules/_ImageGrid.scss
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/Molecules/_ImageGrid.scss
@@ -44,3 +44,7 @@
}
}
+
+.image-grid__greyscale figure {
+ filter: grayscale(1);
+}
diff --git a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/_Framework/_Mixins.scss b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/_Framework/_Mixins.scss
index 3ebc025e..c1e5456c 100644
--- a/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/_Framework/_Mixins.scss
+++ b/DistributionPackages/Neos.NeosIo/Resources/Private/Scss/_Framework/_Mixins.scss
@@ -87,3 +87,13 @@
}
}
}
+
+@mixin clippedForm() {
+ $btn-clip-width: 14px;
+ $btn-clip-height: 10px;
+ $clip-path: polygon($btn-clip-width 0px, 0 $btn-clip-height, 0 100%, calc(100% - #{$btn-clip-width}) 100%, 100% calc(100% - #{$btn-clip-height}), 100% 0);
+
+ // Autoprefixer doesn't add the required webkit prefix for some reason
+ -webkit-clip-path: #{$clip-path};
+ clip-path: #{$clip-path};
+}
diff --git a/composer.json b/composer.json
index 0274eb04..8571f3ad 100644
--- a/composer.json
+++ b/composer.json
@@ -31,7 +31,8 @@
"ttree/outofbandrendering": "dev-task/newer-neos-versions as 4.0.0",
"cweagans/composer-patches": "^1.7",
- "jcupitt/vips": "^1.0"
+ "jcupitt/vips": "^1.0",
+ "internezzo/childreload": "^1.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
diff --git a/composer.lock b/composer.lock
index fe5b2972..728323f3 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4083,6 +4083,44 @@
},
"time": "2023-06-07T14:49:52+00:00"
},
+ {
+ "name": "internezzo/childreload",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/internezzo/neos-childreload.git",
+ "reference": "cdc6bd7cc768aa3d7a3d04a371fe34d7118ffc8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/internezzo/neos-childreload/zipball/cdc6bd7cc768aa3d7a3d04a371fe34d7118ffc8a",
+ "reference": "cdc6bd7cc768aa3d7a3d04a371fe34d7118ffc8a",
+ "shasum": ""
+ },
+ "require": {
+ "neos/neos": ">=3.3"
+ },
+ "type": "neos-package",
+ "extra": {
+ "neos": {
+ "package-key": "Internezzo.ChildReload"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Internezzo\\ChildReload\\": "Classes/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-3.0-or-later"
+ ],
+ "description": "Neos CMS package that helps to reload the page on change of any of the child nodes of the specific nodetype",
+ "support": {
+ "source": "https://github.com/internezzo/neos-childreload/tree/1.0.2"
+ },
+ "time": "2019-07-26T09:47:31+00:00"
+ },
{
"name": "jcupitt/vips",
"version": "v1.0.10",