From 9e7f77eaf4c0197cb797c266ae8749ca0bddeb46 Mon Sep 17 00:00:00 2001 From: Kyriakos Akriotis Date: Fri, 22 Aug 2025 09:36:14 +0000 Subject: [PATCH 01/36] first draft #306 --- package-lock.json | 55 ++- package.json | 1 + src/components/PeriodicTable/index.tsx | 228 +++++++++ src/components/PeriodicTable/index.tsx.old | 453 ++++++++++++++++++ .../PeriodicTable/styles.module.css | 101 ++++ src/pages/periodic-table.mdx | 11 + yarn.lock | 46 +- 7 files changed, 879 insertions(+), 16 deletions(-) create mode 100644 src/components/PeriodicTable/index.tsx create mode 100644 src/components/PeriodicTable/index.tsx.old create mode 100644 src/components/PeriodicTable/styles.module.css create mode 100644 src/pages/periodic-table.mdx diff --git a/package-lock.json b/package-lock.json index e539f1b19e..0c58b1881d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@docusaurus/preset-classic": "^3.7.0", "@mdx-js/react": "^3.1.0", "@telekom/scale-components": "^3.0.0-beta.151", + "@telekom/scale-components-react": "^3.0.0-beta.56", "clsx": "^2.1.1", "docusaurus-plugin-zooming": "^1.0.0", "docusaurus-theme-search-typesense": "^0.23.0", @@ -4894,9 +4895,10 @@ "integrity": "sha512-57BFgDxDkvOOrQf7wmS1u5jQmPRdhh6Y/qOHwTvYlS5GwGlkr3EeGeuwePL5pJ7z0jTnxEIVhmwbZnIx9AIX8w==" }, "node_modules/@telekom/scale-components": { - "version": "3.0.0-beta.151", - "resolved": "https://registry.npmjs.org/@telekom/scale-components/-/scale-components-3.0.0-beta.151.tgz", - "integrity": "sha512-CVqCJEBUW/15wrtzFUae6kTqZz13ZCCfaCrlPPYxWG+E6aavvEzPnZVROUAbXbKFfvMSsOV+zCcVDhMER27x9A==", + "version": "3.0.0-beta.156", + "resolved": "https://registry.npmjs.org/@telekom/scale-components/-/scale-components-3.0.0-beta.156.tgz", + "integrity": "sha512-H8Dml6tKjaCYDBXr4TWaGv5u457C9dMTm6ZuTnhWMzaQBESxK0lYOuUGrQmN1IiX8mHYSzjQuIpwIRlZ1nyrkw==", + "license": "MPL-2.0", "dependencies": { "@duetds/date-picker": "1.2.0", "@floating-ui/dom": "^1.2.8", @@ -4907,6 +4909,20 @@ "stencil-inline-svg": "^1.0.1" } }, + "node_modules/@telekom/scale-components-react": { + "version": "3.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@telekom/scale-components-react/-/scale-components-react-3.0.0-beta.56.tgz", + "integrity": "sha512-FtCd4gyyriSbeN7nUAjHo6jtIFW7tpFTzqDXFCjtiNaGAHVxAI5YLmhH41yPP8704FshNB5M+dBsRqNXQst2kg==", + "license": "MPL-2.0", + "dependencies": { + "@types/react-dom": "^16.9.6", + "@types/vfile-message": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.13.1", + "react-dom": ">=16.13.1" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -5155,14 +5171,25 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/react": { - "version": "18.2.79", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", - "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "version": "16.14.65", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.65.tgz", + "integrity": "sha512-Guc3kE+W8LrQB9I3bF3blvNH15dXFIVIHIJTqrF8cp5XI/3IJcHGo4C3sJNPb8Zx49aofXKnAGIKyonE4f7XWg==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", + "@types/scheduler": "^0.16", "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "16.9.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.25.tgz", + "integrity": "sha512-ZK//eAPhwft9Ul2/Zj+6O11YR6L4JX0J2sVeBC9Ft7x7HFN7xk7yUV/zDxqV6rjvqgl6r8Dq7oQImxtyf/Mzcw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^16.0.0" + } + }, "node_modules/@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -5206,6 +5233,12 @@ "@types/node": "*" } }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "license": "MIT" + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -5246,6 +5279,16 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" }, + "node_modules/@types/vfile-message": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-2.0.0.tgz", + "integrity": "sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw==", + "deprecated": "This is a stub types definition. vfile-message provides its own type definitions, so you do not need this installed.", + "license": "MIT", + "dependencies": { + "vfile-message": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", diff --git a/package.json b/package.json index 38f97200de..f826fd3785 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@docusaurus/preset-classic": "^3.7.0", "@mdx-js/react": "^3.1.0", "@telekom/scale-components": "^3.0.0-beta.151", + "@telekom/scale-components-react": "^3.0.0-beta.56", "clsx": "^2.1.1", "docusaurus-plugin-zooming": "^1.0.0", "docusaurus-theme-search-typesense": "^0.23.0", diff --git a/src/components/PeriodicTable/index.tsx b/src/components/PeriodicTable/index.tsx new file mode 100644 index 0000000000..8bb4ac1884 --- /dev/null +++ b/src/components/PeriodicTable/index.tsx @@ -0,0 +1,228 @@ +import React, { useMemo, useState, KeyboardEvent } from "react"; +import { Search, Columns3, ExternalLink } from "lucide-react"; +import styles from "./styles.module.css"; + +export type OtcCategory = + | "Compute" + | "Storage" + | "Networking" + | "Database" + | "AI & Big Data" + | "Security" + | "Containers & DevOps" + | "Management"; + +export type Chip = "IaaS" | "PaaS" | "Security" | "Management"; + +export type OtcService = { + id: string; + symbol: string; + name: string; + category: OtcCategory; + description: string; + status?: "GA" | "Beta" | "Preview"; + url?: string; + chips: Chip[]; // <-- chips live on the service itself +}; + +/* --------- Data (example) --------- */ +const SERVICES: OtcService[] = [ + // Compute (IaaS) + { id: "ecs", symbol: "ECS", name: "Elastic Cloud Server", category: "Compute", description: "Virtual machines for general compute workloads.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/elastic-cloud-server", chips: ["IaaS"] }, + { id: "bms", symbol: "BMS", name: "Bare Metal Server", category: "Compute", description: "Dedicated, single-tenant physical servers.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/bare-metal-server", chips: ["IaaS"] }, + { id: "as", symbol: "AS", name: "Auto Scaling", category: "Compute", description: "Automatic capacity right-sizing for ECS fleets.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/auto-scaling", chips: ["IaaS"] }, + + // Storage (IaaS) + { id: "obs", symbol: "OBS", name: "Object Storage Service", category: "Storage", description: "S3-compatible object storage for any scale.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/object-storage-service", chips: ["IaaS"] }, + { id: "evs", symbol: "EVS", name: "Elastic Volume Service", category: "Storage", description: "Block storage for ECS/BMS.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/elastic-volume-service", chips: ["IaaS"] }, + { id: "sfs", symbol: "SFS", name: "Scalable File Service", category: "Storage", description: "Managed, elastic NFS file shares.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/scalable-file-service", chips: ["IaaS"] }, + + // Networking (IaaS) (+ some also Security) + { id: "vpc", symbol: "VPC", name: "Virtual Private Cloud", category: "Networking", description: "Private networks, subnets, routing, security groups.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/virtual-private-cloud", chips: ["IaaS", "Security"] }, + { id: "elb", symbol: "ELB", name: "Elastic Load Balancer", category: "Networking", description: "Distribute traffic across instances.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/elastic-load-balancer", chips: ["IaaS", "Security"] }, + { id: "nat", symbol: "NAT", name: "NAT Gateway", category: "Networking", description: "Outbound internet for private subnets.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/nat-gateway", chips: ["IaaS", "Security"] }, + { id: "dns", symbol: "DNS", name: "Domain Name Service", category: "Networking", description: "Authoritative and private DNS zones.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/dns-service", chips: ["IaaS", "Security"] }, + + // Database (PaaS) + { id: "rds", symbol: "RDS", name: "Relational Database Service", category: "Database", description: "Managed MySQL, PostgreSQL, etc.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/relational-database-service", chips: ["PaaS"] }, + { id: "dws", symbol: "DWS", name: "Data Warehouse Service", category: "Database", description: "MPP analytics at petabyte scale.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/data-warehouse-service", chips: ["PaaS"] }, + { id: "dds", symbol: "DDS", name: "Distributed Database Service", category: "Database", description: "Managed document DB.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/distributed-database-service", chips: ["PaaS"] }, + + // AI & Big Data (PaaS) + { id: "ma", symbol: "MA", name: "ModelArts", category: "AI & Big Data", description: "End-to-end ML platform.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/ai/modelarts", chips: ["PaaS"] }, + { id: "mrs", symbol: "MRS", name: "MapReduce Service", category: "AI & Big Data", description: "Managed Hadoop/Spark.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/big-data/mapreduce-service", chips: ["PaaS"] }, + + // Security (Security) + { id: "kms", symbol: "KMS", name: "Key Management Service", category: "Security", description: "HSM-backed encryption keys.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/key-management-service", chips: ["Security"] }, + { id: "waf", symbol: "WAF", name: "Web Application Firewall", category: "Security", description: "Protect web apps from exploits.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/web-application-firewall", chips: ["Security"] }, + { id: "ad", symbol: "AD", name: "Anti-DDoS", category: "Security", description: "DDoS detection and mitigation.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/anti-ddos", chips: ["Security"] }, + + // Containers & DevOps (PaaS) + { id: "cce", symbol: "CCE", name: "Cloud Container Engine", category: "Containers & DevOps", description: "Managed Kubernetes.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/containers/cloud-container-engine", chips: ["PaaS"] }, + { id: "cci", symbol: "CCI", name: "Cloud Container Instance", category: "Containers & DevOps", description: "Serverless containers.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/containers/cloud-container-instance", chips: ["PaaS"] }, + { id: "sst", symbol: "SST", name: "ServiceStage", category: "Containers & DevOps", description: "App platform & CI/CD.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/devtools/servicestage", chips: ["PaaS"] }, + + // Management (Management) + { id: "ces", symbol: "CES", name: "Cloud Eye", category: "Management", description: "Monitoring, metrics, alarms.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/management/ces", chips: ["Management"] }, + { id: "cts", symbol: "CTS", name: "Cloud Trace Service", category: "Management", description: "Audit trails for API ops.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/management/cloud-trace-service", chips: ["Management"] }, + { id: "smn", symbol: "SMN", name: "Simple Message Notification", category: "Management", description: "Pub/Sub style notifications.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/application/smn", chips: ["Management", "PaaS"] }, // example in 2 chips +]; + +/* fixed columns — always render all of them so nothing disappears */ +const ALL_CATS: OtcCategory[] = [ + "Compute", + "Storage", + "Networking", + "Database", + "AI & Big Data", + "Security", + "Containers & DevOps", + "Management", +]; + +function cx(...arr: (string | false | null | undefined)[]) { + return arr.filter(Boolean).join(" "); +} + +export default function OtcServicesColumns() { + const [query, setQuery] = useState(""); + const [chips, setChips] = useState>(new Set()); // multi-select; OR + + const toggleChip = (c: Chip) => { + setChips((prev) => { + const next = new Set(prev); + if (next.has(c)) next.delete(c); + else next.add(c); + return next; + }); + }; + + // Text + chips (chips are ORed: match any selected chip) + const filtered = useMemo(() => { + const q = query.trim().toLowerCase(); + return SERVICES.filter((s) => { + const mq = + !q || + s.name.toLowerCase().includes(q) || + s.symbol.toLowerCase().includes(q) || + s.category.toLowerCase().includes(q); + + if (!mq) return false; + if (chips.size === 0) return true; + + return s.chips?.some((ch) => chips.has(ch)); // OR + }); + }, [query, chips]); + + // Seed all columns so empty ones show their placeholder + const byCategory = useMemo(() => { + const map = new Map(); + ALL_CATS.forEach((c) => map.set(c, [])); + filtered.forEach((s) => map.set(s.category, [...(map.get(s.category) || []), s])); + map.forEach((list, c) => map.set(c, [...list].sort((a, b) => a.name.localeCompare(b.name)))); + return map; + }, [filtered]); + + function onTileKey(e: KeyboardEvent, url?: string) { + if (!url) return; + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + window.open(url, "_blank"); + } + } + + return ( +
+
+
+
+

Open Telekom Cloud — Services

+

Columns view (fixed width, no horizontal scroll). Empty columns stay visible.

+
+ +
+
+ + setQuery(e.target.value)} + /> +
+
+ Columns +
+
+
+ + {/* Chips */} +
+ + {(["IaaS", "PaaS", "Security", "Management"] as Chip[]).map((c) => ( + + ))} +
+ + {/* Columns (fixed width; wrap; never removed) */} +
+ {ALL_CATS.map((cat) => { + const items = byCategory.get(cat) || []; + return ( +
+
+
{cat}
+
+
+ {items.length === 0 ? ( +
No matches
+ ) : ( + items.map((s) => ( +
s.url && window.open(s.url, "_blank")} + onKeyDown={(e) => onTileKey(e, s.url)} + role="button" + tabIndex={0} + data-tooltip={s.description} + data-cat={s.category} + aria-label={`${s.name} (${s.symbol}) — ${s.description}`} + > +
+
{s.category}
+ {s.status && {s.status}} +
+
{s.symbol}
+
+
+ {s.name} +
+ +
+
+ )) + )} +
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/PeriodicTable/index.tsx.old b/src/components/PeriodicTable/index.tsx.old new file mode 100644 index 0000000000..573cf80ef6 --- /dev/null +++ b/src/components/PeriodicTable/index.tsx.old @@ -0,0 +1,453 @@ +// src/components/OtcPeriodicTableCSS.tsx +import React, { useMemo, useState, KeyboardEvent } from "react"; +import { Search, Grid3x3, LayoutGrid, ExternalLink } from "lucide-react"; + +/** + * Open Telekom Cloud — Services Periodic (Pure CSS + lucide-react) + * - No UI libraries. Only semantic HTML + CSS in this file. + * - Two views: Auto Grid and Periodic mock + * - Search + category filters + * - CSS-only tooltip via [data-tooltip] + * - Category accent colors via data attributes and CSS variables + */ + +// --- Types --- +export type OtcCategory = + | "Compute" + | "Storage" + | "Networking" + | "Database" + | "AI & Big Data" + | "Security" + | "Containers & DevOps" + | "Management"; + +export type OtcService = { + id: string; + symbol: string; // like ECS, EVS, OBS + name: string; + category: OtcCategory; + description: string; + status?: "GA" | "Beta" | "Preview"; + url?: string; + // Optional coordinates for a true "periodic" mock layout + period?: number; // row + group?: number; // column +}; + +// --- Mock Data (curated sample; adjust freely) --- +const SERVICES: OtcService[] = [ + // Compute + { id: "ecs", symbol: "ECS", name: "Elastic Cloud Server", category: "Compute", description: "Virtual machines for general compute workloads.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/elastic-cloud-server", period: 1, group: 1 }, + { id: "bms", symbol: "BMS", name: "Bare Metal Server", category: "Compute", description: "Dedicated, single-tenant physical servers.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/bare-metal-server", period: 1, group: 18 }, + { id: "as", symbol: "AS", name: "Auto Scaling", category: "Compute", description: "Automatic capacity right-sizing for ECS fleets.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/compute/auto-scaling", period: 2, group: 1 }, + + // Storage + { id: "obs", symbol: "OBS", name: "Object Storage Service", category: "Storage", description: "S3-compatible object storage for any scale.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/object-storage-service", period: 2, group: 13 }, + { id: "evs", symbol: "EVS", name: "Elastic Volume Service", category: "Storage", description: "Block storage volumes for ECS and BMS.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/elastic-volume-service", period: 3, group: 14 }, + { id: "efs", symbol: "SFS", name: "Scalable File Service", category: "Storage", description: "Managed, elastic file shares (NFS).", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/storage/scalable-file-service", period: 3, group: 15 }, + + // Networking + { id: "vpc", symbol: "VPC", name: "Virtual Private Cloud", category: "Networking", description: "Private networks, subnets, routing, and security groups.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/virtual-private-cloud", period: 4, group: 1 }, + { id: "elb", symbol: "ELB", name: "Elastic Load Balancer", category: "Networking", description: "Distribute traffic across instances.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/elastic-load-balancer", period: 4, group: 17 }, + { id: "nat", symbol: "NAT", name: "NAT Gateway", category: "Networking", description: "Outbound internet for private subnets.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/nat-gateway", period: 4, group: 18 }, + { id: "dns", symbol: "DNS", name: "Domain Name Service", category: "Networking", description: "Authoritative and private DNS zones.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/networking/dns-service", period: 4, group: 16 }, + + // Database + { id: "rds", symbol: "RDS", name: "Relational Database Service", category: "Database", description: "Managed MySQL, PostgreSQL, and more.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/relational-database-service", period: 5, group: 2 }, + { id: "dws", symbol: "DWS", name: "Data Warehouse Service", category: "Database", description: "Massively parallel, petabyte-scale analytics.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/data-warehouse-service", period: 5, group: 3 }, + { id: "dds", symbol: "DDS", name: "Distributed Database Service", category: "Database", description: "Managed document databases.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/database/distributed-database-service", period: 5, group: 4 }, + + // AI & Big Data + { id: "ma", symbol: "MA", name: "ModelArts", category: "AI & Big Data", description: "End-to-end ML platform for training and deployment.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/ai/modelarts", period: 6, group: 2 }, + { id: "mrs", symbol: "MRS", name: "MapReduce Service", category: "AI & Big Data", description: "Managed Hadoop/Spark clusters for big data.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/big-data/mapreduce-service", period: 6, group: 3 }, + + // Security + { id: "kms", symbol: "KMS", name: "Key Management Service", category: "Security", description: "Customer-managed encryption keys (HSM-backed).", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/key-management-service", period: 7, group: 17 }, + { id: "waf", symbol: "WAF", name: "Web Application Firewall", category: "Security", description: "Protect web apps from common exploits.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/web-application-firewall", period: 7, group: 16 }, + { id: "antiddos", symbol: "AD", name: "Anti-DDoS", category: "Security", description: "DDoS detection and mitigation.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/security/anti-ddos", period: 7, group: 18 }, + + // Containers & DevOps + { id: "cce", symbol: "CCE", name: "Cloud Container Engine", category: "Containers & DevOps", description: "Managed Kubernetes clusters.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/containers/cloud-container-engine", period: 2, group: 6 }, + { id: "cci", symbol: "CCI", name: "Cloud Container Instance", category: "Containers & DevOps", description: "Serverless containers.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/containers/cloud-container-instance", period: 2, group: 7 }, + { id: "sst", symbol: "SST", name: "ServiceStage", category: "Containers & DevOps", description: "App platform & CI/CD for microservices.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/devtools/servicestage", period: 2, group: 8 }, + + // Management + { id: "ces", symbol: "CES", name: "Cloud Eye", category: "Management", description: "Monitoring, metrics, and alarms.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/management/ces", period: 3, group: 6 }, + { id: "cts", symbol: "CTS", name: "Cloud Trace Service", category: "Management", description: "Audit trails for API operations.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/management/cloud-trace-service", period: 3, group: 7 }, + { id: "smn", symbol: "SMN", name: "Simple Message Notification", category: "Management", description: "Pub/Sub style notifications.", status: "GA", url: "https://open-telekom-cloud.com/en/products-services/application/smn", period: 3, group: 8 }, +]; + +// grid dimensions for the periodic mock +const MAX_PERIOD = 7; +const MAX_GROUP = 18; + +function cx(...arr: (string | false | null | undefined)[]) { + return arr.filter(Boolean).join(" "); +} + +export default function OtcPeriodicTableCSS() { + const [query, setQuery] = useState(""); + const [view, setView] = useState<"auto-grid" | "periodic">("auto-grid"); + const [activeCats, setActiveCats] = useState([]); + + const categories = useMemo( + () => + ([ + "Compute", + "Storage", + "Networking", + "Database", + "AI & Big Data", + "Security", + "Containers & DevOps", + "Management", + ] as OtcCategory[]), + [] + ); + + const filtered = useMemo(() => { + const q = query.trim().toLowerCase(); + return SERVICES.filter((s) => { + const matchesQuery = + !q || + s.name.toLowerCase().includes(q) || + s.symbol.toLowerCase().includes(q) || + s.category.toLowerCase().includes(q); + const matchesCat = + activeCats.length === 0 || activeCats.includes(s.category); + return matchesQuery && matchesCat; + }); + }, [query, activeCats]); + + function toggleCat(cat: OtcCategory) { + setActiveCats((prev) => + prev.includes(cat) ? prev.filter((c) => c !== cat) : [...prev, cat] + ); + } + + function onTileKey(e: KeyboardEvent, url?: string) { + if (!url) return; + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + window.open(url, "_blank"); + } + } + + return ( +
+ {/* Inline CSS so this file is self-contained */} + + +
+ {/* Header */} +
+
+ {/*

Open Telekom Cloud — Services Periodic

+

A periodic-table inspired catalogue with pure CSS components.

*/} +
+ +
+
+ + setQuery(e.target.value)} + /> +
+ +
+ + +
+
+
+ + {/* Filters */} +
+ + {categories.map((cat) => ( + + ))} +
+ + {/* Legend */} +
+
+
Tile anatomy
+
+
ECS
+
+
Symbol
+
Short code for quick scanning
+
+
+
+
+
Interactions
+
    +
  • Hover: quick summary tooltip
  • +
  • Click: open product page
  • +
  • Filter + search can be combined
  • +
+
+
+
Status
+
GA
+
+
+ + {/* Views */} + {view === "auto-grid" ? ( + + ) : ( + + )} + + {/* Pitch notes */} +
+
Pitch ideas
+
    +
  • + Hero transition: animate Grid ⇄ Periodic via CSS transform + classes. +
  • +
  • + Booth mode: full-screen, auto-cycling spotlight across + categories. +
  • +
  • + Export: render the current view into SVG/PNG for print + assets. +
  • +
+
+
+
+ ); +} + +// --- Subcomponents --- +function AutoGrid({ + services, + onTileKey, +}: { + services: OtcService[]; + onTileKey: (e: KeyboardEvent, url?: string) => void; +}) { + return ( +
+ {services.map((s) => ( + + ))} +
+ ); +} + +function PeriodicGrid({ + services, + onTileKey, +}: { + services: OtcService[]; + onTileKey: (e: KeyboardEvent, url?: string) => void; +}) { + const grid: (OtcService | null)[][] = Array.from({ length: MAX_PERIOD }, () => + Array.from({ length: MAX_GROUP }, () => null) + ); + services.forEach((s) => { + if (s.period && s.group) { + const r = s.period - 1; + const c = s.group - 1; + if (grid[r] && grid[r][c] === null) grid[r][c] = s; + } + }); + + return ( +
+
+ {grid.map((row, rIdx) => ( + + {row.map((cell, cIdx) => ( +
+ {cell ? ( + + ) : ( +
+ )} +
+ ))} + + ))} +
+
+ Conceptual layout. Add coordinates per service for precise placement. +
+
+ ); +} + +function ServiceTile({ + service, + compact = false, + onTileKey, +}: { + service: OtcService; + compact?: boolean; + onTileKey: (e: KeyboardEvent, url?: string) => void; +}) { + return ( +
service.url && window.open(service.url, "_blank")} + onKeyDown={(e) => onTileKey(e, service.url)} + role="button" + tabIndex={0} + data-tooltip={service.description} + data-cat={service.category} + aria-label={`${service.name} (${service.symbol}) — ${service.description}`} + > +
+
{service.category}
+ {service.status && {service.status}} +
+
{service.symbol}
+ {!compact && ( +
+
+ {service.name} +
+ +
+ )} +
+ ); +} + +// --- CSS (scoped via - -
- {/* Header */} -
-
- {/*

Open Telekom Cloud — Services Periodic

-

A periodic-table inspired catalogue with pure CSS components.

*/} -
- -
-
- - setQuery(e.target.value)} - /> -
- -
- - -
-
-
- - {/* Filters */} -
- - {categories.map((cat) => ( - - ))} -
- - {/* Legend */} -
-
-
Tile anatomy
-
-
ECS
-
-
Symbol
-
Short code for quick scanning
-
-
-
-
-
Interactions
-
    -
  • Hover: quick summary tooltip
  • -
  • Click: open product page
  • -
  • Filter + search can be combined
  • -
-
-
-
Status
-
GA
-
-
- - {/* Views */} - {view === "auto-grid" ? ( - - ) : ( - - )} - - {/* Pitch notes */} -
-
Pitch ideas
-
    -
  • - Hero transition: animate Grid ⇄ Periodic via CSS transform - classes. -
  • -
  • - Booth mode: full-screen, auto-cycling spotlight across - categories. -
  • -
  • - Export: render the current view into SVG/PNG for print - assets. -
  • -
-
-
-
- ); -} - -// --- Subcomponents --- -function AutoGrid({ - services, - onTileKey, -}: { - services: OtcService[]; - onTileKey: (e: KeyboardEvent, url?: string) => void; -}) { - return ( -
- {services.map((s) => ( - - ))} -
- ); -} - -function PeriodicGrid({ - services, - onTileKey, -}: { - services: OtcService[]; - onTileKey: (e: KeyboardEvent, url?: string) => void; -}) { - const grid: (OtcService | null)[][] = Array.from({ length: MAX_PERIOD }, () => - Array.from({ length: MAX_GROUP }, () => null) - ); - services.forEach((s) => { - if (s.period && s.group) { - const r = s.period - 1; - const c = s.group - 1; - if (grid[r] && grid[r][c] === null) grid[r][c] = s; - } - }); - - return ( -
-
- {grid.map((row, rIdx) => ( - - {row.map((cell, cIdx) => ( -
- {cell ? ( - - ) : ( -
- )} -
- ))} - - ))} -
-
- Conceptual layout. Add coordinates per service for precise placement. -
-
- ); -} - -function ServiceTile({ - service, - compact = false, - onTileKey, -}: { - service: OtcService; - compact?: boolean; - onTileKey: (e: KeyboardEvent, url?: string) => void; -}) { - return ( -
service.url && window.open(service.url, "_blank")} - onKeyDown={(e) => onTileKey(e, service.url)} - role="button" - tabIndex={0} - data-tooltip={service.description} - data-cat={service.category} - aria-label={`${service.name} (${service.symbol}) — ${service.description}`} - > -
-
{service.category}
- {service.status && {service.status}} -
-
{service.symbol}
- {!compact && ( -
-
- {service.name} -
- -
- )} -
- ); -} - -// --- CSS (scoped via