${icons}
diff --git a/packages/website/build-scripts/illustrations-generation/index.mjs b/packages/website/build-scripts/illustrations-generation/index.mjs
new file mode 100644
index 000000000000..04fe6937097c
--- /dev/null
+++ b/packages/website/build-scripts/illustrations-generation/index.mjs
@@ -0,0 +1,184 @@
+import fs from "fs";
+import path from "path";
+import { fileURLToPath } from "node:url";
+
+const SAPIllustrationsConfig = {
+ title: "SAP Illustrations",
+ npmLink: "https://www.npmjs.com/package/@ui5/webcomponents-fiori",
+ npmPackage: "@ui5/webcomponents-fiori",
+ dir: "illustrations",
+ componentName: "SAPIllustrations",
+ subfolder: "", // No subfolder for SAP illustrations
+};
+
+const SAPTNTIllustrationsConfig = {
+ title: "SAP TNT Illustrations",
+ npmLink: "https://www.npmjs.com/package/@ui5/webcomponents-fiori",
+ npmPackage: "@ui5/webcomponents-fiori",
+ dir: "illustrations-tnt",
+ componentName: "SAPTNTIllustrations",
+ subfolder: "tnt/", // TNT illustrations are in tnt/ subfolder
+};
+
+const capitalize = (str) => {
+ const firstLetter = str.charAt(0);
+ const firstLetterCap = firstLetter.toUpperCase();
+ const remainingLetters = str.slice(1);
+ const capitalizedWord = firstLetterCap + remainingLetters;
+ return capitalizedWord;
+};
+
+const writeFile = (targetDir, content) => {
+ const targetPath = path.resolve(`./${targetDir}`);
+ const targetFile = path.resolve(`${targetPath}/index.js`);
+
+ if (!fs.existsSync(targetPath)) {
+ fs.mkdirSync(targetPath, { recursive: true });
+ }
+ fs.writeFileSync(targetFile, content, { encoding: 'utf8', flag: 'w' });
+};
+
+const commonImports = `
+import React, { useState } from 'react';
+import clsx from "clsx";
+import Heading from '@theme/Heading';
+import Link from '@docusaurus/Link';
+`;
+
+const additionalImports = ``;
+
+/**
+ * Parse the IllustrationMessageType enum and extract non-deprecated illustrations
+ */
+const parseIllustrationsFromEnum = () => {
+ const enumPath = path.join(
+ findRoot("@ui5/webcomponents-fiori"),
+ "src/types/IllustrationMessageType.ts"
+ );
+
+ const enumContent = fs.readFileSync(enumPath, 'utf8');
+
+ const illustrations = [];
+
+ // Extract enum entries with their JSDoc comments
+ const enumEntryRegex = /\/\*\*([\s\S]*?)\*\/\s*(\w+)\s*=\s*"(\w+)"/g;
+ let match;
+
+ while ((match = enumEntryRegex.exec(enumContent)) !== null) {
+ const [, jsdoc, enumKey, enumValue] = match;
+ const isDeprecated = jsdoc.includes('@deprecated');
+
+ if (!isDeprecated) {
+ illustrations.push({
+ name: enumValue.startsWith('Tnt') ? enumValue.replace(/^Tnt/, '') : enumValue,
+ displayName: enumValue, // Full enum name for display
+ isTnt: enumValue.startsWith('Tnt')
+ });
+ }
+ }
+
+ return illustrations;
+};
+
+const _generateIllustrationsPage = (illustrations, config) => {
+ let imports = ``;
+ let illustrationCards = ``;
+ let illustrationDataArray = ``;
+
+ illustrations.forEach(illustration => {
+ const illustrationName = illustration.name;
+ const illustrationNameImportName = `${illustrationName}`;
+
+ imports += `
+import ${illustrationNameImportName} from "${config.npmPackage}/dist/illustrations/${config.subfolder}${illustrationName}.js";
+import { spotSvg as ${illustrationName}SpotSvg } from "${config.npmPackage}/dist/illustrations/${config.subfolder}${illustrationName}.js";
+`;
+
+ illustrationCards += `
+ {isVisible("${illustrationName}") && (
+
onIllustrationSelect(illustrations.find(item => item.name === "${illustrationName}"))}>
+
+
+
+
{illustrations.find(item => item.name === "${illustrationName}")?.displayName || "${illustrationName}"}
+
+ )}`;
+
+ illustrationDataArray += `
+ { name: "${illustrationName}", displayName: "${illustration.displayName}", isTnt: ${illustration.isTnt} },`;
+ });
+
+ const classDef = `export default function ${config.componentName}({ onIllustrationSelect, selectedIllustration, searchQuery = "" }) {
+ const illustrations = [${illustrationDataArray}
+ ];
+
+ // Filter based on search query
+ const filteredIllustrations = searchQuery
+ ? illustrations.filter(item => item.displayName.toLowerCase().includes(searchQuery.toLowerCase()))
+ : illustrations;
+
+ // Check if each illustration should be visible
+ const isVisible = (illustrationName) => {
+ return filteredIllustrations.some(item => item.name === illustrationName);
+ };
+
+ return (
+
+
+ ${illustrationCards}
+
+ {filteredIllustrations.length === 0 && (
+
+
No matching illustrations found
+
+ )}
+
+ );
+ }
+
+export const illustrationsData = [${illustrationDataArray}
+];`;
+
+ return { imports, classDef };
+};
+
+const generateIllustrationsPage = (illustrations, config) => {
+ const { imports, classDef } = _generateIllustrationsPage(illustrations, config);
+
+ const content = `
+${commonImports}
+${additionalImports}
+${imports}
+${classDef}`;
+
+ writeFile(config.dir, content);
+};
+
+function findRoot(pkgName) {
+ return path.dirname(fileURLToPath(import.meta.resolve(`${pkgName}/package.json`)));
+}
+
+// Main execution
+const allIllustrations = parseIllustrationsFromEnum();
+
+// Split into SAP and TNT collections
+const sapIllustrations = allIllustrations.filter(ill => !ill.isTnt);
+const tntIllustrations = allIllustrations.filter(ill => ill.isTnt);
+
+console.log(`Found ${sapIllustrations.length} SAP illustrations (non-deprecated)`);
+console.log(`Found ${tntIllustrations.length} TNT illustrations (non-deprecated)`);
+
+generateIllustrationsPage(sapIllustrations, SAPIllustrationsConfig);
+generateIllustrationsPage(tntIllustrations, SAPTNTIllustrationsConfig);
+
+console.log("Illustrations pages generated successfully!");
diff --git a/packages/website/docusaurus.config.ts b/packages/website/docusaurus.config.ts
index 2a41300248cf..df26e0607c04 100644
--- a/packages/website/docusaurus.config.ts
+++ b/packages/website/docusaurus.config.ts
@@ -163,6 +163,11 @@ const config: Config = {
label: 'Icons',
activeBasePath: 'icons',
},
+ {
+ to: 'illustrations/',
+ label: 'Illustrations',
+ activeBasePath: 'illustrations',
+ },
{
to: 'play/',
label: 'Playground',
diff --git a/packages/website/package.json b/packages/website/package.json
index 16c2e84844fe..5cf7cd192d5e 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -7,7 +7,8 @@
"generate-api-reference": "rimraf ./docs/components/fiori && rimraf ./docs/components/main && rimraf ./docs/components/compat && rimraf ./docs/components/ai && node ./build-scripts/api-reference-generation/index.mjs",
"generate-documentation": "rimraf ./docs/docs && node ./build-scripts/documentation-generation/index.mjs",
"generate-icons": "rimraf ./icons && rimraf ./icons-tnt && rimraf ./icons-business-suite && node ./build-scripts/icons-generation/index.mjs",
- "generate-local-env": "yarn generate-api-reference && yarn generate-documentation && yarn generate-icons",
+ "generate-illustrations": "rimraf ./illustrations && rimraf ./illustrations-tnt && node ./build-scripts/illustrations-generation/index.mjs",
+ "generate-local-env": "yarn generate-api-reference && yarn generate-documentation && yarn generate-icons && yarn generate-illustrations",
"generate-production-env": "yarn generate-local-env && rimraf ./static/pages && rimraf ./static/assets && yarn copy:pages:compat && yarn copy:pages:ai && yarn copy:pages:fiori && yarn copy:pages:main",
"docusaurus": "docusaurus",
"start": "yarn generate-local-cdn && yarn generate-local-env && docusaurus start",
diff --git a/packages/website/src/pages/icons.css b/packages/website/src/pages/icons.css
index b47041a7a832..47c9a6bf9fa5 100644
--- a/packages/website/src/pages/icons.css
+++ b/packages/website/src/pages/icons.css
@@ -1,6 +1,72 @@
+@import "./shared.css";
+
+/* Main container */
+.icons__container {
+ width: 100%;
+ min-height: calc(100vh - var(--ifm-navbar-height));
+ display: flex;
+ flex-direction: column;
+}
+
+/* Header with collection switcher and search */
+.icons__header {
+ padding: 1rem 2rem;
+ border-bottom: 1px solid var(--ifm-color-emphasis-300);
+ background-color: var(--ifm-background-surface-color);
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+ flex-wrap: wrap;
+}
+
+/* Collection metadata (title + package link) */
+.icons__header__metadata {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.125rem;
+ min-width: 200px;
+}
+
+/* Collection metadata (title + package link) */
+.icons__header__separator {
+ flex: 1;
+}
+
+.icons__header__title {
+ margin: 0;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--ifm-font-color-base);
+}
+
+.icons__header__package {
+ font-size: 0.875rem;
+ color: var(--ifm-color-primary);
+ text-decoration: none;
+}
+
+.icons__header__package:hover {
+ text-decoration: underline;
+}
+
+/* Responsive: Stack header elements on mobile */
+@media (max-width: 996px) {
+ .icons__header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+
+ .icons__header__metadata {
+ align-items: flex-start;
+ width: 100%;
+ }
+}
+
.icon__grid {
display: grid;
- padding: 2rem 0;
+ padding: 2rem;
gap: 2rem;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
@@ -15,21 +81,7 @@
justify-content: center;
}
-.icons__search {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background: var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat 0.75rem center / 1rem 1rem;
- border: none;
- border-radius: 2rem;
- color: var(--ifm-navbar-search-input-color);
- cursor: text;
- display: inline-block;
- font-size: 0.9rem;
- height: 2.5rem;
- padding: 0 0.5rem 0 2.25rem;
- width: 12.5rem;
-}
+/* Search input styles moved to shared.css */
.icon__wrapper {
position: relative;
@@ -81,7 +133,7 @@
[data-theme='dark'] .icon__svg--picture {
fill: var(--ifm-color-primary);
}
-
+
.icon__wrapper__title {
color: var(--ifm-font-color-base);
@@ -119,30 +171,4 @@
background-color: var(--ifm-background-surface-color);
}
-
-
-.segmented__button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- background-color: var(--ifm-background-surface-color);
- padding: 0.5rem;
-}
-
-.segmented__button__item {
- padding: 0.25rem 0.5rem;
- margin: 0 0.125rem;
- border-radius: 0.5rem;
- color: var(--ifm-navbar-link-color);
-}
-
-.segmented__button__item:hover {
- cursor: pointer;
- transition: all 0.5s;
- background-color: var(--ifm-menu-color-background-active);
-}
-
-.segmented__button__item--active {
- color: var(--ifm-menu-color-active);
- background-color: var(--ifm-menu-color-background-active);
-}
\ No newline at end of file
+/* Segmented button styles moved to shared.css */
\ No newline at end of file
diff --git a/packages/website/src/pages/icons.tsx b/packages/website/src/pages/icons.tsx
index 57668854fccb..a09fb6f088c5 100644
--- a/packages/website/src/pages/icons.tsx
+++ b/packages/website/src/pages/icons.tsx
@@ -1,59 +1,139 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import clsx from "clsx";
import SAPIcons from "../../icons/index.js";
import SAPTNTIcons from "../../icons-tnt/index.js";
import SAPBSIcons from "../../icons-business-suite/index.js";
import Layout from '@theme/Layout';
+import "./shared.css";
import "./icons.css";
+// Collection metadata
+const COLLECTION_METADATA = {
+ "SAP Icons": {
+ title: "SAP Icons",
+ package: "@ui5/webcomponents-icons",
+ packageUrl: "https://www.npmjs.com/package/@ui5/webcomponents-icons"
+ },
+ "SAP TNT Icons": {
+ title: "SAP TNT Icons",
+ package: "@ui5/webcomponents-icons-tnt",
+ packageUrl: "https://www.npmjs.com/package/@ui5/webcomponents-icons-tnt"
+ },
+ "SAP BSuite Icons": {
+ title: "SAP BSuite Icons",
+ package: "@ui5/webcomponents-icons-business-suite",
+ packageUrl: "https://www.npmjs.com/package/@ui5/webcomponents-icons-business-suite"
+ }
+};
const Select = ({ updateState }) => {
- const [ collection, setCollection ] = useState("SAP Icons");
+ const [collection, setCollection] = useState("SAP Icons");
return
{
- setCollection("SAP Icons")
- updateState("SAP Icons");
- }}
- className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP Icons" })}
+ onClick={() => {
+ setCollection("SAP Icons")
+ updateState("SAP Icons");
+ }}
+ className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP Icons" })}
>SAP Icons
{
- setCollection("SAP TNT Icons");
- updateState("SAP TNT Icons");
- }}
- className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP TNT Icons" })}
+ onClick={() => {
+ setCollection("SAP TNT Icons");
+ updateState("SAP TNT Icons");
+ }}
+ className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP TNT Icons" })}
>SAP TNT Icons
{
- setCollection("SAP BSuite Icons");
- updateState("SAP BSuite Icons");
- }}
- className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP BSuite Icons" })}
+ onClick={() => {
+ setCollection("SAP BSuite Icons");
+ updateState("SAP BSuite Icons");
+ }}
+ className={clsx("segmented__button__item", { 'segmented__button__item--active': collection === "SAP BSuite Icons" })}
>SAP BSuite Icons
;
};
-const Collection = ({ currCollection }) => {
- if (currCollection === "SAP TNT Icons") {
- return
- } else if (currCollection === "SAP BSuite Icons") {
- return
- }
+const Collection = ({ currCollection }) => {
+ // if (currCollection === "SAP TNT Icons") {
+ // return
+ // } else if (currCollection === "SAP BSuite Icons") {
+ // return
+ // }
return
};
export default function Icons() {
- const [ collection, setCollection ] = useState("SAP Icons");
+ const [collection, setCollection] = useState("SAP Icons");
+ const [searchQuery, setSearchQuery] = useState("");
+
+ // Client-side search filtering using DOM manipulation
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+
+ const iconWrappers = document.querySelectorAll('.icon__wrapper');
+ const notFoundElement = document.querySelector('.icon__not__found');
+ let visibleCount = 0;
+
+ iconWrappers.forEach(wrapper => {
+ const iconName = wrapper.getAttribute('data-icon-name') || '';
+ const matches = iconName.toLowerCase().includes(searchQuery.toLowerCase());
+
+ if (matches) {
+ wrapper.classList.remove('hidden');
+ visibleCount++;
+ } else {
+ wrapper.classList.add('hidden');
+ }
+ });
+
+ // Show/hide "not found" message
+ if (visibleCount === 0 && searchQuery) {
+ notFoundElement?.classList.remove('hidden');
+ } else {
+ notFoundElement?.classList.add('hidden');
+ }
+ }, [searchQuery, collection]); // Re-run when search or collection changes
return (
-
);
};
diff --git a/packages/website/src/pages/illustrations.css b/packages/website/src/pages/illustrations.css
new file mode 100644
index 000000000000..7d49b1e9cef1
--- /dev/null
+++ b/packages/website/src/pages/illustrations.css
@@ -0,0 +1,240 @@
+@import "./shared.css";
+
+:root {
+ --sapIllus_BrandColorPrimary: var(--sapContent_Illustrative_Color1);
+ --sapIllus_BrandColorSecondary: var(--sapContent_Illustrative_Color2);
+ --sapIllus_StrokeDetailColor: var(--sapContent_Illustrative_Color4);
+ --sapIllus_Layering1: var(--sapContent_Illustrative_Color5);
+ --sapIllus_Layering2: var(--sapContent_Illustrative_Color6);
+ --sapIllus_BackgroundColor: var(--sapContent_Illustrative_Color7);
+ --sapIllus_ObjectFillColor: var(--sapContent_Illustrative_Color8);
+ --sapIllus_AccentColor: var(--sapContent_Illustrative_Color3);
+ --sapIllus_NoColor: none;
+ --sapIllus_PatternShadow: url(#sapIllus_PatternShadow);
+ --sapIllus_PatternHighlight: url(#sapIllus_PatternHighlight);
+}
+
+/* Main container */
+.illustrations__container {
+ width: 100%;
+ height: calc(100vh - var(--ifm-navbar-height));
+ display: flex;
+ flex-direction: column;
+}
+
+/* Header with collection switcher and search */
+.illustrations__header {
+ padding: 1rem 2rem;
+ border-bottom: 1px solid var(--ifm-color-emphasis-300);
+ background-color: var(--ifm-background-surface-color);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 2rem;
+ flex-wrap: wrap;
+}
+
+/* Split-pane layout */
+.illustrations__split-pane {
+ display: flex;
+ flex: 1;
+ overflow: hidden;
+}
+
+.illustrations__left-pane {
+ width: 40%;
+ overflow-y: auto;
+ border-right: 1px solid var(--ifm-color-emphasis-300);
+ background-color: var(--ifm-background-color);
+}
+
+.illustrations__right-pane {
+ width: 60%;
+ overflow-y: auto;
+ background-color: var(--ifm-background-surface-color);
+ padding: 2rem;
+}
+
+/* Grid for left pane thumbnails */
+.illustration__grid {
+ display: grid;
+ padding: 1rem;
+ gap: 1rem;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+}
+
+/* Simplified thumbnail card */
+.illustration__wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ padding: 0.75rem 0.5rem;
+ background-color: var(--ifm-background-surface-color);
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 1.5px 3px 0px;
+ border: 2px solid transparent;
+ border-radius: 0.5rem;
+ cursor: pointer;
+ gap: 0.5rem;
+ transition: all 0.2s;
+}
+
+.illustration__wrapper:hover {
+ background-color: var(--ifm-menu-color-background-hover);
+ border-color: var(--ifm-color-primary-light);
+}
+
+.illustration__wrapper--selected {
+ border-color: var(--ifm-color-primary);
+ background-color: var(--ifm-menu-color-background-active);
+}
+
+/* Small thumbnail preview */
+.illustration__preview {
+ width: 100%;
+ max-width: 80px;
+ height: auto;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ min-height: 60px;
+}
+
+.illustration__preview svg {
+ width: 100%;
+ height: auto;
+ max-height: 80px;
+}
+
+.illustration__wrapper__title {
+ color: var(--ifm-font-color-base);
+ text-align: center;
+ word-break: break-word;
+ font-size: 0.75rem;
+ width: 100%;
+ line-height: 1.2;
+}
+
+/* Right pane preview panel */
+.illustration__preview-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+
+.illustration__preview-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid var(--ifm-color-emphasis-300);
+}
+
+.illustration__preview-header h2 {
+ margin: 0;
+ font-size: 1.5rem;
+ color: var(--ifm-font-color-base);
+}
+
+.illustration__preview-sizes {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+
+.illustration__preview-size {
+ padding: 1.5rem;
+ background-color: var(--ifm-background-color);
+ border-radius: 0.5rem;
+ border: 1px solid var(--ifm-color-emphasis-200);
+}
+
+.illustration__preview-size h3 {
+ margin: 0 0 1rem 0;
+ font-size: 1rem;
+ color: var(--ifm-color-primary);
+ font-weight: 600;
+}
+
+/* ui5-illustrated-message styling */
+.illustration__preview-size ui5-illustrated-message {
+ display: block;
+ width: 100%;
+}
+
+/* Welcome message styling */
+.illustration__welcome {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ padding: 2rem;
+}
+
+.illustration__welcome-content {
+ max-width: 600px;
+ text-align: center;
+}
+
+.illustration__welcome-content h2 {
+ margin-bottom: 1rem;
+ color: var(--ifm-font-color-base);
+}
+
+.illustration__welcome-content p {
+ margin-bottom: 1.5rem;
+ color: var(--ifm-color-content-secondary);
+ font-size: 1.1rem;
+}
+
+.illustration__welcome-content ul {
+ text-align: left;
+ list-style: none;
+ padding: 0;
+}
+
+.illustration__welcome-content ul li {
+ padding: 0.5rem 0;
+ color: var(--ifm-font-color-base);
+ position: relative;
+ padding-left: 1.5rem;
+}
+
+.illustration__welcome-content ul li:before {
+ content: "→";
+ position: absolute;
+ left: 0;
+ color: var(--ifm-color-primary);
+}
+
+/* Segmented button styles moved to shared.css */
+/* Search input styles moved to shared.css */
+
+/* Responsive: Stack panes on mobile */
+@media (max-width: 996px) {
+ .illustrations__split-pane {
+ flex-direction: column;
+ }
+
+ .illustrations__left-pane,
+ .illustrations__right-pane {
+ width: 100%;
+ border: none;
+ }
+
+ .illustrations__right-pane {
+ border-top: 1px solid var(--ifm-color-emphasis-300);
+ }
+
+ .illustrations__header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+}
+
+/* Utility classes */
+.hidden {
+ display: none !important;
+}
diff --git a/packages/website/src/pages/illustrations.tsx b/packages/website/src/pages/illustrations.tsx
new file mode 100644
index 000000000000..bfee571663b0
--- /dev/null
+++ b/packages/website/src/pages/illustrations.tsx
@@ -0,0 +1,286 @@
+import React, { useState, useEffect } from 'react';
+import clsx from "clsx";
+import Layout from '@theme/Layout';
+import BrowserOnly from '@docusaurus/BrowserOnly';
+import "./shared.css";
+import "./illustrations.css";
+
+// Import generated components and data
+import SAPIllustrations, { illustrationsData as sapData } from "../../illustrations/index.js";
+import SAPTNTIllustrations, { illustrationsData as tntData } from "../../illustrations-tnt/index.js";
+
+// IllustratedMessage preview component
+function IllustrationPreview({ name, displayName, collection }) {
+ const [copied, setCopied] = useState(false);
+
+ useEffect(() => {
+ // Dynamically import UI5 components (SSR-safe)
+ if (typeof window !== 'undefined') {
+ import("@ui5/webcomponents-fiori/dist/IllustratedMessage.js");
+ import("@ui5/webcomponents-fiori/dist/illustrations/AllIllustrations.js");
+ }
+ }, []);
+
+ const illustrationPath = collection === "SAP"
+ ? `@ui5/webcomponents-fiori/dist/illustrations/${name}.js`
+ : `@ui5/webcomponents-fiori/dist/illustrations/tnt/${name}.js`;
+
+ const handleCopyImport = () => {
+ navigator.clipboard.writeText(`import "${illustrationPath}";`);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ return (
+
+
+
{displayName}
+
+
+
+
+
+
Extra Small (Dot)
+
+
+
+
+
Small (Spot)
+
+
+
+
+
Medium (Dialog)
+
+
+
+
+
Large (Scene)
+
+
+
+
+ );
+}
+
+// Welcome message shown when no illustration is selected
+function WelcomeMessage() {
+ return (
+
+
+
Welcome to UI5 Illustrations
+
+ Browse and explore SAP Fiori and TNT illustrations for your UI5 applications.
+
+
+ - Select an illustration from the grid to preview all size variants
+ - Use the search box to filter illustrations by name
+ - Toggle between SAP and TNT illustration collections
+ - Copy the import statement to use in your project
+
+
+
+ );
+}
+
+// Main illustrations page component
+function IllustrationsContent() {
+ const [collection, setCollection] = useState("SAP");
+ const [selectedIllustration, setSelectedIllustration] = useState(null);
+ const [searchQuery, setSearchQuery] = useState("");
+
+ // Load UI5 components and handle URL hash on INITIAL MOUNT only (auto-switch collection)
+ useEffect(() => {
+ // Load UI5 components
+ if (typeof window !== 'undefined') {
+ import("@ui5/webcomponents-fiori/dist/IllustratedMessage.js");
+ import("@ui5/webcomponents-fiori/dist/illustrations/AllIllustrations.js");
+ }
+
+ // Check URL hash for pre-selected illustration (auto-switch collection)
+ if (typeof window !== 'undefined') {
+ const hash = window.location.hash.slice(1); // Remove '#' prefix
+ if (hash) {
+ // Search BOTH collections to find the illustration
+ let illustration = sapData.find(item => item.displayName === hash);
+ let targetCollection = "SAP";
+
+ if (!illustration) {
+ illustration = tntData.find(item => item.displayName === hash);
+ targetCollection = "TNT";
+ }
+
+ if (illustration) {
+ // Auto-switch collection if needed
+ if (targetCollection !== "SAP") {
+ setCollection(targetCollection);
+ }
+ setSelectedIllustration(illustration);
+ } else {
+ // Hash doesn't exist in any collection, clear selection
+ setSelectedIllustration(null);
+ }
+ } else {
+ // No hash, clear selection
+ setSelectedIllustration(null);
+ }
+ }
+ }, []); // Empty dependency array - run only on mount
+
+ // Handle manual tab switching - check if current hash belongs to new collection
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ const hash = window.location.hash.slice(1);
+ if (hash) {
+ const currentData = collection === "SAP" ? sapData : tntData;
+ const illustration = currentData.find(item => item.displayName === hash);
+ if (illustration) {
+ // Hash exists in the new collection, select it
+ setSelectedIllustration(illustration);
+ } else {
+ // Hash doesn't exist in the new collection, clear selection
+ setSelectedIllustration(null);
+ }
+ } else {
+ setSelectedIllustration(null);
+ }
+ }
+ }, [collection]); // Run when collection changes (manual tab switch)
+
+ // Handle browser back/forward navigation
+ useEffect(() => {
+ const handleHashChange = () => {
+ if (typeof window === 'undefined') return;
+
+ const hash = window.location.hash.slice(1);
+ if (hash) {
+ // Search BOTH collections to find the illustration
+ let illustration = sapData.find(item => item.displayName === hash);
+ let targetCollection = "SAP";
+
+ if (!illustration) {
+ illustration = tntData.find(item => item.displayName === hash);
+ targetCollection = "TNT";
+ }
+
+ if (illustration) {
+ // Auto-switch collection if needed
+ if (collection !== targetCollection) {
+ setCollection(targetCollection);
+ }
+ setSelectedIllustration(illustration);
+ }
+ } else {
+ setSelectedIllustration(null);
+ }
+ };
+
+ if (typeof window !== 'undefined') {
+ window.addEventListener('hashchange', handleHashChange);
+ return () => window.removeEventListener('hashchange', handleHashChange);
+ }
+ }, [collection]);
+
+ // Wrapper function that updates URL hash when illustration is selected
+ const handleIllustrationSelect = (illustration) => {
+ setSelectedIllustration(illustration);
+ if (typeof window !== 'undefined') {
+ window.history.pushState(null, '', `#${illustration.displayName}`);
+ }
+ };
+
+ const currentData = collection === "SAP" ? sapData : tntData;
+
+ return (
+
+ {/* Header with collection switcher and search */}
+
+
+
setCollection("SAP")}
+ className={clsx("segmented__button__item", {
+ 'segmented__button__item--active': collection === "SAP"
+ })}
+ >SAP Illustrations
+
setCollection("TNT")}
+ className={clsx("segmented__button__item", {
+ 'segmented__button__item--active': collection === "TNT"
+ })}
+ >SAP TNT Illustrations
+
+
+
setSearchQuery(e.target.value)}
+ />
+
+
+ {/* Split pane layout */}
+
+ {/* Left pane: Grid of thumbnails */}
+
+ {collection === "SAP" ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Right pane: IllustratedMessage preview or welcome message */}
+
+ {selectedIllustration ? (
+
+ {() => (
+
+ )}
+
+ ) : (
+
+ )}
+
+
+
+ );
+}
+
+export default function Illustrations() {
+ return (
+
+
+
+ );
+}
diff --git a/packages/website/src/pages/shared.css b/packages/website/src/pages/shared.css
new file mode 100644
index 000000000000..dc512aa20eef
--- /dev/null
+++ b/packages/website/src/pages/shared.css
@@ -0,0 +1,47 @@
+/* Shared styles for icons and illustrations pages */
+
+/* Search input */
+.page__search {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background: var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat 0.75rem center / 1rem 1rem;
+ border: none;
+ border-radius: 2rem;
+ color: var(--ifm-navbar-search-input-color);
+ cursor: text;
+ display: inline-block;
+ font-size: 0.9rem;
+ height: 2.5rem;
+ padding: 0 0.5rem 0 2.25rem;
+ width: 12.5rem;
+}
+
+/* Segmented button */
+.segmented__button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--ifm-background-color);
+ padding: 0.5rem;
+ border-radius: 0.5rem;
+}
+
+.segmented__button__item {
+ padding: 0.5rem 1rem;
+ margin: 0 0.125rem;
+ border-radius: 0.5rem;
+ color: var(--ifm-navbar-link-color);
+ font-weight: 500;
+}
+
+.segmented__button__item:hover {
+ cursor: pointer;
+ transition: all 0.2s;
+ background-color: var(--ifm-menu-color-background-active);
+}
+
+.segmented__button__item--active {
+ color: var(--ifm-menu-color-active);
+ background-color: var(--ifm-menu-color-background-active);
+}