Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/website/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ src/components/Editor/ui5-autocomplete.json
/icons
/icons-tnt
/icons-business-suite
/illustrations
/illustrations-tnt
static/packages
static/assets

Expand Down
24 changes: 1 addition & 23 deletions packages/website/build-scripts/icons-generation/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,29 +131,7 @@ const _generateIconsPage = (sourceDir, config) => {

const classDef = `export default function ${config.componentName}() {
return (
<div style={{
padding: "2rem 2rem",
}}>
<div style={{ display: "flex", flexDirection: "column" }}>
<div style={{ display: "flex", flexDirection: "column" }}>
<Heading as="h2" style={{ marginBottom: "0.125rem" }}>${config.title}</Heading>
<Link to="${config.npmLink}">${config.npmPackage}</Link>
</div>
<div style={{ marginTop: "1rem" }}>
<span role="presentation"></span>
<input className="icons__search" type="search" placeholder="Filter icons..." aria-label="Filter icons" onInput={function (e) {
[...document.querySelectorAll("[data-icon-name]")].forEach(iconWrapper => {
const iconName = iconWrapper.getAttribute("data-icon-name").toLowerCase();
iconWrapper.classList.toggle("hidden", !iconName.includes(e.target.value))
})

document
.querySelector(".icon__not__found")
.classList
.toggle("hidden", ![...document.querySelectorAll("[data-icon-name]")].every(iconWrapper => iconWrapper.classList.contains("hidden")))
}} />
</div>
</div>
<div>
<div className="icon__grid">
${icons}
</div>
Expand Down
184 changes: 184 additions & 0 deletions packages/website/build-scripts/illustrations-generation/index.mjs
Original file line number Diff line number Diff line change
@@ -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}") && (
<div
className={clsx("illustration__wrapper", {
"illustration__wrapper--selected": selectedIllustration?.name === "${illustrationName}"
})}
data-illustration-name="${illustrationName}"
onClick={() => onIllustrationSelect(illustrations.find(item => item.name === "${illustrationName}"))}>

<div
className="illustration__preview"
dangerouslySetInnerHTML={{ __html: ${illustrationName}SpotSvg }}
/>

<span className="illustration__wrapper__title">{illustrations.find(item => item.name === "${illustrationName}")?.displayName || "${illustrationName}"}</span>
</div>
)}`;

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 (
<div style={{
padding: "1rem",
}}>
<div className="illustration__grid">
${illustrationCards}
</div>
{filteredIllustrations.length === 0 && (
<div style={{ textAlign: "center", padding: "2rem" }}>
<h3>No matching illustrations found</h3>
</div>
)}
</div>
);
}

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!");
5 changes: 5 additions & 0 deletions packages/website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ const config: Config = {
label: 'Icons',
activeBasePath: 'icons',
},
{
to: 'illustrations/',
label: 'Illustrations',
activeBasePath: 'illustrations',
},
{
to: 'play/',
label: 'Playground',
Expand Down
3 changes: 2 additions & 1 deletion packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
114 changes: 70 additions & 44 deletions packages/website/src/pages/icons.css
Original file line number Diff line number Diff line change
@@ -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));
}
Expand All @@ -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;
Expand Down Expand Up @@ -81,7 +133,7 @@
[data-theme='dark'] .icon__svg--picture {
fill: var(--ifm-color-primary);
}


.icon__wrapper__title {
color: var(--ifm-font-color-base);
Expand Down Expand Up @@ -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);
}
/* Segmented button styles moved to shared.css */
Loading
Loading