diff --git a/scripts/migrate-to-split-panel.js b/scripts/migrate-to-split-panel.js
new file mode 100755
index 00000000..5f1fd773
--- /dev/null
+++ b/scripts/migrate-to-split-panel.js
@@ -0,0 +1,141 @@
+#!/usr/bin/env node
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+
+/**
+ * Migration script to convert pages from global drawer plugin to split panel
+ *
+ * This script:
+ * 1. Removes WithGlobalDrawer wrapper from index.tsx files
+ * 2. Adds split panel imports and hooks to root.tsx files
+ * 3. Adds split panel props to CustomAppLayout
+ */
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const pagesDir = path.join(__dirname, '../src/pages');
+
+// Pages to skip (already migrated or special cases)
+const skipPages = ['cards', 'commons', 'split-panel-comparison', 'split-panel-multiple'];
+
+function migrateIndexFile(indexPath) {
+ let content = fs.readFileSync(indexPath, 'utf8');
+
+ // Remove WithGlobalDrawer import
+ content = content.replace(
+ /import\s+{\s*WithGlobalDrawer\s*}\s+from\s+['"]\.\.\/commons\/common-components['"];?\n?/g,
+ '',
+ );
+
+ // Remove WithGlobalDrawer wrapper
+ content = content.replace(
+ /createRoot\(document\.getElementById\(['"]app['"]\)!\)\.render\(\s*
\s* \s*<\/WithGlobalDrawer>\s*\);?/g,
+ "createRoot(document.getElementById('app')!).render( );",
+ );
+
+ fs.writeFileSync(indexPath, content, 'utf8');
+ console.log(`✓ Migrated ${indexPath}`);
+}
+
+function migrateRootFile(rootPath) {
+ let content = fs.readFileSync(rootPath, 'utf8');
+
+ // Check if already has split panel
+ if (content.includes('useGlobalSplitPanel') || content.includes('splitPanelOpen')) {
+ console.log(`⊘ Skipped ${rootPath} (already has split panel)`);
+ return;
+ }
+
+ // Add SplitPanel import if not present
+ if (!content.includes('import SplitPanel')) {
+ content = content.replace(
+ /(import.*from\s+['"]@cloudscape-design\/components\/app-layout['"];?\n)/,
+ "$1import SplitPanel from '@cloudscape-design/components/split-panel';\n",
+ );
+ }
+
+ // Add split panel utilities to common-components import
+ content = content.replace(
+ /(import\s+{[^}]*)(}\s+from\s+['"]\.\.\/commons\/common-components['"])/,
+ (match, p1, p2) => {
+ if (!p1.includes('GlobalSplitPanelContent')) {
+ return p1 + ',\n GlobalSplitPanelContent,\n useGlobalSplitPanel' + p2;
+ }
+ return match;
+ },
+ );
+
+ // Add hook call in App component
+ content = content.replace(
+ /(export\s+(?:function|const)\s+App\s*=\s*\([^)]*\)\s*(?:=>)?\s*{[\s\S]*?)(const\s+\[toolsOpen)/,
+ '$1const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();\n $2',
+ );
+
+ // Add split panel props to CustomAppLayout
+ content = content.replace(
+ /(onToolsChange=\{[^}]+}\}[\s\n]*)/,
+ `$1splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanel={
+
+
+
+ }
+ `,
+ );
+
+ fs.writeFileSync(rootPath, content, 'utf8');
+ console.log(`✓ Migrated ${rootPath}`);
+}
+
+function migratePage(pageName) {
+ const pageDir = path.join(pagesDir, pageName);
+ const indexPath = path.join(pageDir, 'index.tsx');
+ const rootPath = path.join(pageDir, 'root.tsx');
+ const appPath = path.join(pageDir, 'app.tsx');
+
+ try {
+ // Migrate index.tsx if it exists
+ if (fs.existsSync(indexPath)) {
+ migrateIndexFile(indexPath);
+ }
+
+ // Migrate root.tsx or app.tsx if they exist
+ if (fs.existsSync(rootPath)) {
+ migrateRootFile(rootPath);
+ } else if (fs.existsSync(appPath)) {
+ migrateRootFile(appPath);
+ }
+ } catch (error) {
+ console.error(`✗ Error migrating ${pageName}:`, error.message);
+ }
+}
+
+// Get all page directories
+const pages = fs.readdirSync(pagesDir).filter(name => {
+ const stat = fs.statSync(path.join(pagesDir, name));
+ return stat.isDirectory() && !skipPages.includes(name);
+});
+
+console.log(`Found ${pages.length} pages to migrate\n`);
+
+pages.forEach(pageName => {
+ console.log(`\nMigrating ${pageName}...`);
+ migratePage(pageName);
+});
+
+console.log('\n✓ Migration complete!');
+console.log('\nNext steps:');
+console.log('1. Review the changes with git diff');
+console.log('2. Test the pages to ensure split panel works correctly');
+console.log('3. Remove the old global drawer files:');
+console.log(' - src/pages/commons/global-drawer-plugin.tsx');
+console.log(' - src/pages/commons/with-global-drawer.tsx');
+console.log(' - src/common/mount.tsx');
diff --git a/src/common/THEMING.md b/src/common/THEMING.md
new file mode 100644
index 00000000..891b2024
--- /dev/null
+++ b/src/common/THEMING.md
@@ -0,0 +1,80 @@
+# Cloudscape Runtime Theming Implementation
+
+This project implements Cloudscape's runtime theming capability to customize the visual appearance of components.
+
+## Files
+
+- `theme-core.ts` - Theme definition with custom design tokens
+- `apply-theme.ts` - Utility function to apply the theme (optional helper)
+
+## Theme Structure
+
+The theme is defined in `theme-core.ts` and follows Cloudscape's theming API:
+
+```typescript
+export const themeCoreConfig = {
+ tokens: {
+ // Global token overrides for light and dark modes
+ colorBorderButtonNormalDefault: {
+ light: '#232f3e',
+ dark: '#e9ebed',
+ },
+ // ... more tokens
+ },
+ contexts: {
+ // Context-specific overrides
+ header: {
+ tokens: {
+ // Tokens specific to header context
+ },
+ },
+ 'app-layout-toolbar': {
+ // Tokens for app layout toolbar
+ },
+ flashbar: {
+ // Tokens for flashbar notifications
+ },
+ },
+};
+```
+
+## Usage
+
+The theme is applied in each demo page's `index.tsx` file before rendering:
+
+```typescript
+import { applyTheme } from '@cloudscape-design/components/theming';
+import { themeCoreConfig } from '../../common/theme-core';
+
+applyTheme({ theme: themeCoreConfig });
+
+createRoot(document.getElementById('app')!).render( );
+```
+
+## Customization
+
+To customize the theme:
+
+1. Edit `src/common/theme-core.ts`
+2. Modify token values for light/dark modes
+3. Add or remove context-specific overrides
+4. Changes will apply to all demo pages
+
+## Available Contexts
+
+- `header` - Dark header area (high contrast header variant)
+- `top-navigation` - Top navigation component
+- `app-layout-toolbar` - App layout toolbar area
+- `flashbar` - Flashbar notifications
+- `alert` - Alert components
+
+## Token Categories
+
+- **Color tokens** - Can have light/dark mode values
+- **Border radius tokens** - Applied globally (e.g., `borderRadiusButton`)
+- **Typography tokens** - Font families applied globally
+
+## Resources
+
+- [Cloudscape Theming Documentation](https://cloudscape.design/foundation/visual-foundation/theming/)
+- [Design Tokens Reference](https://cloudscape.design/foundation/visual-foundation/design-tokens/)
diff --git a/src/common/apply-mode.ts b/src/common/apply-mode.ts
index 7e03b801..b42b7bd1 100644
--- a/src/common/apply-mode.ts
+++ b/src/common/apply-mode.ts
@@ -1,16 +1,18 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import { applyDensity, Density, disableMotion } from '@cloudscape-design/global-styles';
+import { applyDensity, applyMode, Density, disableMotion, Mode } from '@cloudscape-design/global-styles';
import * as localStorage from './local-storage';
import '@cloudscape-design/global-styles/index.css';
+import './custom-font.css';
(window as any).disableMotionForTests = disableMotion;
// always `true` in this design
export const isVisualRefresh = true;
+// Initialize density
export let currentDensity: Density = localStorage.load('Awsui-Density-Preference') ?? Density.Comfortable;
applyDensity(currentDensity);
@@ -19,3 +21,23 @@ export function updateDensity(density: string) {
localStorage.save('Awsui-Density-Preference', density);
currentDensity = density as Density;
}
+
+// Initialize mode
+export let currentMode: Mode = localStorage.load('Awsui-Mode-Preference') ?? Mode.Light;
+applyMode(currentMode);
+
+export function updateMode(mode: string) {
+ applyMode(mode as Mode);
+ localStorage.save('Awsui-Mode-Preference', mode);
+ currentMode = mode as Mode;
+}
+
+// Initialize direction
+export let currentDirection: string = localStorage.load('Awsui-Direction-Preference') ?? 'ltr';
+document.documentElement.dir = currentDirection;
+
+export function updateDirection(direction: string) {
+ document.documentElement.dir = direction;
+ localStorage.save('Awsui-Direction-Preference', direction);
+ currentDirection = direction;
+}
diff --git a/src/common/apply-theme.ts b/src/common/apply-theme.ts
new file mode 100644
index 00000000..697bcbd3
--- /dev/null
+++ b/src/common/apply-theme.ts
@@ -0,0 +1,93 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { generateThemeConfig, generateThemeConfigConsole, themeCoreConfig } from './theme-core';
+
+// Store the reset function from the current theme
+let currentThemeReset: (() => void) | null = null;
+
+/**
+ * Applies the custom theme to the application
+ * @param customConfig - Optional custom theme configuration. Pass undefined to reset to defaults.
+ */
+export function applyCustomTheme(customConfig?: Partial) {
+ // Always reset previous theme first
+ if (currentThemeReset) {
+ currentThemeReset();
+ currentThemeReset = null;
+ }
+
+ // If no config provided, apply empty theme to reset to Cloudscape defaults
+ if (!customConfig) {
+ const { reset: resetFn } = applyTheme({ theme: { tokens: {} } });
+ currentThemeReset = resetFn;
+ return;
+ }
+
+ // Apply the new theme and store its reset function
+ const { reset: resetFn } = applyTheme({ theme: customConfig as any });
+ currentThemeReset = resetFn;
+}
+
+/**
+ * Resets to Cloudscape defaults (no custom theme)
+ */
+export function resetToDefaults() {
+ if (currentThemeReset) {
+ currentThemeReset();
+ currentThemeReset = null;
+ }
+ // Apply empty theme to ensure complete reset
+ const { reset: resetFn } = applyTheme({ theme: { tokens: {} } });
+ currentThemeReset = resetFn;
+}
+
+// ============================================================================
+// Theme Comparison API
+// ============================================================================
+
+/**
+ * Hook-style API for comparing different theme design directions.
+ * Ensures complete isolation between themes by resetting before each application.
+ *
+ * @example
+ * ```tsx
+ * function ThemeComparison() {
+ * const { applyDirectionA, applyDirectionB, resetToDefault } = useThemeComparison();
+ *
+ * return (
+ *
+ * applyDirectionA()}>Direction A
+ * applyDirectionB()}>Direction B
+ * Reset
+ *
+ * );
+ * }
+ * ```
+ */
+export function useThemeComparison() {
+ const applyDirectionA = (customAccentColor?: { light: string; dark: string }) => {
+ const themeA = generateThemeConfig(customAccentColor);
+ applyCustomTheme(themeA);
+ };
+
+ const applyDirectionB = () => {
+ const themeB = generateThemeConfigConsole();
+ applyCustomTheme(themeB);
+ };
+
+ const resetToDefault = () => {
+ resetToDefaults();
+ };
+
+ return {
+ applyDirectionA,
+ applyDirectionB,
+ resetToDefault,
+ };
+}
+
+// Apply default theme and custom CSS class on module load
+applyCustomTheme();
+document.body.classList.add('custom-css-enabled');
diff --git a/src/common/custom-font.css b/src/common/custom-font.css
new file mode 100644
index 00000000..3311ec53
--- /dev/null
+++ b/src/common/custom-font.css
@@ -0,0 +1,52 @@
+/* Ember Modern Text webfont declarations */
+@font-face {
+ font-family: 'Ember Modern Text';
+ src: url('./fonts/EmberModernTextV1.1-Regular.otf') format('opentype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+@font-face {
+ font-family: 'Ember Modern Text';
+ src: url('./fonts/EmberModernTextV1.1-Bold.otf') format('opentype');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+@font-face {
+ font-family: 'Ember Modern Text';
+ src: url('./fonts/EmberModernTextV1.1-Italic.otf') format('opentype');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+@font-face {
+ font-family: 'Ember Modern Text';
+ src: url('./fonts/EmberModernTextV1.1-BoldItalic.otf') format('opentype');
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Noto Sans variable webfont declarations */
+@font-face {
+ font-family: 'Noto Sans';
+ src: url('./fonts/NotoSans-VariableFont_wdth,wght.ttf') format('truetype-variations');
+ font-weight: 100 900;
+ font-stretch: 62.5% 100%;
+ font-style: normal;
+ font-display: swap;
+}
+@font-face {
+ font-family: 'Noto Sans';
+ src: url('./fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf') format('truetype-variations');
+ font-weight: 100 900;
+ font-stretch: 62.5% 100%;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Default font-stretch for Noto Sans */
+:root {
+ font-stretch: 96%;
+}
diff --git a/src/common/fonts/AmazonEmberDisplay_Bd.ttf b/src/common/fonts/AmazonEmberDisplay_Bd.ttf
new file mode 100644
index 00000000..18d2fec4
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_Bd.ttf differ
diff --git a/src/common/fonts/AmazonEmberDisplay_BdIt.ttf b/src/common/fonts/AmazonEmberDisplay_BdIt.ttf
new file mode 100644
index 00000000..b9758e17
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_BdIt.ttf differ
diff --git a/src/common/fonts/AmazonEmberDisplay_Md.ttf b/src/common/fonts/AmazonEmberDisplay_Md.ttf
new file mode 100644
index 00000000..68553254
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_Md.ttf differ
diff --git a/src/common/fonts/AmazonEmberDisplay_MdIt.ttf b/src/common/fonts/AmazonEmberDisplay_MdIt.ttf
new file mode 100644
index 00000000..f93be278
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_MdIt.ttf differ
diff --git a/src/common/fonts/AmazonEmberDisplay_Rg.ttf b/src/common/fonts/AmazonEmberDisplay_Rg.ttf
new file mode 100644
index 00000000..b5b38fe0
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_Rg.ttf differ
diff --git a/src/common/fonts/AmazonEmberDisplay_RgIt.ttf b/src/common/fonts/AmazonEmberDisplay_RgIt.ttf
new file mode 100644
index 00000000..0e01f86b
Binary files /dev/null and b/src/common/fonts/AmazonEmberDisplay_RgIt.ttf differ
diff --git a/src/common/fonts/EmberModernTextV1.1-Bold.otf b/src/common/fonts/EmberModernTextV1.1-Bold.otf
new file mode 100755
index 00000000..e3a4cbd0
Binary files /dev/null and b/src/common/fonts/EmberModernTextV1.1-Bold.otf differ
diff --git a/src/common/fonts/EmberModernTextV1.1-BoldItalic.otf b/src/common/fonts/EmberModernTextV1.1-BoldItalic.otf
new file mode 100755
index 00000000..9c4968c9
Binary files /dev/null and b/src/common/fonts/EmberModernTextV1.1-BoldItalic.otf differ
diff --git a/src/common/fonts/EmberModernTextV1.1-Italic.otf b/src/common/fonts/EmberModernTextV1.1-Italic.otf
new file mode 100755
index 00000000..7e0da20e
Binary files /dev/null and b/src/common/fonts/EmberModernTextV1.1-Italic.otf differ
diff --git a/src/common/fonts/EmberModernTextV1.1-Regular.otf b/src/common/fonts/EmberModernTextV1.1-Regular.otf
new file mode 100755
index 00000000..f340064d
Binary files /dev/null and b/src/common/fonts/EmberModernTextV1.1-Regular.otf differ
diff --git a/src/common/fonts/NotoSans-Bold.ttf b/src/common/fonts/NotoSans-Bold.ttf
new file mode 100644
index 00000000..07f0d257
Binary files /dev/null and b/src/common/fonts/NotoSans-Bold.ttf differ
diff --git a/src/common/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf b/src/common/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf
new file mode 100644
index 00000000..6245ba01
Binary files /dev/null and b/src/common/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf differ
diff --git a/src/common/fonts/NotoSans-Italic.ttf b/src/common/fonts/NotoSans-Italic.ttf
new file mode 100644
index 00000000..d9b9e148
Binary files /dev/null and b/src/common/fonts/NotoSans-Italic.ttf differ
diff --git a/src/common/fonts/NotoSans-Medium.ttf b/src/common/fonts/NotoSans-Medium.ttf
new file mode 100644
index 00000000..a44124bb
Binary files /dev/null and b/src/common/fonts/NotoSans-Medium.ttf differ
diff --git a/src/common/fonts/NotoSans-MediumItalic.ttf b/src/common/fonts/NotoSans-MediumItalic.ttf
new file mode 100644
index 00000000..467af1b3
Binary files /dev/null and b/src/common/fonts/NotoSans-MediumItalic.ttf differ
diff --git a/src/common/fonts/NotoSans-Regular.ttf b/src/common/fonts/NotoSans-Regular.ttf
new file mode 100644
index 00000000..4bac02f2
Binary files /dev/null and b/src/common/fonts/NotoSans-Regular.ttf differ
diff --git a/src/common/fonts/NotoSans-VariableFont_wdth,wght.ttf b/src/common/fonts/NotoSans-VariableFont_wdth,wght.ttf
new file mode 100644
index 00000000..9530d84d
Binary files /dev/null and b/src/common/fonts/NotoSans-VariableFont_wdth,wght.ttf differ
diff --git a/src/common/image-example-1.png b/src/common/image-example-1.png
new file mode 100644
index 00000000..353289e3
Binary files /dev/null and b/src/common/image-example-1.png differ
diff --git a/src/common/image-example-2.png b/src/common/image-example-2.png
new file mode 100644
index 00000000..9c0dbc82
Binary files /dev/null and b/src/common/image-example-2.png differ
diff --git a/src/common/image-example-3.png b/src/common/image-example-3.png
new file mode 100644
index 00000000..5720f567
Binary files /dev/null and b/src/common/image-example-3.png differ
diff --git a/src/common/logo.svg b/src/common/logo.svg
new file mode 100644
index 00000000..eae2fb04
--- /dev/null
+++ b/src/common/logo.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/common/mount.tsx b/src/common/mount.tsx
new file mode 100644
index 00000000..e4426c14
--- /dev/null
+++ b/src/common/mount.tsx
@@ -0,0 +1,27 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+/* eslint-disable @eslint-react/naming-convention/filename-extension */
+import React from 'react';
+import { createRoot, Root } from 'react-dom/client';
+
+const rootMap = new WeakMap();
+
+export function mount(element: React.ReactElement, container: HTMLElement): void {
+ let root = rootMap.get(container);
+
+ if (!root) {
+ root = createRoot(container);
+ rootMap.set(container, root);
+ }
+
+ root.render(element);
+}
+
+export function unmount(container: HTMLElement): void {
+ const root = rootMap.get(container);
+
+ if (root) {
+ root.unmount();
+ rootMap.delete(container);
+ }
+}
diff --git a/src/common/theme-core.ts b/src/common/theme-core.ts
new file mode 100644
index 00000000..0819bc8c
--- /dev/null
+++ b/src/common/theme-core.ts
@@ -0,0 +1,540 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+
+// ============================================================================
+// Theme for new Core default
+// ============================================================================
+
+export function generateThemeConfig(customAccentColor?: { light: string; dark: string }, fontFamily?: string) {
+ const isEmberModern = fontFamily?.includes('Ember Modern Text') ?? false;
+ const isNotoSans = fontFamily?.includes('Noto Sans') ?? false;
+ const headingFontWeight = isEmberModern ? '700' : isNotoSans ? '600' : '500';
+ // Primary accent colors
+
+ const colorSelectedAccent = customAccentColor || { light: '#1b232d', dark: '#F9F9FB' };
+
+ const colorSelectedAccentSubtle = { light: '#F6F6F9', dark: '#06080A' };
+
+ const colorSelectedAccentSubtleHover = { light: '#EBEBF0', dark: '#131920' };
+
+ // Secondary accent colors (darker/more saturated variant)
+ const colorSelectedAccentSecondary = { light: '#1b232d', dark: '#F9F9FB' };
+
+ // Neutral colors
+ const colorNeutralDefault = { light: '#1b232d', dark: '#f3f3f7' };
+ const colorNeutralInverse = { light: '#ffffff', dark: '#131920' };
+ const colorNeutralBackground = { light: '#F6F6F9', dark: '#333843' };
+
+ // Toned down text color
+ const colorTextBodySecondary = { light: '#656871', dark: '#B4B4BB' };
+
+ // Status colors
+ const colorSuccess = { light: '#008559', dark: '#008559' };
+
+ return {
+ tokens: {
+ fontFamilyBase: "'Noto Sans', 'Helvetica Neue', Roboto, Arial, sans-serif",
+ colorTextBodyDefault: { light: '#161D26', dark: '#c6c6cd' },
+ colorTextBodySecondary: colorTextBodySecondary,
+
+ // ========================================================================
+ // BUTTONS - Normal
+ // ========================================================================
+ colorBorderButtonNormalDefault: colorNeutralDefault,
+ colorBorderButtonNormalHover: colorSelectedAccent,
+ colorBorderButtonNormalActive: colorSelectedAccentSecondary,
+
+ colorBackgroundButtonNormalHover: colorNeutralBackground,
+ colorBackgroundButtonNormalActive: colorSelectedAccentSubtleHover,
+
+ colorTextButtonNormalDefault: colorNeutralDefault,
+ colorTextButtonNormalHover: colorSelectedAccent,
+ colorTextButtonNormalActive: colorSelectedAccentSecondary,
+
+ // ========================================================================
+ // BUTTONS - Primary
+ // ========================================================================
+ colorBackgroundButtonPrimaryDefault: colorSelectedAccentSecondary,
+ colorBackgroundButtonPrimaryHover: { light: '#06080A', dark: '#FFFFFF' },
+ colorBackgroundButtonPrimaryActive: colorSelectedAccentSecondary,
+
+ colorTextButtonPrimaryDefault: colorNeutralInverse,
+ colorTextButtonPrimaryHover: colorNeutralInverse,
+ colorTextButtonPrimaryActive: colorNeutralInverse,
+
+ // ========================================================================
+ // BUTTONS - Link
+ // ========================================================================
+ colorBackgroundButtonLinkHover: { light: '#F6F6F9', dark: '#333843' },
+ colorBackgroundButtonLinkActive: colorSelectedAccentSubtleHover,
+
+ colorTextLinkButtonNormalDefault: colorSelectedAccent,
+
+ // ========================================================================
+ // BUTTONS - Toggle
+ // ========================================================================
+ colorBackgroundToggleButtonNormalPressed: colorSelectedAccentSubtleHover,
+ colorBorderToggleButtonNormalPressed: colorSelectedAccent,
+ colorTextToggleButtonNormalPressed: colorSelectedAccent,
+
+ // ========================================================================
+ // CONTROLS - Checkboxes, Radio, Toggle
+ // ========================================================================
+ colorBackgroundControlChecked: colorSelectedAccent,
+ //colorBackgroundToggleCheckedDisabled: colorSelectedAccentDisabled,
+
+ // ========================================================================
+ // LINKS & TEXT
+ // ========================================================================
+ colorTextLinkDefault: { light: '#06080A', dark: '#EBEBF0' },
+ colorTextLinkHover: { light: '#424650', dark: '#FFFFFF' },
+ colorTextAccent: colorSelectedAccent,
+
+ // ========================================================================
+ // SELECTION & FOCUS
+ // ========================================================================
+ colorBorderItemFocused: colorSelectedAccent,
+ colorBorderItemSelected: colorSelectedAccent,
+ colorBackgroundItemSelected: colorSelectedAccentSubtle,
+ colorBackgroundLayoutToggleSelectedDefault: colorSelectedAccent,
+
+ // ========================================================================
+ // SEGMENTS & TABS
+ // ========================================================================
+ colorBackgroundSegmentActive: colorSelectedAccent,
+
+ // ========================================================================
+ // SLIDER
+ // ========================================================================
+ colorBackgroundSliderRangeDefault: colorSelectedAccent,
+ colorBackgroundSliderHandleDefault: colorSelectedAccent,
+
+ // ========================================================================
+ // PROGRESS BAR
+ // ========================================================================
+ colorBackgroundProgressBarValueDefault: colorSelectedAccent,
+
+ // ========================================================================
+ // NOTIFICATIONS
+ // ========================================================================
+ colorBackgroundNotificationGreen: colorSuccess,
+ colorBackgroundNotificationBlue: { light: '#0033CC', dark: '#0033CC' },
+ colorTextNotificationDefault: { light: '#ffffff', dark: '#ffffff' },
+
+ // ========================================================================
+ // STATUS
+ // ========================================================================
+ colorTextStatusInfo: { light: '#0033CC', dark: '#7598FF' },
+ // colorTextStatusWarning: { light: '#855900', dark: '#ffe347' },
+ // colorTextStatusError: { light: '#DB0000', dark: '#ff7a7a' },
+
+ // ========================================================================
+ // TYPOGRAPHY - Headings
+ // ========================================================================
+ colorTextBreadcrumbCurrent: colorSelectedAccent,
+
+ // H1
+ fontSizeHeadingXl: '26px',
+ lineHeightHeadingXl: '32px',
+ fontWeightHeadingXl: headingFontWeight,
+
+ // H2
+ fontSizeHeadingL: '22px',
+ lineHeightHeadingL: '26px',
+ fontWeightHeadingL: headingFontWeight,
+ //letterSpacingHeadingL: '20px',
+
+ // H3
+ fontSizeHeadingM: '18px',
+ lineHeightHeadingM: '24px',
+ fontWeightHeadingM: headingFontWeight,
+
+ // H4
+ fontSizeHeadingS: '16px',
+ lineHeightHeadingS: '20px',
+ fontWeightHeadingS: headingFontWeight,
+
+ // H5
+ fontSizeHeadingXs: '14px',
+ lineHeightHeadingXs: '20px',
+ fontWeightHeadingXs: headingFontWeight,
+
+ //fontFamilyHeading: 'Ember Modern Display',
+
+ // ========================================================================
+ // TYPOGRAPHY - Other
+ // ========================================================================
+ fontWeightButton: headingFontWeight,
+
+ // ========================================================================
+ // BORDERS - Width
+ // ========================================================================
+ borderWidthButton: '1px',
+ borderWidthToken: '1px',
+ borderWidthAlert: '0px',
+ borderItemWidth: '1px',
+
+ // ========================================================================
+ // BORDERS - Radius
+ // ========================================================================
+ borderRadiusAlert: '2px',
+ borderRadiusBadge: '4px',
+ borderRadiusButton: '8px',
+ borderRadiusContainer: '12px',
+ borderRadiusDropdown: '8px',
+ borderRadiusDropzone: '8px',
+ borderRadiusFlashbar: '4px',
+ borderRadiusItem: '8px',
+ borderRadiusInput: '8px',
+ borderRadiusPopover: '8px',
+ borderRadiusTabsFocusRing: '10px',
+ borderRadiusToken: '8px',
+ borderRadiusTutorialPanelItem: '4px',
+
+ // ========================================================================
+ // ICONS - Stroke Width
+ // ========================================================================
+ borderWidthIconSmall: '1.5px',
+ borderWidthIconNormal: '1.5px',
+ borderWidthIconMedium: '2px',
+ borderWidthIconBig: '2px',
+ borderWidthIconLarge: '2.5px',
+ },
+
+ referenceTokens: {
+ color: {
+ primary: {
+ seed: '#1b232d',
+ },
+ },
+ },
+
+ contexts: {
+ 'top-navigation': {
+ tokens: {
+ colorBackgroundContainerContent: { light: '#ffffff', dark: '#161d26' },
+ colorBorderDividerDefault: { light: '#c6c6cd', dark: '#424650' },
+ colorTextTopNavigationTitle: colorNeutralDefault,
+
+ // Interactive elements
+ colorTextInteractiveDefault: colorNeutralDefault,
+ colorTextInteractiveHover: colorSelectedAccent,
+ colorTextInteractiveActive: { light: '#1b232d', dark: '#7598ff' },
+ colorTextAccent: colorNeutralDefault,
+ },
+ },
+ header: {
+ tokens: {
+ // Normal button
+ colorBorderButtonNormalDefault: '#f3f3f7',
+ // colorBorderButtonNormalHover: '#7598ff',
+ // colorBorderButtonNormalActive: '#7598ff',
+
+ colorBackgroundButtonNormalHover: '#1b232d',
+ colorBackgroundButtonNormalActive: '#000833',
+
+ colorTextButtonNormalDefault: '#f3f3f7',
+ // colorTextButtonNormalHover: '#7598ff',
+ // colorTextButtonNormalActive: '#7598ff',
+
+ // Primary button
+ colorBackgroundButtonPrimaryDefault: '#f9f9fb',
+ // colorBackgroundButtonPrimaryHover: '#c2d1ff',
+ // colorBackgroundButtonPrimaryActive: '#f9f9fb',
+
+ colorTextButtonPrimaryDefault: '#131920',
+ colorTextButtonPrimaryHover: '#131920',
+ colorTextButtonPrimaryActive: '#131920',
+ },
+ },
+ flashbar: {
+ tokens: {
+ // Custom flashbar colors
+ colorBackgroundNotificationGreen: colorSuccess,
+ colorBackgroundNotificationBlue: { light: '#0033cc', dark: '#0033cc' },
+ colorTextNotificationDefault: { light: '#ffffff', dark: '#ffffff' },
+ },
+ },
+ alert: {
+ tokens: {
+ colorBackgroundStatusInfo: { light: '#f6f6f9', dark: '#232b37' },
+ colorBackgroundStatusWarning: { light: '#f6f6f9', dark: '#232b37' },
+ colorBackgroundStatusError: { light: '#f6f6f9', dark: '#232b37' },
+ colorBackgroundStatusSuccess: { light: '#f6f6f9', dark: '#232b37' },
+ colorTextStatusInfo: { light: '#0033CC', dark: '#7598FF' },
+ colorBorderStatusInfo: { light: '#0033CC', dark: '#7598FF' },
+ },
+ },
+ },
+ };
+}
+
+// ============================================================================
+// Theme for Console
+// ============================================================================
+
+export function generateThemeConfigConsole() {
+ return {
+ tokens: {
+ fontFamilyBase: "var(--font-amazon-ember, 'Amazon Ember', sans-serif)",
+
+ // ========================================================================
+ // BUTTONS - Normal
+ // ========================================================================
+ colorBorderButtonNormalDefault: { light: '#006CE0', dark: '#42B4FF' },
+ colorBorderButtonNormalHover: { light: '#002A66', dark: '#75CFFF' },
+ colorBorderButtonNormalActive: { light: '#002A66', dark: '#75CFFF' },
+
+ colorBackgroundButtonNormalHover: { light: '#F0FBFF', dark: '#1B232D' },
+ colorBackgroundButtonNormalActive: { light: '#D1F1FF', dark: '#333843' },
+
+ colorTextButtonNormalDefault: { light: '#006CE0', dark: '#42B4FF' },
+ colorTextButtonNormalHover: { light: '#002A66', dark: '#75CFFF' },
+ colorTextButtonNormalActive: { light: '#002A66', dark: '#75CFFF' },
+
+ // ========================================================================
+ // BUTTONS - Primary
+ // ========================================================================
+ colorBackgroundButtonPrimaryDefault: { light: '#FF9900', dark: '#FFB347' },
+ colorBackgroundButtonPrimaryHover: { light: '#FA6F00', dark: '#FFC870' },
+ colorBackgroundButtonPrimaryActive: { light: '#FA6F00', dark: '#FFC870' },
+
+ colorTextButtonPrimaryDefault: { light: '#0F141A', dark: '#0F141A' },
+ colorTextButtonPrimaryHover: { light: '#0F141A', dark: '#0F141A' },
+ colorTextButtonPrimaryActive: { light: '#0F141A', dark: '#0F141A' },
+
+ // ========================================================================
+ // BUTTONS - Link
+ // ========================================================================
+ colorBackgroundButtonLinkHover: { light: '#F0FBFF', dark: '#1B232D' },
+ colorBackgroundButtonLinkActive: { light: '#D1F1FF', dark: '#333843' },
+
+ colorTextLinkButtonNormalDefault: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // BUTTONS - Toggle
+ // ========================================================================
+ colorBackgroundToggleButtonNormalPressed: { light: '#D1F1FF', dark: '#333843' },
+ colorBorderToggleButtonNormalPressed: { light: '#006CE0', dark: '#42B4FF' },
+ colorTextToggleButtonNormalPressed: { light: '#002A66', dark: '#75CFFF' },
+
+ // ========================================================================
+ // CONTROLS - Checkboxes, Radio, Toggle
+ // ========================================================================
+ colorBackgroundControlChecked: { light: '#006CE0', dark: '#42B4FF' },
+ //colorBackgroundToggleCheckedDisabled: { light: '#B8E7FF', dark: '#002A66' },
+
+ // ========================================================================
+ // LINKS & TEXT
+ // ========================================================================
+ colorTextLinkDefault: { light: '#006CE0', dark: '#42B4FF' },
+ colorTextLinkHover: { light: '#002A66', dark: '#75CFFF' },
+ colorTextAccent: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // SELECTION & FOCUS
+ // ========================================================================
+ colorBorderItemFocused: { light: '#006CE0', dark: '#42B4FF' },
+ colorBorderItemSelected: { light: '#006CE0', dark: '#42B4FF' },
+ colorBackgroundItemSelected: { light: '#F0FBFF', dark: '#001129' },
+ colorBackgroundLayoutToggleSelectedDefault: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // SEGMENTS & TABS
+ // ========================================================================
+ colorBackgroundSegmentActive: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // SLIDER
+ // ========================================================================
+ colorBackgroundSliderRangeDefault: { light: '#006CE0', dark: '#42B4FF' },
+ colorBackgroundSliderHandleDefault: { light: '#006CE0', dark: '#42B4FF' },
+ //colorBackgroundSliderRangeActive: { light: '#004A9E', dark: '#75CFFF' },
+ //colorBackgroundSliderHandleActive: { light: '#004A9E', dark: '#75CFFF' },
+
+ // ========================================================================
+ // PROGRESS BAR
+ // ========================================================================
+ colorBackgroundProgressBarValueDefault: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // NOTIFICATIONS
+ // ========================================================================
+ colorBackgroundNotificationGreen: { light: '#00802F', dark: '#2BB534' },
+ colorBackgroundNotificationBlue: { light: '#006CE0', dark: '#42B4FF' },
+
+ // ========================================================================
+ // STATUS
+ // ========================================================================
+ colorTextStatusInfo: { light: '#006CE0', dark: '#42B4FF' },
+ // colorTextStatusWarning: { light: '#855900', dark: '#FFE347' },
+ // colorTextStatusError: { light: '#DB0000', dark: '#FF7A7A' },
+
+ colorTextBreadcrumbCurrent: { light: '#656871', dark: '#8c8c94' },
+
+ // ========================================================================
+ // TYPOGRAPHY - Headings
+ // ========================================================================
+ // Display Large
+ // fontSizeDisplayL: '42px',
+ // lineHeightDisplayL: '48px',
+ fontWeightDisplayL: '700',
+
+ // H1
+ fontSizeHeadingXl: '24px',
+ lineHeightHeadingXl: '30px',
+ fontWeightHeadingXl: '700',
+
+ // H2
+ fontSizeHeadingL: '20px',
+ lineHeightHeadingL: '24px',
+ fontWeightHeadingL: '700',
+ //letterSpacingHeadingL: '-0.015em',
+
+ // H3
+ fontSizeHeadingM: '18px',
+ lineHeightHeadingM: '22px',
+ fontWeightHeadingM: '700',
+
+ // H4
+ fontSizeHeadingS: '16px',
+ lineHeightHeadingS: '20px',
+ fontWeightHeadingS: '700',
+
+ // H5
+ fontSizeHeadingXs: '14px',
+ lineHeightHeadingXs: '18px',
+ fontWeightHeadingXs: '700',
+
+ //fontFamilyHeading: 'Ember Modern Display',
+
+ // ========================================================================
+ // TYPOGRAPHY - Other
+ // ========================================================================
+ fontWeightButton: '700',
+
+ // ========================================================================
+ // BORDERS - Width
+ // ========================================================================
+ borderWidthButton: '2px',
+ borderWidthToken: '2px',
+ borderWidthAlert: '2px',
+ borderItemWidth: '2px',
+
+ // ========================================================================
+ // BORDERS - Radius
+ // ========================================================================
+ borderRadiusAlert: '12px',
+ borderRadiusBadge: '4px',
+ borderRadiusButton: '20px',
+ borderRadiusContainer: '16px',
+ borderRadiusDropdown: '8px',
+ borderRadiusDropzone: '12px',
+ borderRadiusFlashbar: '12px',
+ borderRadiusItem: '8px',
+ borderRadiusInput: '8px',
+ borderRadiusPopover: '8px',
+ borderRadiusTabsFocusRing: '20px',
+ borderRadiusToken: '8px',
+ borderRadiusTutorialPanelItem: '8px',
+
+ // ========================================================================
+ // ICONS - Stroke Width
+ // ========================================================================
+ borderWidthIconSmall: '2px',
+ borderWidthIconNormal: '2px',
+ borderWidthIconMedium: '2px',
+ borderWidthIconBig: '3px',
+ borderWidthIconLarge: '4px',
+ },
+
+ contexts: {
+ 'top-navigation': {
+ tokens: {
+ //colorBackgroundContainerContent: { light: '#161D26', dark: '#161D26' },
+ },
+ },
+ header: {
+ tokens: {
+ // Normal button
+ colorBorderButtonNormalDefault: '#f3f3f7',
+ // colorBorderButtonNormalHover: '#7598ff',
+ // colorBorderButtonNormalActive: '#7598ff',
+
+ colorBackgroundButtonNormalHover: '#1b232d',
+ colorBackgroundButtonNormalActive: '#000833',
+
+ colorTextButtonNormalDefault: '#f3f3f7',
+ // colorTextButtonNormalHover: '#7598ff',
+ // colorTextButtonNormalActive: '#7598ff',
+
+ // Primary button
+ colorBackgroundButtonPrimaryDefault: '#f9f9fb',
+ // colorBackgroundButtonPrimaryHover: '#c2d1ff',
+ // colorBackgroundButtonPrimaryActive: '#f9f9fb',
+
+ colorTextButtonPrimaryDefault: '#131920',
+ colorTextButtonPrimaryHover: '#131920',
+ colorTextButtonPrimaryActive: '#131920',
+ },
+ },
+ flashbar: {
+ tokens: {
+ // Custom flashbar colors
+ //colorBackgroundNotificationBlue: { light: '#0033cc', dark: '#0033cc' },
+ },
+ },
+ alert: {
+ tokens: {
+ colorBackgroundStatusInfo: { light: '#F0FBFF', dark: '#001129' },
+ colorBackgroundStatusWarning: { light: '#FFFEF0', dark: '#191100' },
+ colorBackgroundStatusError: { light: '#FFF5F5', dark: '#1F0000' },
+ colorBackgroundStatusSuccess: { light: '#EFFFF1', dark: '#001401' },
+ colorTextStatusInfo: { light: '#006CE0', dark: '#42B4FF' },
+ colorBorderStatusInfo: { light: '#006CE0', dark: '#42B4FF' },
+ },
+ },
+ },
+ };
+}
+
+export const themeCoreConfig = generateThemeConfig();
+
+export const colorTextLinkSecondOption = { light: '#295EFF', dark: '#7598FF' };
+
+// ============================================================================
+// Theme Comparison Utilities
+// ============================================================================
+
+/**
+ * Utility for comparing different theme design directions.
+ * Ensures complete theme isolation by resetting before each application.
+ */
+export function createThemeComparison() {
+ return {
+ /**
+ * Apply Design Direction A with optional custom accent color
+ */
+ applyDirectionA: (customAccentColor?: { light: string; dark: string }) => {
+ const themeA = generateThemeConfig(customAccentColor);
+ // applyCustomTheme handles reset automatically
+ return themeA;
+ },
+
+ /**
+ * Apply Design Direction B
+ */
+ applyDirectionB: () => {
+ const themeB = generateThemeConfigConsole();
+ // applyCustomTheme handles reset automatically
+ return themeB;
+ },
+
+ /**
+ * Get theme config without applying (for inspection/comparison)
+ */
+ getThemeConfig: (direction: 'A' | 'B', customAccentColor?: { light: string; dark: string }) => {
+ return direction === 'A' ? generateThemeConfig(customAccentColor) : generateThemeConfigConsole();
+ },
+ };
+}
diff --git a/src/pages/cards/index.tsx b/src/pages/cards/index.tsx
index f7e11d3e..33cbea92 100644
--- a/src/pages/cards/index.tsx
+++ b/src/pages/cards/index.tsx
@@ -3,6 +3,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
+
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/cards/root.tsx b/src/pages/cards/root.tsx
index fecb5226..c423fb4d 100644
--- a/src/pages/cards/root.tsx
+++ b/src/pages/cards/root.tsx
@@ -6,7 +6,9 @@ import { useCollection } from '@cloudscape-design/collection-hooks';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Cards from '@cloudscape-design/components/cards';
import CollectionPreferences from '@cloudscape-design/components/collection-preferences';
+import Flashbar, { FlashbarProps } from '@cloudscape-design/components/flashbar';
import Pagination from '@cloudscape-design/components/pagination';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import TextFilter from '@cloudscape-design/components/text-filter';
import { Distribution } from '../../fake-server/types';
@@ -19,10 +21,12 @@ import {
import { FullPageHeader } from '../commons';
import {
CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
Navigation,
- Notifications,
TableEmptyState,
TableNoMatchState,
+ useGlobalSplitPanel,
} from '../commons/common-components';
import DataProvider from '../commons/data-provider';
import { useLocalStorage } from '../commons/use-local-storage';
@@ -30,6 +34,54 @@ import { CARD_DEFINITIONS, DEFAULT_PREFERENCES, PAGE_SIZE_OPTIONS, VISIBLE_CONTE
import { Breadcrumbs, ToolsContent } from './common-components';
import '../../styles/base.scss';
+import '../../styles/top-navigation.scss';
+
+function StackedNotifications() {
+ const [items, setItems] = useState([
+ {
+ type: 'success',
+ dismissible: true,
+ dismissLabel: 'Dismiss message',
+ content: 'This is a success flash message',
+ id: 'message_5',
+ onDismiss: () => setItems(items => items.filter(item => item.id !== 'message_5')),
+ },
+ {
+ type: 'warning',
+ dismissible: true,
+ dismissLabel: 'Dismiss message',
+ content: 'This is a warning flash message',
+ id: 'message_4',
+ onDismiss: () => setItems(items => items.filter(item => item.id !== 'message_4')),
+ },
+ {
+ type: 'error',
+ dismissible: true,
+ dismissLabel: 'Dismiss message',
+ header: 'Failed to update instance id-4890f893e',
+ content: 'This is a dismissible error message',
+ id: 'message_3',
+ onDismiss: () => setItems(items => items.filter(item => item.id !== 'message_3')),
+ },
+ ]);
+
+ return (
+
+ );
+}
interface DetailsCardsProps {
loadHelpPanelContent: () => void;
@@ -113,26 +165,41 @@ function DetailsCards({ loadHelpPanelContent }: DetailsCardsProps) {
export function App() {
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- contentType="cards"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- stickyNotifications={true}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ contentType="cards"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ stickyNotifications={true}
+ />
+ >
);
}
diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx
index 00340308..b00d6ac2 100644
--- a/src/pages/chat/index.tsx
+++ b/src/pages/chat/index.tsx
@@ -5,24 +5,30 @@ import { createRoot } from 'react-dom/client';
import { I18nProvider } from '@cloudscape-design/components/i18n';
import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json';
+import { applyTheme } from '@cloudscape-design/components/theming';
-import { CustomAppLayout, Notifications } from '../commons/common-components';
+import { themeCoreConfig } from '../../common/theme-core';
+import { CustomAppLayout, DemoTopNavigation, Notifications } from '../commons/common-components';
import Chat from './chat';
import '../../styles/base.scss';
-
+import '../../styles/top-navigation.scss';
function App() {
return (
- }
- notifications={ }
- />
+ <>
+
+ }
+ notifications={ }
+ />
+ >
);
}
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/commons/common-components.tsx b/src/pages/commons/common-components.tsx
index 479f3613..61cf4c9b 100644
--- a/src/pages/commons/common-components.tsx
+++ b/src/pages/commons/common-components.tsx
@@ -15,6 +15,9 @@ import { isVisualRefresh } from '../../common/apply-mode';
// backward compatibility
export * from './index';
+export { DemoTopNavigation } from './top-navigation';
+export { GlobalSplitPanelContent } from './split-panel-content';
+export { useGlobalSplitPanel } from './use-global-split-panel';
export const ec2NavItems = [
{ type: 'link', text: 'Instances', href: '#/instances' },
diff --git a/src/pages/commons/external-link-group.tsx b/src/pages/commons/external-link-group.tsx
index c5f6c80e..ab86a143 100644
--- a/src/pages/commons/external-link-group.tsx
+++ b/src/pages/commons/external-link-group.tsx
@@ -22,7 +22,7 @@ interface ExternalLinkGroupProps {
function ExternalLinkItem({ href, text }: ExternalLinkItemProps) {
return (
-
+
{text}
);
diff --git a/src/pages/commons/global-drawer-plugin.tsx b/src/pages/commons/global-drawer-plugin.tsx
new file mode 100644
index 00000000..1f5505a1
--- /dev/null
+++ b/src/pages/commons/global-drawer-plugin.tsx
@@ -0,0 +1,53 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import Box from '@cloudscape-design/components/box';
+import Drawer from '@cloudscape-design/components/drawer';
+import Header from '@cloudscape-design/components/header';
+import awsuiPlugins from '@cloudscape-design/components/internal/plugins';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+
+import { mount, unmount } from '../../common/mount';
+
+function GlobalDrawerContent() {
+ return (
+ Design exploration}>
+
+ This is a custom global drawer component that appears across all demo pages.
+ You can add any content here that you want to be accessible from all pages.
+
+ This drawer is registered using the Cloudscape AppLayout plugin system and appears in the toolbar.
+
+
+
+ );
+}
+
+// Register the global drawer plugin
+export function registerGlobalDrawer() {
+ awsuiPlugins.appLayout.registerDrawer({
+ id: 'global-drawer-demo',
+ //type: 'global',
+ defaultActive: false,
+ resizable: true,
+ defaultSize: 400,
+ preserveInactiveContent: true,
+ ariaLabels: {
+ closeButton: 'Close global drawer',
+ content: 'Global drawer content',
+ triggerButton: 'Open global drawer',
+ resizeHandle: 'Resize global drawer',
+ },
+ trigger: {
+ iconSvg: `
+ `,
+ },
+ mountContent: container => {
+ mount( , container);
+ },
+ unmountContent: container => {
+ unmount(container);
+ },
+ });
+}
diff --git a/src/pages/commons/index.ts b/src/pages/commons/index.ts
index 965e1017..a62576c4 100644
--- a/src/pages/commons/index.ts
+++ b/src/pages/commons/index.ts
@@ -8,3 +8,4 @@ export { HelpPanelProvider, useHelpPanel } from './help-panel';
export { InfoLink } from './info-link';
export { Navigation, navItems } from './navigation';
export { Notifications } from './notifications';
+export { DemoTopNavigation } from './top-navigation';
diff --git a/src/pages/commons/navigation.tsx b/src/pages/commons/navigation.tsx
index c85ae947..2b81cd28 100644
--- a/src/pages/commons/navigation.tsx
+++ b/src/pages/commons/navigation.tsx
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import React from 'react';
+import React, { useState } from 'react';
import SideNavigation, { SideNavigationProps } from '@cloudscape-design/components/side-navigation';
@@ -29,11 +29,6 @@ export const navItems: SideNavigationProps['items'] = [
},
];
-const defaultOnFollowHandler: SideNavigationProps['onFollow'] = event => {
- // keep the locked href for our demo pages
- event.preventDefault();
-};
-
interface NavigationProps {
activeHref?: string;
header?: SideNavigationProps['header'];
@@ -42,10 +37,18 @@ interface NavigationProps {
}
export function Navigation({
- activeHref,
+ activeHref: activeHrefProp,
header = navHeader,
items = navItems,
- onFollowHandler = defaultOnFollowHandler,
+ onFollowHandler,
}: NavigationProps) {
- return ;
+ const [activeHref, setActiveHref] = useState(activeHrefProp || '#/distributions');
+
+ const handleFollow: SideNavigationProps['onFollow'] = event => {
+ event.preventDefault();
+ setActiveHref(event.detail.href);
+ onFollowHandler?.(event);
+ };
+
+ return ;
}
diff --git a/src/pages/commons/split-panel-content.tsx b/src/pages/commons/split-panel-content.tsx
new file mode 100644
index 00000000..15fbe923
--- /dev/null
+++ b/src/pages/commons/split-panel-content.tsx
@@ -0,0 +1,677 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useEffect, useState } from 'react';
+
+import Box from '@cloudscape-design/components/box';
+import Button from '@cloudscape-design/components/button';
+import Checkbox from '@cloudscape-design/components/checkbox';
+import ColumnLayout from '@cloudscape-design/components/column-layout';
+import FormField from '@cloudscape-design/components/form-field';
+import Input from '@cloudscape-design/components/input';
+import Select, { SelectProps } from '@cloudscape-design/components/select';
+import Slider from '@cloudscape-design/components/slider';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+
+import { applyCustomTheme } from '../../common/apply-theme';
+import {
+ colorTextLinkSecondOption,
+ generateThemeConfig,
+ generateThemeConfigConsole,
+ themeCoreConfig,
+} from '../../common/theme-core';
+
+interface ThemeConfig {
+ colorSelectedAccent?: string;
+ borderWidthButton?: string;
+ borderWidthField?: string;
+ borderWidthIconSmall?: string;
+ borderWidthIconNormal?: string;
+ borderWidthIconMedium?: string;
+ borderWidthIconBig?: string;
+ borderWidthIconLarge?: string;
+ borderRadiusButton?: string;
+ borderRadiusInput?: string;
+ borderRadiusContainer?: string;
+ spaceScaledXxxs?: string;
+ spaceScaledXxs?: string;
+ spaceScaledXs?: string;
+ spaceScaledS?: string;
+ spaceScaledM?: string;
+ spaceScaledL?: string;
+ spaceScaledXl?: string;
+ spaceScaledXxl?: string;
+ colorBackgroundButtonPrimaryDefault?: string;
+ colorBackgroundButtonPrimaryHover?: string;
+ colorTextButtonPrimaryDefault?: string;
+ fontSizeBodyS?: string;
+ fontSizeBodyM?: string;
+ fontSizeHeadingXl?: string;
+ fontSizeHeadingL?: string;
+ fontSizeHeadingM?: string;
+ fontSizeHeadingS?: string;
+ fontSizeHeadingXs?: string;
+ lineHeightHeadingXl?: string;
+ lineHeightHeadingL?: string;
+ lineHeightHeadingM?: string;
+ lineHeightHeadingS?: string;
+ lineHeightHeadingXs?: string;
+ shadowContainer?: string;
+ fontFamilyBase?: string;
+ colorTextLinkDefault?: string;
+ colorTextLinkHover?: string;
+}
+
+export function GlobalSplitPanelContent() {
+ // Helper function to extract numeric value from CSS value like "24px" -> "24"
+ const extractNumericValue = (value: string | undefined): string => {
+ if (!value) {
+ return '';
+ }
+ const match = value.match(/^(\d+(?:\.\d+)?)/);
+ return match ? match[1] : value;
+ };
+
+ // Helper function to format color object to string
+ const formatColorValue = (value: { light: string; dark: string } | undefined): string => {
+ if (!value) {
+ return '';
+ }
+ return `light: '${value.light}', dark: '${value.dark}'`;
+ };
+
+ const [fontStretch, setFontStretch] = useState(96);
+ const [consoleTheme, setConsoleTheme] = useState(false);
+ const [checked, setChecked] = useState(false);
+ const [checkedFontSmooth, setCheckedFontSmooth] = useState(true);
+ const [customLinkColor, setCustomLinkColor] = useState(false);
+ const [config, setConfig] = useState({
+ colorSelectedAccent: formatColorValue({ light: '#1b232d', dark: '#f3f3f7' }),
+ borderWidthButton: extractNumericValue((themeCoreConfig.tokens?.borderWidthButton as string) || '2px'),
+ borderWidthIconSmall: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconSmall as string) || '1.5px'),
+ borderWidthIconNormal: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconNormal as string) || '1.5px'),
+ borderWidthIconMedium: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconMedium as string) || '2px'),
+ borderWidthIconBig: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconBig as string) || '2px'),
+ borderWidthIconLarge: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconLarge as string) || '2.5px'),
+ borderRadiusButton: extractNumericValue((themeCoreConfig.tokens?.borderRadiusButton as string) || '8px'),
+ borderRadiusContainer: extractNumericValue((themeCoreConfig.tokens?.borderRadiusContainer as string) || '12px'),
+ fontSizeHeadingXl: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingXl as string) || '26px'),
+ fontSizeHeadingL: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingL as string) || '22px'),
+ fontSizeHeadingM: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingM as string) || '20px'),
+ fontSizeHeadingS: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingS as string) || '18px'),
+ fontSizeHeadingXs: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingXs as string) || '16px'),
+ lineHeightHeadingXl: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingXl as string) || '32px'),
+ lineHeightHeadingL: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingL as string) || '26px'),
+ lineHeightHeadingM: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingM as string) || '24px'),
+ lineHeightHeadingS: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingS as string) || '22px'),
+ lineHeightHeadingXs: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingXs as string) || '20px'),
+ fontFamilyBase: (themeCoreConfig.tokens?.fontFamilyBase as string) || '',
+ });
+
+ const [errors, setErrors] = useState>({});
+
+ const fontFamilyOptions: SelectProps.Options = [
+ { label: 'NotoSans', value: "'NotoSans', 'Noto Sans', sans-serif" },
+ { label: 'AmazonEmberDisplay', value: "'AmazonEmberDisplay', 'Amazon Ember Display', sans-serif" },
+ { label: 'EmberModernText', value: "'Ember Modern Text', sans-serif" },
+ ];
+
+ // Apply CSS class to body when toggle changes
+ useEffect(() => {
+ const applyThemeChanges = () => {
+ try {
+ // Parse colorSelectedAccent if present
+ let customAccentColor;
+ if (config.colorSelectedAccent) {
+ const lightMatch = config.colorSelectedAccent.match(/light:\s*'([^']+)'/);
+ const darkMatch = config.colorSelectedAccent.match(/dark:\s*'([^']+)'/);
+ if (lightMatch && darkMatch) {
+ customAccentColor = { light: lightMatch[1], dark: darkMatch[1] };
+ }
+ }
+
+ // Select base theme based on checkbox selection
+ let baseTheme;
+ let shouldApplyCustomTokens = true;
+
+ if (consoleTheme) {
+ // Console theme: Minimal theme with only specific tokens
+ // Don't apply form customizations for Console theme
+ baseTheme = generateThemeConfigConsole();
+ shouldApplyCustomTokens = false;
+ } else {
+ // New Core theme: Complete theme with form customizations
+ baseTheme = customAccentColor
+ ? generateThemeConfig(customAccentColor, config.fontFamilyBase)
+ : generateThemeConfig(undefined, config.fontFamilyBase);
+ shouldApplyCustomTokens = true;
+ }
+
+ // Build the theme object
+ let themeTokens: any = { ...baseTheme.tokens };
+
+ // Only apply custom tokens from form for Option A
+ if (shouldApplyCustomTokens) {
+ const colorTokenKeys = ['colorTextLinkDefault', 'colorTextLinkHover'];
+ const customTokens = Object.fromEntries(
+ Object.entries(config)
+ .filter(([key, value]) => key !== 'colorSelectedAccent' && value !== undefined && value !== '')
+ .map(([key, value]) => {
+ // Parse color tokens that use "light: '...', dark: '...'" format
+ if (colorTokenKeys.includes(key)) {
+ const lightMatch = String(value).match(/light:\s*'([^']+)'/);
+ const darkMatch = String(value).match(/dark:\s*'([^']+)'/);
+ if (lightMatch && darkMatch) {
+ return [key, { light: lightMatch[1], dark: darkMatch[1] }];
+ }
+ return [key, undefined]; // skip invalid color values
+ }
+ // Font family should not have 'px' appended
+ if (key === 'fontFamilyBase') {
+ return [key, value];
+ }
+ // If value is a number without unit, append 'px'
+ const stringValue = String(value).trim();
+ if (stringValue && /^\d+(\.\d+)?$/.test(stringValue)) {
+ return [key, `${stringValue}px`];
+ }
+ return [key, value];
+ })
+ .filter(([, value]) => value !== undefined),
+ );
+ themeTokens = { ...themeTokens, ...customTokens };
+ }
+
+ // Apply borderRadiusFlashbar only when toggle is on
+ if (checked) {
+ themeTokens.borderRadiusFlashbar = '0px';
+ }
+
+ const updatedTheme = {
+ tokens: themeTokens,
+ referenceTokens: (baseTheme as any).referenceTokens || {},
+ contexts: (baseTheme as any).contexts || {},
+ };
+
+ // Apply theme - reset happens automatically in applyCustomTheme
+ applyCustomTheme(updatedTheme as any);
+ } catch (error) {
+ console.error('Failed to apply theme:', error);
+ }
+ };
+
+ if (checked) {
+ document.body.classList.add('filled-flashbar');
+ } else {
+ document.body.classList.remove('filled-flashbar');
+ }
+
+ // Apply custom CSS class when Console checkbox is disabled (unchecked)
+ if (!consoleTheme) {
+ document.body.classList.add('custom-css-enabled');
+ } else {
+ document.body.classList.remove('custom-css-enabled');
+ }
+
+ // Apply theme changes when toggle state or console theme changes
+ applyThemeChanges();
+ }, [checked, config, consoleTheme, customLinkColor]);
+
+ // Toggle font-smooth-auto class on body
+ // When checkbox is OFF (default), font smoothing is subpixel-antialiased (normal browser behavior)
+ // When checkbox is ON, we add the class to set font-smoothing to auto
+ useEffect(() => {
+ if (checkedFontSmooth) {
+ document.body.classList.remove('font-smooth-auto');
+ } else {
+ document.body.classList.add('font-smooth-auto');
+ }
+ }, [checkedFontSmooth]);
+
+ // Apply font-stretch globally via injected style tag
+ useEffect(() => {
+ const styleId = 'font-stretch-override';
+ let styleEl = document.getElementById(styleId) as HTMLStyleElement | null;
+ if (!styleEl) {
+ styleEl = document.createElement('style');
+ styleEl.id = styleId;
+ document.head.appendChild(styleEl);
+ }
+ styleEl.textContent = `* { font-stretch: ${fontStretch}% !important; }`;
+ return () => {
+ styleEl?.remove();
+ };
+ }, [fontStretch]);
+
+ const handleInputChange = (key: keyof ThemeConfig, value: string) => {
+ setConfig(prev => ({ ...prev, [key]: value }));
+ if (errors[key]) {
+ setErrors(prev => {
+ const newErrors = { ...prev };
+ delete newErrors[key];
+ return newErrors;
+ });
+ }
+ };
+
+ const applyThemeChanges = () => {
+ try {
+ // Parse colorSelectedAccent if present
+ let customAccentColor;
+ if (config.colorSelectedAccent) {
+ const lightMatch = config.colorSelectedAccent.match(/light:\s*'([^']+)'/);
+ const darkMatch = config.colorSelectedAccent.match(/dark:\s*'([^']+)'/);
+ if (lightMatch && darkMatch) {
+ customAccentColor = { light: lightMatch[1], dark: darkMatch[1] };
+ }
+ }
+
+ // Select base theme based on checkbox selection
+ let baseTheme;
+ let shouldApplyCustomTokens = true;
+
+ if (consoleTheme) {
+ // Console theme: Minimal theme with only specific tokens
+ // Don't apply form customizations for Console theme
+ baseTheme = generateThemeConfigConsole();
+ shouldApplyCustomTokens = false;
+ } else {
+ // New Core theme: Complete theme with form customizations
+ baseTheme = customAccentColor
+ ? generateThemeConfig(customAccentColor, config.fontFamilyBase)
+ : generateThemeConfig(undefined, config.fontFamilyBase);
+ shouldApplyCustomTokens = true;
+ }
+
+ // Build the theme object
+ let themeTokens: any = { ...baseTheme.tokens };
+
+ // Only apply custom tokens from form for Option A
+ if (shouldApplyCustomTokens) {
+ const customTokens = Object.fromEntries(
+ Object.entries(config)
+ .filter(([key, value]) => key !== 'colorSelectedAccent' && value !== undefined && value !== '')
+ .map(([key, value]) => {
+ // Font family should not have 'px' appended
+ if (key === 'fontFamilyBase') {
+ return [key, value];
+ }
+ // If value is a number without unit, append 'px'
+ const stringValue = String(value).trim();
+ if (stringValue && /^\d+(\.\d+)?$/.test(stringValue)) {
+ return [key, `${stringValue}px`];
+ }
+ return [key, value];
+ }),
+ );
+ themeTokens = { ...themeTokens, ...customTokens };
+ }
+
+ // Apply borderRadiusFlashbar only when toggle is on
+ if (checked) {
+ themeTokens.borderRadiusFlashbar = '0px';
+ }
+
+ const updatedTheme = {
+ tokens: themeTokens,
+ referenceTokens: (baseTheme as any).referenceTokens || {},
+ contexts: (baseTheme as any).contexts || {},
+ };
+
+ // Apply theme - reset happens automatically in applyCustomTheme
+ applyCustomTheme(updatedTheme as any);
+ } catch (error) {
+ console.error('Failed to apply theme:', error);
+ }
+ };
+
+ const resetTheme = () => {
+ setConsoleTheme(false);
+ setChecked(false);
+ setCheckedFontSmooth(true);
+ setFontStretch(100);
+ setConfig({
+ colorSelectedAccent: formatColorValue({ light: '#1b232d', dark: '#f3f3f7' }),
+ borderWidthButton: extractNumericValue((themeCoreConfig.tokens?.borderWidthButton as string) || '1px'),
+ borderWidthIconSmall: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconSmall as string) || '1.5px'),
+ borderWidthIconNormal: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconNormal as string) || '1.5px'),
+ borderWidthIconMedium: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconMedium as string) || '2px'),
+ borderWidthIconBig: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconBig as string) || '2px'),
+ borderWidthIconLarge: extractNumericValue((themeCoreConfig.tokens?.borderWidthIconLarge as string) || '2.5px'),
+ borderRadiusButton: extractNumericValue((themeCoreConfig.tokens?.borderRadiusButton as string) || '8px'),
+ borderRadiusInput: extractNumericValue((themeCoreConfig.tokens?.borderRadiusInput as string) || '8px'),
+ borderRadiusContainer: extractNumericValue((themeCoreConfig.tokens?.borderRadiusContainer as string) || '12px'),
+ fontSizeHeadingXl: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingXl as string) || '26px'),
+ fontSizeHeadingL: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingL as string) || '22px'),
+ fontSizeHeadingM: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingM as string) || '20px'),
+ fontSizeHeadingS: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingS as string) || '18px'),
+ fontSizeHeadingXs: extractNumericValue((themeCoreConfig.tokens?.fontSizeHeadingXs as string) || '16px'),
+ lineHeightHeadingXl: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingXl as string) || '32px'),
+ lineHeightHeadingL: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingL as string) || '26px'),
+ lineHeightHeadingM: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingM as string) || '24px'),
+ lineHeightHeadingS: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingS as string) || '22px'),
+ lineHeightHeadingXs: extractNumericValue((themeCoreConfig.tokens?.lineHeightHeadingXs as string) || '20px'),
+ fontFamilyBase: (themeCoreConfig.tokens?.fontFamilyBase as string) || '',
+ });
+ setErrors({});
+ // Reset to Cloudscape defaults by passing undefined
+ applyCustomTheme(undefined);
+ };
+
+ return (
+
+
+
+
+ Theme Selection
+
+ setConsoleTheme(detail.checked)} checked={consoleTheme}>
+ Console
+
+
+
+
+ Font
+
+
+
+ o.value === config.fontFamilyBase) || fontFamilyOptions[0]}
+ onChange={({ detail }) => handleInputChange('fontFamilyBase', detail.selectedOption.value ?? '')}
+ options={fontFamilyOptions}
+ />
+
+
+ setCheckedFontSmooth(detail.checked)} checked={checkedFontSmooth}>
+ font-smooth
+
+
+
+ setFontStretch(detail.value)} value={fontStretch} max={100} min={90} />
+
+
+
+ Accent color
+
+
+
+ handleInputChange('colorSelectedAccent', detail.value)}
+ />
+
+
+ {
+ setCustomLinkColor(detail.checked);
+ if (detail.checked) {
+ // Set default to the second option when toggled on
+ handleInputChange(
+ 'colorTextLinkDefault',
+ `light: '${colorTextLinkSecondOption.light}', dark: '${colorTextLinkSecondOption.dark}'`,
+ );
+ handleInputChange('colorTextLinkHover', "light: '#0033CC', dark: '#C2D1FF'");
+ } else {
+ // Clear custom values when toggled off (will fall back to theme default)
+ handleInputChange('colorTextLinkDefault', '');
+ handleInputChange('colorTextLinkHover', '');
+ }
+ }}
+ checked={customLinkColor}
+ >
+ Use alternate link color
+
+
+ {customLinkColor && (
+ <>
+
+ handleInputChange('colorTextLinkDefault', detail.value)}
+ />
+
+
+ handleInputChange('colorTextLinkHover', detail.value)}
+ />
+
+ >
+ )}
+
+
+
+
+
+ handleInputChange('borderWidthButton', detail.value)}
+ />
+
+
+
+ handleInputChange('borderRadiusButton', detail.value)}
+ />
+
+
+
+ handleInputChange('borderRadiusContainer', detail.value)}
+ />
+
+
+
+ handleInputChange('borderRadiusInput', detail.value)}
+ />
+
+
+
+
+
+
+ Icon stroke
+
+
+
+
+ handleInputChange('borderWidthIconSmall', detail.value)}
+ />
+
+
+
+ handleInputChange('borderWidthIconNormal', detail.value)}
+ />
+
+
+
+ handleInputChange('borderWidthIconMedium', detail.value)}
+ />
+
+
+
+ handleInputChange('borderWidthIconBig', detail.value)}
+ />
+
+
+
+ handleInputChange('borderWidthIconLarge', detail.value)}
+ />
+
+
+
+
+
+
+ Font related themes
+
+
+
+
+ H1
+
+
+ handleInputChange('fontSizeHeadingXl', detail.value)}
+ />
+
+
+
+ handleInputChange('lineHeightHeadingXl', detail.value)}
+ />
+
+
+
+ H2
+
+
+ handleInputChange('fontSizeHeadingL', detail.value)}
+ />
+
+
+
+ handleInputChange('lineHeightHeadingL', detail.value)}
+ />
+
+
+
+ H3
+
+
+ handleInputChange('fontSizeHeadingM', detail.value)}
+ />
+
+
+
+ handleInputChange('lineHeightHeadingM', detail.value)}
+ />
+
+
+
+ H4
+
+
+ handleInputChange('fontSizeHeadingS', detail.value)}
+ />
+
+
+
+ handleInputChange('lineHeightHeadingS', detail.value)}
+ />
+
+
+
+ H5
+
+
+ handleInputChange('fontSizeHeadingXs', detail.value)}
+ />
+
+
+
+ handleInputChange('lineHeightHeadingXs', detail.value)}
+ />
+
+
+
+
+ {/*
+
+ {
+ setChecked(detail.checked);
+ }}
+ checked={checked}
+ >
+ Filled flashbar
+
+
+ */}
+
+
+
+
+
+ Apply Theme
+
+ Reset to Default
+
+
+ );
+}
diff --git a/src/pages/commons/top-navigation.tsx b/src/pages/commons/top-navigation.tsx
new file mode 100644
index 00000000..126660a5
--- /dev/null
+++ b/src/pages/commons/top-navigation.tsx
@@ -0,0 +1,91 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { ReactNode } from 'react';
+import { createPortal } from 'react-dom';
+
+import { ButtonDropdownProps } from '@cloudscape-design/components/button-dropdown';
+import TopNavigation from '@cloudscape-design/components/top-navigation';
+import { TopNavigationProps } from '@cloudscape-design/components/top-navigation';
+import { Mode } from '@cloudscape-design/global-styles';
+
+import { updateDensity, updateDirection, updateMode } from '../../common/apply-mode';
+import logo from '../../common/logo.svg';
+
+/**
+ * This Portal is for demo purposes only due to the additional
+ * header used on the Demo page.
+ */
+interface DemoHeaderPortalProps {
+ children: ReactNode;
+}
+
+const DemoHeaderPortal = ({ children }: DemoHeaderPortalProps) => {
+ const domNode = document.querySelector('#h')!;
+ return createPortal(children, domNode);
+};
+
+export function DemoTopNavigation() {
+ const handlePreferenceChange = (event: CustomEvent) => {
+ const itemId = event.detail.id;
+
+ // Handle mode changes
+ if (itemId === Mode.Light || itemId === Mode.Dark) {
+ updateMode(itemId);
+ }
+ // Handle density changes
+ else if (itemId === 'comfortable' || itemId === 'compact') {
+ updateDensity(itemId);
+ }
+ // Handle direction changes
+ else if (itemId === 'ltr' || itemId === 'rtl') {
+ updateDirection(itemId);
+ }
+ };
+
+ const utilities: TopNavigationProps.Utility[] = [
+ {
+ type: 'menu-dropdown',
+ text: 'Preferences',
+ items: [
+ {
+ text: 'Appearance',
+ items: [
+ { text: 'Light', id: Mode.Light },
+ { text: 'Dark', id: Mode.Dark },
+ ],
+ },
+ {
+ text: 'Directionality',
+ items: [
+ { text: 'Left-to-right', id: 'ltr' },
+ { text: 'Right-to-left', id: 'rtl' },
+ ],
+ },
+ {
+ text: 'Density',
+ items: [
+ { text: 'Comfortable', id: 'comfortable' },
+ { text: 'Compact', id: 'compact' },
+ ],
+ },
+ ],
+ onItemClick: handlePreferenceChange,
+ },
+ ];
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/commons/use-global-split-panel.ts b/src/pages/commons/use-global-split-panel.ts
new file mode 100644
index 00000000..28461acd
--- /dev/null
+++ b/src/pages/commons/use-global-split-panel.ts
@@ -0,0 +1,26 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import { useState } from 'react';
+
+import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
+
+export const useGlobalSplitPanel = () => {
+ const [splitPanelSize, setSplitPanelSize] = useState(400);
+ const [splitPanelOpen, setSplitPanelOpen] = useState(false);
+
+ const onSplitPanelResize: AppLayoutProps['onSplitPanelResize'] = ({ detail: { size } }) => {
+ setSplitPanelSize(size);
+ };
+
+ const onSplitPanelToggle: AppLayoutProps['onSplitPanelToggle'] = ({ detail: { open } }) => {
+ setSplitPanelOpen(open);
+ };
+
+ return {
+ splitPanelOpen,
+ onSplitPanelToggle,
+ splitPanelSize,
+ onSplitPanelResize,
+ splitPanelPreferences: { position: 'side' as const },
+ };
+};
diff --git a/src/pages/commons/with-global-drawer.tsx b/src/pages/commons/with-global-drawer.tsx
new file mode 100644
index 00000000..65fb0711
--- /dev/null
+++ b/src/pages/commons/with-global-drawer.tsx
@@ -0,0 +1,22 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { ReactNode, useEffect, useRef } from 'react';
+
+import { registerGlobalDrawer } from './global-drawer-plugin';
+
+interface WithGlobalDrawerProps {
+ children: ReactNode;
+}
+
+export function WithGlobalDrawer({ children }: WithGlobalDrawerProps) {
+ const registered = useRef(false);
+
+ useEffect(() => {
+ if (!registered.current) {
+ registerGlobalDrawer();
+ registered.current = true;
+ }
+ }, []);
+
+ return <>{children}>;
+}
diff --git a/src/pages/components-overview/buttons-inputs-dropdowns.tsx b/src/pages/components-overview/buttons-inputs-dropdowns.tsx
new file mode 100644
index 00000000..918ccb98
--- /dev/null
+++ b/src/pages/components-overview/buttons-inputs-dropdowns.tsx
@@ -0,0 +1,243 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useState } from 'react';
+
+import Button from '@cloudscape-design/components/button';
+import ButtonGroup from '@cloudscape-design/components/button-group';
+import Calendar from '@cloudscape-design/components/calendar';
+import DatePicker from '@cloudscape-design/components/date-picker';
+import ExpandableSection from '@cloudscape-design/components/expandable-section';
+import Grid from '@cloudscape-design/components/grid';
+import Multiselect, { MultiselectProps } from '@cloudscape-design/components/multiselect';
+import SegmentedControl from '@cloudscape-design/components/segmented-control';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import ToggleButton from '@cloudscape-design/components/toggle-button';
+
+import { generateDropdownOptions } from './component-data';
+import { Section } from './utils';
+
+function Buttons() {
+ const [selectedSegment, setSelectedSegment] = useState('seg-1');
+ const [toggle1, setToggle1] = useState(true);
+ const [toggle2, setToggle2] = useState(false);
+ const [toggle3, setToggle3] = useState(false);
+ const [toggle4, setToggle4] = useState(true);
+ const [toggle5, setToggle5] = useState(false);
+ const [toggle6, setToggle6] = useState(true);
+
+ return (
+
+
+ Primary button
+ Secondary button
+
+ Tertiary button
+
+
+
+ Primary button
+
+
+ Secondary button
+
+
+
+ Tertiary button
+
+
+
+
+
+
+
+
+ setToggle1(detail.pressed)}
+ pressed={toggle1}
+ iconName="star"
+ pressedIconName="star-filled"
+ >
+ Toggle button
+
+ setToggle2(detail.pressed)}
+ pressed={toggle2}
+ iconName="star"
+ pressedIconName="star-filled"
+ >
+ Toggle button
+
+ setToggle3(detail.pressed)}
+ pressed={toggle3}
+ iconName="star"
+ pressedIconName="star-filled"
+ ariaLabel="Toggle button"
+ />
+ setToggle4(detail.pressed)}
+ pressed={toggle4}
+ iconName="star"
+ pressedIconName="star-filled"
+ ariaLabel="Toggle button pressed"
+ />
+ setToggle5(detail.pressed)}
+ pressed={toggle5}
+ iconName="star"
+ pressedIconName="star-filled"
+ ariaLabel="Toggle button icon"
+ />
+ setToggle6(detail.pressed)}
+ pressed={toggle6}
+ iconName="star"
+ pressedIconName="star-filled"
+ ariaLabel="Toggle button icon pressed"
+ />
+
+
+
+ Toggle button
+
+
+ Toggle button
+
+
+
+
+
+
+ setSelectedSegment(detail.selectedId)}
+ label="Default segmented control"
+ options={[
+ { text: 'Segment 1', id: 'seg-1' },
+ { text: 'Segment 2', id: 'seg-2' },
+ { text: 'Segment 3', id: 'seg-3' },
+ ]}
+ />
+
+ );
+}
+
+function Inputs() {
+ const multiSelectOptions = generateDropdownOptions() as MultiselectProps.Options;
+ const [selectedItems, setSelectedItems] = useState([
+ multiSelectOptions[1],
+ multiSelectOptions[3],
+ ] as MultiselectProps.Options);
+ const [dateValue, setDateValue] = useState('2018-01-02');
+
+ return (
+
+
+ setSelectedItems(detail.selectedOptions)}
+ />
+
+
+
+
+ 'Open calendar'}
+ onChange={({ detail }) => {
+ setDateValue(detail.value);
+ }}
+ />
+ 'Open calendar'}
+ onChange={({ detail }) => {
+ setDateValue(detail.value);
+ }}
+ />
+ 'Open calendar'}
+ onChange={({ detail }) => {
+ setDateValue(detail.value);
+ }}
+ />
+
+
+ setDateValue(detail.value)}
+ value={dateValue}
+ isDateEnabled={date => date.getDay() !== 6 && date.getDay() !== 0}
+ />
+
+
+ Expanded
+
+
+
+
+ );
+}
+
+export default function ButtonsInputsDropdowns() {
+ return (
+
+ );
+}
diff --git a/src/pages/components-overview/charts.tsx b/src/pages/components-overview/charts.tsx
new file mode 100644
index 00000000..bc973726
--- /dev/null
+++ b/src/pages/components-overview/charts.tsx
@@ -0,0 +1,220 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import BarChart from '@cloudscape-design/components/bar-chart';
+import Box from '@cloudscape-design/components/box';
+import Button from '@cloudscape-design/components/button';
+import ColumnLayout from '@cloudscape-design/components/column-layout';
+import LineChart from '@cloudscape-design/components/line-chart';
+import PieChart from '@cloudscape-design/components/pie-chart';
+
+import { Section, SubSection } from './utils';
+
+const formatLargeNumber = (value: number): string => {
+ if (Math.abs(value) >= 1e9) {
+ return (value / 1e9).toFixed(1).replace(/\.0$/, '') + 'G';
+ }
+ if (Math.abs(value) >= 1e6) {
+ return (value / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
+ }
+ if (Math.abs(value) >= 1e3) {
+ return (value / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
+ }
+ return value.toFixed(2);
+};
+
+const formatDateTick = (date: Date): string =>
+ date
+ .toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false })
+ .split(',')
+ .join('\n');
+
+const emptyState = (
+
+ No data available
+
+ There is no data available
+
+
+);
+
+const noMatchState = (
+
+ No matching data
+
+ There is no matching data to display
+
+ Clear filter
+
+);
+
+const barChartDates = [
+ new Date(1601071200000),
+ new Date(1601078400000),
+ new Date(1601085600000),
+ new Date(1601092800000),
+ new Date(1601100000000),
+];
+
+const lineChartDomain: [Date, Date] = [new Date(1600984800000), new Date(1601013600000)];
+
+const lineChartSite1 = [
+ { x: new Date(1600984800000), y: 58020 },
+ { x: new Date(1600985700000), y: 102402 },
+ { x: new Date(1600986600000), y: 104920 },
+ { x: new Date(1600987500000), y: 94031 },
+ { x: new Date(1600988400000), y: 125021 },
+ { x: new Date(1600989300000), y: 159219 },
+ { x: new Date(1600990200000), y: 193082 },
+ { x: new Date(1600991100000), y: 162592 },
+ { x: new Date(1600992000000), y: 274021 },
+ { x: new Date(1600992900000), y: 264286 },
+ { x: new Date(1600993800000), y: 289210 },
+ { x: new Date(1600994700000), y: 256362 },
+ { x: new Date(1600995600000), y: 257306 },
+ { x: new Date(1600996500000), y: 186776 },
+ { x: new Date(1600997400000), y: 294020 },
+ { x: new Date(1600998300000), y: 385975 },
+ { x: new Date(1600999200000), y: 486039 },
+ { x: new Date(1601000100000), y: 490447 },
+ { x: new Date(1601001000000), y: 361845 },
+ { x: new Date(1601001900000), y: 339058 },
+ { x: new Date(1601002800000), y: 298028 },
+ { x: new Date(1601003700000), y: 231902 },
+ { x: new Date(1601004600000), y: 224558 },
+ { x: new Date(1601005500000), y: 253901 },
+ { x: new Date(1601006400000), y: 102839 },
+ { x: new Date(1601007300000), y: 234943 },
+ { x: new Date(1601008200000), y: 204405 },
+ { x: new Date(1601009100000), y: 190391 },
+ { x: new Date(1601010000000), y: 183570 },
+ { x: new Date(1601010900000), y: 162592 },
+ { x: new Date(1601011800000), y: 148910 },
+ { x: new Date(1601012700000), y: 229492 },
+ { x: new Date(1601013600000), y: 293910 },
+];
+
+const lineChartSite2 = [
+ { x: new Date(1600984800000), y: 151023 },
+ { x: new Date(1600985700000), y: 169975 },
+ { x: new Date(1600986600000), y: 176980 },
+ { x: new Date(1600987500000), y: 168852 },
+ { x: new Date(1600988400000), y: 149130 },
+ { x: new Date(1600989300000), y: 147299 },
+ { x: new Date(1600990200000), y: 169552 },
+ { x: new Date(1600991100000), y: 163401 },
+ { x: new Date(1600992000000), y: 154091 },
+ { x: new Date(1600992900000), y: 199516 },
+ { x: new Date(1600993800000), y: 195503 },
+ { x: new Date(1600994700000), y: 189953 },
+ { x: new Date(1600995600000), y: 181635 },
+ { x: new Date(1600996500000), y: 192975 },
+ { x: new Date(1600997400000), y: 205951 },
+ { x: new Date(1600998300000), y: 218958 },
+ { x: new Date(1600999200000), y: 220516 },
+ { x: new Date(1601000100000), y: 213557 },
+ { x: new Date(1601001000000), y: 165899 },
+ { x: new Date(1601001900000), y: 173557 },
+ { x: new Date(1601002800000), y: 172331 },
+ { x: new Date(1601003700000), y: 186492 },
+ { x: new Date(1601004600000), y: 131541 },
+ { x: new Date(1601005500000), y: 142262 },
+ { x: new Date(1601006400000), y: 194091 },
+ { x: new Date(1601007300000), y: 185899 },
+ { x: new Date(1601008200000), y: 173401 },
+ { x: new Date(1601009100000), y: 171635 },
+ { x: new Date(1601010000000), y: 179130 },
+ { x: new Date(1601010900000), y: 185951 },
+ { x: new Date(1601011800000), y: 144091 },
+ { x: new Date(1601012700000), y: 152975 },
+ { x: new Date(1601013600000), y: 157299 },
+];
+
+export default function Charts() {
+ return (
+
+ <>
+
+
+ ({ x, y: [12, 18, 15, 9, 18][i] })),
+ },
+ {
+ title: 'Moderate',
+ type: 'bar',
+ data: barChartDates.map((x, i) => ({ x, y: [8, 11, 12, 11, 13][i] })),
+ },
+ {
+ title: 'Low',
+ type: 'bar',
+ data: barChartDates.map((x, i) => ({ x, y: [7, 9, 8, 7, 5][i] })),
+ },
+ {
+ title: 'Unclassified',
+ type: 'bar',
+ data: barChartDates.map((x, i) => ({ x, y: [14, 8, 6, 4, 6][i] })),
+ },
+ ]}
+ xDomain={barChartDates}
+ yDomain={[0, 50]}
+ i18nStrings={{ xTickFormatter: formatDateTick }}
+ ariaLabel="Stacked bar chart"
+ height={300}
+ stackedBars={true}
+ xTitle="Time (UTC)"
+ yTitle="Error count"
+ empty={emptyState}
+ noMatch={noMatchState}
+ />
+
+
+
+
+
+
+
+ [
+ { key: 'Resource count', value: datum.value },
+ { key: 'Percentage', value: `${((datum.value / sum) * 100).toFixed(0)}%` },
+ { key: 'Last update on', value: datum.lastUpdate },
+ ]}
+ segmentDescription={(datum, sum) => `${datum.value} units, ${((datum.value / sum) * 100).toFixed(0)}%`}
+ ariaDescription="Pie chart showing how many resources are currently in which state."
+ ariaLabel="Pie chart"
+ empty={emptyState}
+ noMatch={noMatchState}
+ />
+
+
+ >
+
+ );
+}
diff --git a/src/pages/components-overview/chat.tsx b/src/pages/components-overview/chat.tsx
new file mode 100644
index 00000000..cbb8b054
--- /dev/null
+++ b/src/pages/components-overview/chat.tsx
@@ -0,0 +1,136 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useState } from 'react';
+
+import Avatar from '@cloudscape-design/chat-components/avatar';
+import ChatBubble from '@cloudscape-design/chat-components/chat-bubble';
+import SupportPromptGroup from '@cloudscape-design/chat-components/support-prompt-group';
+import Box from '@cloudscape-design/components/box';
+import ButtonGroup from '@cloudscape-design/components/button-group';
+import FileTokenGroup from '@cloudscape-design/components/file-token-group';
+import Grid from '@cloudscape-design/components/grid';
+import PromptInput from '@cloudscape-design/components/prompt-input';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+
+import { Section } from './utils';
+
+export default function Chat() {
+ const [value, setValue] = useState('');
+ const [files, setFiles] = React.useState([
+ new File([new Blob(['Test content'])], 'file-1.pdf', {
+ type: 'application/pdf',
+ lastModified: 1590962400000,
+ }),
+ new File([new Blob(['Test content'])], 'file-2.pdf', {
+ type: 'application/pdf',
+ lastModified: 1590962400000,
+ }),
+ ]);
+ return (
+
+
+
+ }
+ ariaLabel="Jane Doe message"
+ type="outgoing"
+ >
+ This is an outgoing message from a user.
+
+ }
+ ariaLabel="AI Assistant message"
+ type="incoming"
+ >
+ This is an incoming message from the AI assistant.
+
+
+ }
+ >
+ Generating response
+
+
+
+
+ {
+ /* no-op for demo purposes */
+ }}
+ />
+
+
+ setValue(detail.value)}
+ value={value}
+ actionButtonAriaLabel="Send message"
+ actionButtonIconName="send"
+ disableSecondaryActionsPaddings
+ placeholder="Ask a question"
+ ariaLabel="Prompt input with files"
+ secondaryActions={
+
+
+
+ }
+ secondaryContent={
+ ({ file }))}
+ onDismiss={({ detail }) => setFiles(files => files.filter((_, index) => index !== detail.fileIndex))}
+ alignment="horizontal"
+ showFileSize={true}
+ showFileLastModified={true}
+ showFileThumbnail={true}
+ i18nStrings={{
+ removeFileAriaLabel: () => 'Remove file',
+ limitShowFewer: 'Show fewer files',
+ limitShowMore: 'Show more files',
+ errorIconAriaLabel: 'Error',
+ warningIconAriaLabel: 'Warning',
+ }}
+ />
+ }
+ />
+
+
+ );
+}
diff --git a/src/pages/components-overview/component-data.tsx b/src/pages/components-overview/component-data.tsx
new file mode 100644
index 00000000..21e8a662
--- /dev/null
+++ b/src/pages/components-overview/component-data.tsx
@@ -0,0 +1,289 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+import padStart from 'lodash/padStart';
+import range from 'lodash/range';
+
+import { BoxProps } from '@cloudscape-design/components/box';
+import { FlashbarProps } from '@cloudscape-design/components/flashbar';
+import { MultiselectProps } from '@cloudscape-design/components/multiselect';
+import ProgressBar from '@cloudscape-design/components/progress-bar';
+import { SelectProps } from '@cloudscape-design/components/select';
+import { StatusIndicatorProps } from '@cloudscape-design/components/status-indicator';
+
+let seed = 1;
+export default function pseudoRandom() {
+ const x = Math.sin(seed++) * 10000;
+ return x - Math.floor(x);
+}
+
+export const items = [
+ { id: '1', title: 'Item 1', description: 'Description 1' },
+ { id: '2', title: 'Item 2', description: 'Description 2' },
+ { id: '3', title: 'Item 3', description: 'Description 3' },
+];
+
+export type InstanceState = 'PENDING' | 'RUNNING' | 'STOPPING' | 'STOPPED' | 'TERMINATING' | 'TERMINATED';
+
+export interface Instance {
+ id: string;
+ name: string;
+ description: string;
+ state: InstanceState;
+ type: string;
+ imageId: string;
+ dnsName?: string;
+}
+
+export function id() {
+ const id = Math.ceil(pseudoRandom() * Math.pow(16, 8)).toString(16);
+ return padStart(id, 8, '0');
+}
+
+function state() {
+ const states = [
+ 'PENDING',
+ 'RUNNING',
+ 'RUNNING',
+ 'RUNNING',
+ 'STOPPING',
+ 'STOPPED',
+ 'STOPPED',
+ 'TERMINATED',
+ 'TERMINATING',
+ ] as const;
+ return states[Math.floor(pseudoRandom() * states.length)];
+}
+
+function number() {
+ return 1 + Math.floor(pseudoRandom() * 256);
+}
+
+function dnsName() {
+ return `ec2-${number()}-${number()}-${number()}-${number()}.eu-west-1.compute.amazonaws.com`;
+}
+
+export const flashbarItems: FlashbarProps.MessageDefinition[] = [
+ {
+ header: 'Success',
+ type: 'success',
+ content: 'This is a success message -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss success message',
+ id: 'success',
+ },
+ {
+ header: 'Warning',
+ type: 'warning',
+ content: 'This is a warning message -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss warning message',
+ id: 'warning',
+ },
+ {
+ header: 'Error',
+ type: 'error',
+ content: 'This is an error message -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss error message',
+ id: 'error',
+ },
+ {
+ header: 'Info',
+ type: 'info',
+ content: 'This is an info message -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss info message',
+ id: 'info',
+ },
+ {
+ header: 'In-progress',
+ type: 'in-progress',
+ content: (
+ <>
+ This is an in-progress flash -- check it out!
+
+ >
+ ),
+ dismissible: true,
+ dismissLabel: 'Dismiss in-progress message',
+ id: 'in-progress',
+ },
+ {
+ header: 'Loading',
+ type: 'in-progress',
+ loading: true,
+ content: 'This is a loading flash -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss loading message',
+ id: 'loading',
+ },
+];
+
+export interface RandomData {
+ description: string;
+ name: string;
+ amount: string;
+ increase: boolean;
+}
+
+const collectionData: RandomData[] = [
+ {
+ description: 'volutpat. Nulla dignissim. Maecenas ornare egestas ligula. Nullam feugiat placerat',
+ name: 'Velit Egestas LLP',
+ amount: '$68.54',
+ increase: true,
+ },
+ {
+ description: 'vestibulum lorem, sit amet ultricies sem magna nec quam. Curabitur',
+ name: 'Mattis Velit Justo Company',
+ amount: '$80.38',
+ increase: true,
+ },
+ {
+ description: 'aliquet odio. Etiam ligula tortor, dictum eu, placerat eget, venenatis',
+ name: 'Tempor LLP',
+ amount: '$1.66',
+ increase: false,
+ },
+ {
+ description: 'ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor',
+ name: 'Egestas Hendrerit Neque Corporation',
+ amount: '$31.74',
+ increase: true,
+ },
+ {
+ description: 'Vivamus molestie dapibus ligula. Aliquam erat volutpat. Nulla dignissim. Maecenas',
+ name: 'Aenean Incorporated',
+ amount: '$53.61',
+ increase: true,
+ },
+ {
+ description: 'Cras sed leo. Cras vehicula aliquet libero. Integer in magna.',
+ name: 'Proin Ltd',
+ amount: '$42.19',
+ increase: false,
+ },
+ {
+ description: 'Phasellus at augue id ante dictum cursus. Nunc mauris elit,',
+ name: 'Nulla Facilisi Foundation',
+ amount: '$97.03',
+ increase: true,
+ },
+ {
+ description: 'Sed nec metus facilisis lorem tristique aliquet. Phasellus fermentum convallis',
+ name: 'Donec Vitae Corp',
+ amount: '$15.88',
+ increase: false,
+ },
+];
+
+export const cardItems = collectionData.slice(0, 2);
+export const tableItems = collectionData;
+
+export const fontSizes: BoxProps.FontSize[] = [
+ 'body-s',
+ 'body-m',
+ 'heading-xs',
+ 'heading-s',
+ 'heading-m',
+ 'heading-l',
+ 'heading-xl',
+ 'display-l',
+];
+
+export const statusToText: [StatusIndicatorProps.Type, string][] = [
+ ['error', 'Error'],
+ ['warning', 'Warning'],
+ ['success', 'Success'],
+ ['info', 'Info'],
+ ['stopped', 'Stopped'],
+ ['pending', 'Pending'],
+ ['in-progress', 'In progress'],
+ ['loading', 'Loading'],
+];
+
+export function instanceType() {
+ const types = [
+ 't1.micro',
+ 't2.nano',
+ 't2.small',
+ 't2.xlarge',
+ 't2.2xlarge',
+ 'm3.medium',
+ 'm3.large',
+ 'm3.xlarge',
+ 'm3.2xlarge',
+ 'm4.large',
+ 'm4.xlarge',
+ 'm4.2xlarge',
+ 'm4.4xlarge',
+ 'm4.10xlarge',
+ 'm4.16xlarge',
+ 'cr1.8xlarge',
+ 'r5.large',
+ 'r5.xlarge',
+ 'r5.2xlarge',
+ 'r5.metal',
+ 'r5d.xlarge',
+ 'r5d.2xlarge',
+ 'r5d.4xlarge',
+ 'r5d.8xlarge',
+ 'r5d.12xlarge',
+ 'r5d.16xlarge',
+ 'r5d.24xlarge',
+ 'r5d.metal',
+ 'i3.large',
+ 'i3.xlarge',
+ 'i3.2xlarge',
+ 'i3.16xlarge',
+ 'c3.large',
+ 'c3.xlarge',
+ 'c4.2xlarge',
+ 'c5.large',
+ 'c5.4xlarge ',
+ 'g2.2xlarge',
+ 'p2.xlarge',
+ 'm5.large',
+ 'm5.xlarge',
+ 'm5.2xlarge',
+ 'u-6tb1.metal',
+ ];
+ return types[Math.floor(pseudoRandom() * types.length)];
+}
+
+function imageId() {
+ return `ami-${id()}`;
+}
+
+export function generateCollectionItems(count = 5): Instance[] {
+ return range(count).map(() => {
+ const value: Instance = {
+ id: id(),
+ name: `Instance ${id()}`,
+ description: '',
+ state: state(),
+ type: instanceType(),
+ imageId: imageId(),
+ };
+ if (value.state !== 'PENDING') {
+ value.dnsName = dnsName();
+ }
+ return value;
+ });
+}
+
+export const generateDropdownOptions = (count = 25): SelectProps.Options | MultiselectProps.Options => {
+ return [...Array(count).keys()].map(n => {
+ const numberToDisplay = (n + 1).toString();
+ const baseOption = {
+ id: numberToDisplay,
+ value: numberToDisplay,
+ label: `Option ${numberToDisplay}`,
+ };
+ if (n === 0 || n === 24 || n === 49) {
+ return { ...baseOption, disabled: true, disabledReason: 'disabled reason' };
+ }
+ return baseOption;
+ });
+};
diff --git a/src/pages/components-overview/form-controls.tsx b/src/pages/components-overview/form-controls.tsx
new file mode 100644
index 00000000..c93547b3
--- /dev/null
+++ b/src/pages/components-overview/form-controls.tsx
@@ -0,0 +1,184 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import Checkbox from '@cloudscape-design/components/checkbox';
+import ColumnLayout from '@cloudscape-design/components/column-layout';
+import Grid from '@cloudscape-design/components/grid';
+import RadioGroup from '@cloudscape-design/components/radio-group';
+import Slider from '@cloudscape-design/components/slider';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import Tiles from '@cloudscape-design/components/tiles';
+import Toggle from '@cloudscape-design/components/toggle';
+
+import { Section } from './utils';
+
+export default function FormControls() {
+ return (
+
+
+
+
+
+ Checked
+
+
+ Unchecked
+
+
+ Disabled
+
+
+ Disabled
+
+
+ Read-only
+
+
+ Read-only
+
+
+ Read-only, disabled
+
+
+ Read-only, disabled
+
+
+
+
+
+
+
+
+
+
+
+
+ Checked
+
+
+ Unchecked
+
+
+ Disabled
+
+
+ Disabled
+
+
+ Read-only
+
+
+ Read-only
+
+
+ Read-only, disabled
+
+
+ Read-only, disabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/components-overview/images.tsx b/src/pages/components-overview/images.tsx
new file mode 100644
index 00000000..0e111db0
--- /dev/null
+++ b/src/pages/components-overview/images.tsx
@@ -0,0 +1,193 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import Box from '@cloudscape-design/components/box';
+import Button from '@cloudscape-design/components/button';
+import ColumnLayout from '@cloudscape-design/components/column-layout';
+import Container from '@cloudscape-design/components/container';
+import Header from '@cloudscape-design/components/header';
+import Link from '@cloudscape-design/components/link';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+
+import imageExampleA from '../../common/image-example-1.png';
+import imageExampleB from '../../common/image-example-2.png';
+import imageExampleC from '../../common/image-example-3.png';
+
+export default function Images() {
+ return (
+
+
+
+ ,
+ position: 'side',
+ width: '40%',
+ }}
+ >
+
+
+
+
+ Product title
+
+
+ Company name
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan.
+
+
+ $0.1/hour
+
+ Shop now
+
+
+
+ ,
+ position: 'side',
+ width: '40%',
+ }}
+ >
+
+
+
+
+ Product title
+
+
+ Company name
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan.
+
+
+ $0.1/hour
+
+ Shop now
+
+
+
+ ,
+ position: 'side',
+ width: '40%',
+ }}
+ >
+
+
+
+
+ Product title
+
+
+ Company name
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan.
+
+
+ $0.1/hour
+
+ Shop now
+
+
+
+
+
+
+
+ ),
+ height: 200,
+ }}
+ >
+
+
+ 43 min
+
+
+ Video Title
+
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan. This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor
+ dolor ac accumsan.
+
+
+
+
+
+
+ ),
+ height: 200,
+ }}
+ >
+
+
+ 43 min
+
+
+ Video Title
+
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan. This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor
+ dolor ac accumsan.
+
+
+
+
+
+
+ ),
+ height: 200,
+ }}
+ >
+
+
+ 43 min
+
+
+ Video Title
+
+
+
+ This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor dolor ac
+ accumsan. This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut luctus tempor
+ dolor ac accumsan.
+
+
+
+
+ );
+}
diff --git a/src/pages/components-overview/index.tsx b/src/pages/components-overview/index.tsx
new file mode 100644
index 00000000..b06a7613
--- /dev/null
+++ b/src/pages/components-overview/index.tsx
@@ -0,0 +1,99 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
+applyTheme({ theme: themeCoreConfig });
+
+import Header from '@cloudscape-design/components/header';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
+
+import { Notifications } from '../commons';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
+import ButtonsInputsDropdowns from './buttons-inputs-dropdowns';
+import Chat from './chat';
+import FormControls from './form-controls';
+import Images from './images';
+import KvpForm from './kvp-form';
+import NavigationComponents from './navigation-components';
+import StatusComponents from './status-components';
+import TableAndCards from './table-and-cards';
+import Typography from './typography';
+
+import '../../styles/base.scss';
+import '../../styles/top-navigation.scss';
+
+function App() {
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
+
+ return (
+ <>
+
+ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+
+
+ Components overview page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ />
+ >
+ );
+}
+
+createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/components-overview/kvp-form.tsx b/src/pages/components-overview/kvp-form.tsx
new file mode 100644
index 00000000..aa3f7bd7
--- /dev/null
+++ b/src/pages/components-overview/kvp-form.tsx
@@ -0,0 +1,144 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import { ColumnLayout } from '@cloudscape-design/components';
+import Box from '@cloudscape-design/components/box';
+import Button from '@cloudscape-design/components/button';
+import CopyToClipboard from '@cloudscape-design/components/copy-to-clipboard';
+import Form from '@cloudscape-design/components/form';
+import FormField from '@cloudscape-design/components/form-field';
+import Grid from '@cloudscape-design/components/grid';
+import Header from '@cloudscape-design/components/header';
+import Icon from '@cloudscape-design/components/icon';
+import Input from '@cloudscape-design/components/input';
+import KeyValuePairs from '@cloudscape-design/components/key-value-pairs';
+import Link from '@cloudscape-design/components/link';
+import Select from '@cloudscape-design/components/select';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import StatusIndicator from '@cloudscape-design/components/status-indicator';
+import Textarea from '@cloudscape-design/components/textarea';
+import Tiles from '@cloudscape-design/components/tiles';
+
+import { Section } from './utils';
+
+export default function KvpForm() {
+ const [value, setValue] = React.useState('standard');
+ return (
+
+
+
+
+
+
+ Info
+
+ ),
+ },
+ {
+ label: 'Status',
+ value: Available ,
+ },
+ { label: 'Price class', value: 'Use only US, Canada, Europe, and Asia' },
+ { label: 'CNAMEs', value: example.com },
+ {
+ label: 'ARN',
+ value: (
+
+ ),
+ },
+ ]}
+ />
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/components-overview/navigation-components.tsx b/src/pages/components-overview/navigation-components.tsx
new file mode 100644
index 00000000..7e6bea8f
--- /dev/null
+++ b/src/pages/components-overview/navigation-components.tsx
@@ -0,0 +1,111 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useState } from 'react';
+
+import AnchorNavigation from '@cloudscape-design/components/anchor-navigation';
+import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
+import Grid from '@cloudscape-design/components/grid';
+import Link from '@cloudscape-design/components/link';
+import SideNavigation from '@cloudscape-design/components/side-navigation';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import Tabs from '@cloudscape-design/components/tabs';
+
+import { Section } from './utils';
+
+export default function NavigationComponents() {
+ const [activeTabId, setActiveTabId] = useState('tab1');
+ const [activeHref, setActiveHref] = useState('#/parent-page/child-page1');
+
+ return (
+
+
+
+
+ {
+ if (!event.detail.external) {
+ event.preventDefault();
+ setActiveHref(event.detail.href);
+ }
+ }}
+ items={[
+ { type: 'link', text: 'Page 1', href: '#/page1' },
+ {
+ type: 'expandable-link-group',
+ text: 'Parent page',
+ href: '#/parent-page',
+ items: [
+ {
+ type: 'link',
+ text: 'Child page 1',
+ href: '#/parent-page/child-page1',
+ },
+ {
+ type: 'link',
+ text: 'Child page 2',
+ href: '#/parent-page/child-page2',
+ },
+ ],
+ },
+ { type: 'link', text: 'Page 2', href: '#/page2' },
+ { type: 'link', text: 'Page 3', href: '#/page3' },
+ ]}
+ />
+
+ setActiveTabId(detail.activeTabId)}
+ tabs={[
+ { id: 'tab1', label: 'Tab 1', content: 'Tab 1 Content' },
+ { id: 'tab2', label: 'Tab 2', content: 'Tab 2 Content' },
+ { id: 'tab3', label: 'Tab 3', content: 'Tab 3 Content', disabled: true },
+ ]}
+ />
+
+
+
+
+
+ Primary anchor link
+
+ Secondary anchor link
+
+
+
+ );
+}
diff --git a/src/pages/components-overview/status-components.tsx b/src/pages/components-overview/status-components.tsx
new file mode 100644
index 00000000..2b33c63d
--- /dev/null
+++ b/src/pages/components-overview/status-components.tsx
@@ -0,0 +1,365 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+
+import Alert from '@cloudscape-design/components/alert';
+import Badge from '@cloudscape-design/components/badge';
+import Button from '@cloudscape-design/components/button';
+import ButtonGroup from '@cloudscape-design/components/button-group';
+import ColumnLayout from '@cloudscape-design/components/column-layout';
+import FileTokenGroup from '@cloudscape-design/components/file-token-group';
+import Flashbar, { FlashbarProps } from '@cloudscape-design/components/flashbar';
+import FormField from '@cloudscape-design/components/form-field';
+import Grid from '@cloudscape-design/components/grid';
+import Input from '@cloudscape-design/components/input';
+import Link from '@cloudscape-design/components/link';
+import ProgressBar from '@cloudscape-design/components/progress-bar';
+import Select from '@cloudscape-design/components/select';
+import Slider from '@cloudscape-design/components/slider';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import Spinner from '@cloudscape-design/components/spinner';
+import StatusIndicator from '@cloudscape-design/components/status-indicator';
+import Steps from '@cloudscape-design/components/steps';
+
+import { flashbarItems } from './component-data';
+import { Section, SubSection } from './utils';
+
+export default function StatusComponents() {
+ return (
+
+ <>
+
+
+ item.type === 'error') as FlashbarProps.MessageDefinition]} />
+
+ This is an error alert -- check it out!
+
+
+
+
+
+
+
+
+
+
+
+ Error Status
+ Badge
+
+
+
+
+
+
+
+ item.type === 'warning') as FlashbarProps.MessageDefinition]}
+ />
+
+ This is a warning alert -- check it out!
+
+
+
+
+
+
+
+
+
+ Warning Status
+
+
+
+
+
+
+ item.type === 'success') as FlashbarProps.MessageDefinition]}
+ />
+
+ This is a success alert -- check it out!
+
+
+
+
+
+ Success Status
+ Badge
+
+
+
+
+
+
+
+ item.type === 'info') as FlashbarProps.MessageDefinition]} />
+
+ This is an info alert -- check it out!
+
+
+
+
+
+ Info Status
+
+ Badge
+ Info
+
+
+
+
+
+
+
+
+
+ This is an in-progress flash -- check it out!
+
+ >
+ ),
+ dismissible: true,
+ dismissLabel: 'Dismiss in-progress message',
+ id: 'in-progress',
+ },
+ {
+ header: 'Loading',
+ type: 'in-progress',
+ loading: true,
+ content: 'This is a loading flash -- check it out!',
+ dismissible: true,
+ dismissLabel: 'Dismiss loading message',
+ id: 'loading',
+ },
+ ]}
+ />
+
+ {
+ /* no-op for demo*/
+ }}
+ />
+
+
+
+
+ Loading
+
+ Loading
+
+
+ Loading
+
+
+
+
+
+
+
+
+
+
+
+
+
+ In-progress Status
+ Loading
+ Stopped Status
+ Pending Status
+ Badge
+
+
+
+
+ >
+
+ );
+}
diff --git a/src/pages/components-overview/table-and-cards.tsx b/src/pages/components-overview/table-and-cards.tsx
new file mode 100644
index 00000000..8429b554
--- /dev/null
+++ b/src/pages/components-overview/table-and-cards.tsx
@@ -0,0 +1,91 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useState } from 'react';
+
+import Badge from '@cloudscape-design/components/badge';
+import Cards from '@cloudscape-design/components/cards';
+import Header from '@cloudscape-design/components/header';
+import Link from '@cloudscape-design/components/link';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+import Table from '@cloudscape-design/components/table';
+
+import { cardItems, RandomData, tableItems } from './component-data';
+import { Section } from './utils';
+
+export default function TableAndCards() {
+ const [selectedItems, setSelectedItems] = useState({
+ table: [tableItems[2]],
+ cards: [cardItems[1]],
+ });
+
+ return (
+ // Cards already have built-in margin so the space between is smaller than oter sections
+
+
+ (
+
+ {item.name}
+
+ ),
+ sections: [
+ {
+ id: 'description',
+ header: 'Description',
+ content: item => item.description,
+ width: 85,
+ },
+ {
+ id: 'cost',
+ header: 'Cost',
+ content: item => item.amount,
+ width: 15,
+ },
+ ],
+ }}
+ selectionType="multi"
+ selectedItems={selectedItems.cards}
+ onSelectionChange={({ detail }) =>
+ setSelectedItems(prevState => ({ ...prevState, cards: detail.selectedItems }))
+ }
+ ariaLabels={{
+ itemSelectionLabel: (e, item) => `Select ${item.name}`,
+ selectionGroupLabel: 'Item selection',
+ }}
+ />
+
+
+ Table with common features}
+ columnDefinitions={[
+ { header: 'Name', cell: (item: RandomData) => {item.name} },
+ {
+ header: 'Category',
+ cell: (item: RandomData) => {
+ const categories = ['green', 'grey', 'blue'] as const;
+ const labels = ['Serverless', 'Security', 'Agentic AI'];
+ const index = item.name.length % 3;
+ return {labels[index]} ;
+ },
+ },
+ { header: 'Description', cell: (item: RandomData) => item.description },
+ ]}
+ selectionType="multi"
+ selectedItems={selectedItems.table}
+ onSelectionChange={({ detail }) =>
+ setSelectedItems(prevState => ({ ...prevState, table: detail.selectedItems }))
+ }
+ ariaLabels={{
+ itemSelectionLabel: (e, item) => `Select ${item.name}`,
+ allItemsSelectionLabel: () => 'Select all items',
+ selectionGroupLabel: 'Item selection',
+ }}
+ />
+
+
+ );
+}
diff --git a/src/pages/components-overview/typography.tsx b/src/pages/components-overview/typography.tsx
new file mode 100644
index 00000000..80d5ba6e
--- /dev/null
+++ b/src/pages/components-overview/typography.tsx
@@ -0,0 +1,237 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React, { useState } from 'react';
+
+import {
+ Box,
+ ColumnLayout,
+ Container,
+ Header,
+ Icon,
+ Link,
+ SpaceBetween,
+ TextContent,
+ Toggle,
+} from '@cloudscape-design/components';
+
+export default function Typography() {
+ const [longText, setLongText] = useState(false);
+ return (
+ <>
+
+ setLongText(detail.checked)} checked={longText}>
+ Long text
+
+ }
+ >
+ Typography & Iconography
+
+
+
+
+
+ {longText ? (
+ <>
+
+ Heading 1: The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities.
+
+
+ Heading 2: The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities.
+
+
+ Heading 3: The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities.
+
+
+ Heading 4: The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities.
+
+
+ Heading 5: The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities.
+
+ >
+ ) : (
+ <>
+ Heading 1
+ Heading 2
+ Heading 3
+ Heading 4
+ Heading 5
+ >
+ )}
+
+
+
+ {longText ? (
+ <>
+ Paragraph - The instance type you specify determines the hardware of the host computer used for your
+ instance. Each instance type offers different compute, memory, and storage capabilities, and is grouped in
+ an instance family based on these capabilities. Select an instance type based on the requirements of the
+ application or software that you plan to run on your instance.{' '}
+
+ Amazon EC2
+ {' '}
+ provides each instance with a consistent and predictable amount of CPU capacity, regardless of its{' '}
+
+ underlying hardwares
+
+ . The order of buttons is important when action is required on a data set. It follows the order of major
+ actions that can be performed on items.
+ >
+ ) : (
+ <>
+ Paragraph -{' '}
+
+ Amazon EC2
+ {' '}
+ provides each instance with a consistent and predictable amount of CPU capacity, regardless of its{' '}
+
+ underlying hardwares
+
+ . The order of buttons is important when action is required on a data set. It follows the order of major
+ actions that can be performed on items.
+ >
+ )}
+
+
+ {longText ? (
+ <>
+ Small - Daily instance hours by instance type, aggregated across all regions and availability zones in
+ your account. By default the form field will take up 66% of its container width. Enabling the stretch
+ property will set the width of the form field to 100%. This can be done for fields where a full-width
+ layout is more appropriate, such as when using multi column layout.
+ >
+ ) : (
+ <>Small - Requirements and constraints for the field.>
+ )}
+
+
+ Icon - Small size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Icon - Normal size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Icon - Medium size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Icon - Big size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Icon - Large size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/components-overview/utils.tsx b/src/pages/components-overview/utils.tsx
new file mode 100644
index 00000000..0cedc2ff
--- /dev/null
+++ b/src/pages/components-overview/utils.tsx
@@ -0,0 +1,35 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: MIT-0
+import React from 'react';
+import { ReactElement } from 'react';
+
+import Box from '@cloudscape-design/components/box';
+import Container from '@cloudscape-design/components/container';
+import Header from '@cloudscape-design/components/header';
+import SpaceBetween from '@cloudscape-design/components/space-between';
+
+interface SectionProps {
+ header?: string;
+ level?: 'h2' | 'h3';
+ container?: boolean;
+ children: ReactElement;
+}
+
+export const Section = ({ header, level = 'h2', container = true, children }: SectionProps) => {
+ const content = {children} ;
+ return (
+
+ {header && }
+ {container ? {content} : content}
+
+ );
+};
+
+export const SubSection = ({ header, level = 'h3', children }: SectionProps) => {
+ return (
+
+ {header && {header} }
+ {children}
+
+ );
+};
diff --git a/src/pages/configurable-dashboard/app.tsx b/src/pages/configurable-dashboard/app.tsx
index 47f453a2..0d7e9a44 100644
--- a/src/pages/configurable-dashboard/app.tsx
+++ b/src/pages/configurable-dashboard/app.tsx
@@ -6,7 +6,7 @@ import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import SplitPanel from '@cloudscape-design/components/split-panel';
import { Breadcrumbs, HelpPanelProvider, Notifications } from '../commons';
-import { CustomAppLayout } from '../commons/common-components';
+import { CustomAppLayout, DemoTopNavigation } from '../commons/common-components';
import { useLocalStorage } from '../commons/use-local-storage';
import { DashboardMainInfo } from '../dashboard/components/header';
import { DashboardSideNavigation } from '../dashboard/components/side-navigation';
@@ -15,6 +15,8 @@ import { Content } from './content';
import { StoredWidgetPlacement } from './interfaces';
import { allWidgets, getPaletteWidgets } from './widgets';
+import '../../styles/top-navigation.scss';
+
const supportedWidgets = new Set(Object.keys(allWidgets));
export function App() {
@@ -38,34 +40,37 @@ export function App() {
return (
- }
- navigation={ }
- toolsOpen={toolsOpen}
- tools={toolsContent}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- notifications={ }
- content={
-
- }
- splitPanel={
-
-
-
- }
- splitPanelPreferences={{ position: 'side' }}
- splitPanelOpen={splitPanelOpen}
- onSplitPanelToggle={({ detail }) => setSplitPanelOpen(detail.open)}
- splitPanelSize={splitPanelSize}
- onSplitPanelResize={event => setSplitPanelSize(event.detail.size)}
- />
+ <>
+
+ }
+ navigation={ }
+ toolsOpen={toolsOpen}
+ tools={toolsContent}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ notifications={ }
+ content={
+
+ }
+ splitPanel={
+
+
+
+ }
+ splitPanelPreferences={{ position: 'side' }}
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={({ detail }) => setSplitPanelOpen(detail.open)}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={event => setSplitPanelSize(event.detail.size)}
+ />
+ >
);
}
diff --git a/src/pages/configurable-dashboard/index.tsx b/src/pages/configurable-dashboard/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/configurable-dashboard/index.tsx
+++ b/src/pages/configurable-dashboard/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/dashboard/app.tsx b/src/pages/dashboard/app.tsx
index 26d546cc..83fe52de 100644
--- a/src/pages/dashboard/app.tsx
+++ b/src/pages/dashboard/app.tsx
@@ -5,18 +5,27 @@ import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Button from '@cloudscape-design/components/button';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import { Breadcrumbs, HelpPanelProvider, Notifications } from '../commons';
-import { CustomAppLayout } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { Content } from './components/content';
import { DashboardHeader, DashboardMainInfo } from './components/header';
import { DashboardSideNavigation } from './components/side-navigation';
import '@cloudscape-design/global-styles/dark-mode-utils.css';
+import '../../styles/top-navigation.scss';
export function App() {
const [toolsOpen, setToolsOpen] = useState(false);
const [toolsContent, setToolsContent] = useState(() => );
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
const handleToolsContentChange = (content: React.ReactNode) => {
@@ -27,21 +36,34 @@ export function App() {
return (
-
- Launch instance} />
-
-
- }
- breadcrumbs={ }
- navigation={ }
- tools={toolsContent}
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- notifications={ }
- />
+ <>
+
+
+ Launch instance} />
+
+
+ }
+ breadcrumbs={ }
+ navigation={ }
+ tools={toolsContent}
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/dashboard/components/side-navigation.tsx b/src/pages/dashboard/components/side-navigation.tsx
index 35a55da5..c28a0cb6 100644
--- a/src/pages/dashboard/components/side-navigation.tsx
+++ b/src/pages/dashboard/components/side-navigation.tsx
@@ -40,7 +40,7 @@ const navItems: SideNavigationProps['items'] = [
>
-
+
New
@@ -125,7 +130,12 @@ const navItems: SideNavigationProps['items'] = [
renderWithPortal={true}
dismissAriaLabel="Close"
>
-
+
Beta
diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx
index 03ef22a5..e4b3aebd 100644
--- a/src/pages/dashboard/index.tsx
+++ b/src/pages/dashboard/index.tsx
@@ -3,8 +3,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
+
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/dashboard/widgets/features-spotlight/index.tsx b/src/pages/dashboard/widgets/features-spotlight/index.tsx
index 8578c1af..6c04cd1e 100644
--- a/src/pages/dashboard/widgets/features-spotlight/index.tsx
+++ b/src/pages/dashboard/widgets/features-spotlight/index.tsx
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT-0
import React from 'react';
+import { Alert, SpaceBetween } from '@cloudscape-design/components';
import Box from '@cloudscape-design/components/box';
import ColumnLayout from '@cloudscape-design/components/column-layout';
import Header from '@cloudscape-design/components/header';
@@ -30,33 +31,39 @@ function FeaturesSpotlightFooter() {
export function FeaturesSpotlightContent() {
return (
-
-
- August 26, 2019
-
-
- Amazon EC2 Fleet Functionality
-
-
-
- Amazon EC2 Auto Scaling now lets you provision and automatically scale instances across purchase options,
- Availability Zones (AZ), and instance families in a single Auto Scaling group (ASG), to optimize scale,
- performance, and cost.
-
-
-
- September 9, 2019
-
-
- Amazon EC2 Hibernation Now Available on Amazon Linux 2
-
-
-
- Amazon EC2 expands Hibernation support for Amazon Linux 2. You can now hibernate newly launched EC2 Instances
- running Amazon Linux 2, in addition to Amazon Linux and Ubuntu 18.04 LTS OS.
-
-
-
+
+
+
+ August 26, 2019
+
+
+ Amazon EC2 Fleet Functionality
+
+
+
+ Amazon EC2 Auto Scaling now lets you provision and automatically scale instances across purchase options,
+ Availability Zones (AZ), and instance families in a single Auto Scaling group (ASG), to optimize scale,
+ performance, and cost.
+
+
+
+ September 9, 2019
+
+
+ Amazon EC2 Hibernation Now Available on Amazon Linux 2
+
+
+
+ Amazon EC2 expands Hibernation support for Amazon Linux 2. You can now hibernate newly launched EC2
+ Instances running Amazon Linux 2, in addition to Amazon Linux and Ubuntu 18.04 LTS OS.
+
+
+
+
+ Provisioning less than 100 GiB of General Purpose (SSD) storage for high throughput workloads could result in
+ higher latencies upon exhaustion of the initial General Purpose (SSD) IO credit balance.
+
+
);
}
diff --git a/src/pages/delete-one-click/app.tsx b/src/pages/delete-one-click/app.tsx
index 957ab16d..0b2c7649 100644
--- a/src/pages/delete-one-click/app.tsx
+++ b/src/pages/delete-one-click/app.tsx
@@ -8,19 +8,29 @@ import Container from '@cloudscape-design/components/container';
import Form from '@cloudscape-design/components/form';
import Header from '@cloudscape-design/components/header';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import TagEditor, { TagEditorProps } from '@cloudscape-design/components/tag-editor';
import { resourceManageTagsBreadcrumbs } from '../../common/breadcrumbs';
import { tagEditorI18nStrings } from '../../i18n-strings/tag-editor';
-import { Navigation, Notifications } from '../commons/common-components';
+import {
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { CustomAppLayout } from '../commons/common-components';
import { Info } from './components/info';
import { loadTagKeys, loadTags, loadTagValues } from './utils';
+import '../../styles/top-navigation.scss';
+
export function App() {
const [tags, setTags] = useState([]);
const [loading, setLoading] = useState(true);
const [isValid, setIsValid] = useState(true);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
useEffect(() => {
if (loading) {
@@ -48,54 +58,66 @@ export function App() {
};
return (
-
-
-
-
-
-
- Cancel
-
-
- Save changes
-
-
- }
- >
-
- Tags
-
+ <>
+
+
+
+
+ }
+ content={
+
+
+
+
+
+
+ Cancel
+
+
+ Save changes
+
+
}
>
- onChange(e.detail)}
- keysRequest={loadTagKeys}
- valuesRequest={loadTagValues}
- loading={loading}
- i18nStrings={tagEditorI18nStrings}
- />
-
-
-
-
- }
- breadcrumbs={
-
- }
- navigation={ }
- toolsHide={true}
- notifications={ }
- />
+
+ Tags
+
+ }
+ >
+ onChange(e.detail)}
+ keysRequest={loadTagKeys}
+ valuesRequest={loadTagValues}
+ loading={loading}
+ i18nStrings={tagEditorI18nStrings}
+ />
+
+
+
+
+ }
+ breadcrumbs={
+
+ }
+ navigation={ }
+ toolsHide={true}
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/delete-one-click/index.tsx b/src/pages/delete-one-click/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/delete-one-click/index.tsx
+++ b/src/pages/delete-one-click/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/delete-with-additional-confirmation/index.tsx b/src/pages/delete-with-additional-confirmation/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/delete-with-additional-confirmation/index.tsx
+++ b/src/pages/delete-with-additional-confirmation/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/delete-with-simple-confirmation/app.tsx b/src/pages/delete-with-simple-confirmation/app.tsx
index 43e71732..c47fdac7 100644
--- a/src/pages/delete-with-simple-confirmation/app.tsx
+++ b/src/pages/delete-with-simple-confirmation/app.tsx
@@ -2,7 +2,10 @@
// SPDX-License-Identifier: MIT-0
import React, { useEffect, useRef, useState } from 'react';
+import SplitPanel from '@cloudscape-design/components/split-panel';
+
import { DistributionResource } from '../../resources/types';
+import { GlobalSplitPanelContent, useGlobalSplitPanel } from '../commons/common-components';
import DataProvider from '../commons/data-provider';
import fakeDelay from '../commons/fake-delay';
import { DeleteModal } from './components/delete-modal';
@@ -70,6 +73,22 @@ export function App() {
setSelectedItems([]);
}, [locationHash]);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
+
+ const splitPanelProps = {
+ splitPanelOpen,
+ onSplitPanelToggle,
+ splitPanelSize,
+ onSplitPanelResize,
+ splitPanelPreferences,
+ splitPanel: (
+
+
+
+ ),
+ };
+
return (
<>
{locationDistribution ? (
@@ -77,6 +96,7 @@ export function App() {
distribution={locationDistribution}
onDeleteInit={onDeleteInit}
notifications={notifications}
+ {...splitPanelProps}
/>
) : (
)}
void;
notifications: FlashbarProps.MessageDefinition[];
+ splitPanelOpen: boolean;
+ onSplitPanelToggle: AppLayoutProps['onSplitPanelToggle'];
+ splitPanelSize: number;
+ onSplitPanelResize: AppLayoutProps['onSplitPanelResize'];
+ splitPanelPreferences: AppLayoutProps['splitPanelPreferences'];
+ splitPanel: ReactNode;
}
-export function DistributionDetailsPage({ distribution, onDeleteInit, notifications }: DistributionDetailsPageProps) {
+export function DistributionDetailsPage({
+ distribution,
+ onDeleteInit,
+ notifications,
+ splitPanelOpen,
+ onSplitPanelToggle,
+ splitPanelSize,
+ onSplitPanelResize,
+ splitPanelPreferences,
+ splitPanel,
+}: DistributionDetailsPageProps) {
return (
}
navigationOpen={false}
toolsHide={true}
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={splitPanel}
/>
);
}
diff --git a/src/pages/delete-with-simple-confirmation/components/distribution-page.tsx b/src/pages/delete-with-simple-confirmation/components/distribution-page.tsx
index 4ceef32a..f2f269ec 100644
--- a/src/pages/delete-with-simple-confirmation/components/distribution-page.tsx
+++ b/src/pages/delete-with-simple-confirmation/components/distribution-page.tsx
@@ -1,7 +1,8 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import React from 'react';
+import React, { ReactNode } from 'react';
+import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
import Flashbar, { FlashbarProps } from '@cloudscape-design/components/flashbar';
@@ -16,6 +17,12 @@ interface DistributionsPageProps {
setSelectedItems: (items: DistributionResource[]) => void;
onDeleteInit: () => void;
notifications: FlashbarProps.MessageDefinition[];
+ splitPanelOpen: boolean;
+ onSplitPanelToggle: AppLayoutProps['onSplitPanelToggle'];
+ splitPanelSize: number;
+ onSplitPanelResize: AppLayoutProps['onSplitPanelResize'];
+ splitPanelPreferences: AppLayoutProps['splitPanelPreferences'];
+ splitPanel: ReactNode;
}
export function DistributionsPage({
distributions,
@@ -23,6 +30,12 @@ export function DistributionsPage({
setSelectedItems,
onDeleteInit,
notifications,
+ splitPanelOpen,
+ onSplitPanelToggle,
+ splitPanelSize,
+ onSplitPanelResize,
+ splitPanelPreferences,
+ splitPanel,
}: DistributionsPageProps) {
return (
);
}
diff --git a/src/pages/delete-with-simple-confirmation/index.tsx b/src/pages/delete-with-simple-confirmation/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/delete-with-simple-confirmation/index.tsx
+++ b/src/pages/delete-with-simple-confirmation/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/details-hub/app.tsx b/src/pages/details-hub/app.tsx
index 5ca8899a..57fb3fdf 100644
--- a/src/pages/details-hub/app.tsx
+++ b/src/pages/details-hub/app.tsx
@@ -3,8 +3,16 @@
import React from 'react';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { Breadcrumbs } from '../details/components/breadcrumbs';
import { GeneralConfig } from '../details/components/general-config';
import { OriginsTable } from '../details/components/origins-table';
@@ -12,30 +20,45 @@ import { PageHeader } from '../details/components/page-header';
import { INSTANCE_DROPDOWN_ITEMS } from '../details/details-config';
import { LogsTable } from './components/logs-table';
+import '../../styles/top-navigation.scss';
+
export function App() {
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
return (
-
-
-
-
-
-
+ <>
+
+
+
+
+ }
+ content={
+
+
+
+
+
+
+
-
- }
- breadcrumbs={ }
- navigation={ }
- toolsHide={true}
- contentType="default"
- notifications={ }
- />
+ }
+ breadcrumbs={ }
+ navigation={ }
+ toolsHide={true}
+ contentType="default"
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/details-hub/index.tsx b/src/pages/details-hub/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/details-hub/index.tsx
+++ b/src/pages/details-hub/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/details-tabs/app.tsx b/src/pages/details-tabs/app.tsx
index d6de78fb..6e942c60 100644
--- a/src/pages/details-tabs/app.tsx
+++ b/src/pages/details-tabs/app.tsx
@@ -4,9 +4,17 @@ import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Tabs from '@cloudscape-design/components/tabs';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { BehaviorsTable } from '../details/components/behaviors-table';
import { Breadcrumbs } from '../details/components/breadcrumbs';
import { EmptyTable } from '../details/components/empty-table';
@@ -19,9 +27,13 @@ import ToolsContent from '../details/tools-content';
import { Details } from './components/details';
import { LogsTable } from './components/logs-table';
+import '../../styles/top-navigation.scss';
+
export function App() {
const [toolsIndex, setToolsIndex] = useState(0);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
const loadHelpPanelContent = (index: number): void => {
@@ -31,75 +43,88 @@ export function App() {
};
return (
-
-
-
-
- ,
- },
- {
- label: 'Logs',
- id: 'logs',
- content: ,
- },
- {
- label: 'Origins',
- id: 'origins',
- content: ,
- },
+ <>
+
+
+
+
+ }
+ content={
+
+ ,
+ text: 'Actions',
+ items: INSTANCE_DROPDOWN_ITEMS,
+ itemType: 'group',
},
{
- label: 'Invalidations',
- id: 'invalidations',
- content: ,
+ text: 'Edit',
+ itemType: 'action',
+ id: 'edit',
},
{
- label: 'Tags',
- id: 'tags',
- content: ,
+ text: 'Delete',
+ itemType: 'action',
+ id: 'delete',
},
]}
- ariaLabel="Resource details"
/>
+
+
+ ,
+ },
+ {
+ label: 'Logs',
+ id: 'logs',
+ content: ,
+ },
+ {
+ label: 'Origins',
+ id: 'origins',
+ content: ,
+ },
+ {
+ label: 'Behaviors',
+ id: 'behaviors',
+ content: ,
+ },
+ {
+ label: 'Invalidations',
+ id: 'invalidations',
+ content: ,
+ },
+ {
+ label: 'Tags',
+ id: 'tags',
+ content: ,
+ },
+ ]}
+ ariaLabel="Resource details"
+ />
+
-
- }
- breadcrumbs={ }
- navigation={ }
- tools={ToolsContent[toolsIndex]}
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }: { detail: { open: boolean } }) => setToolsOpen(detail.open)}
- notifications={ }
- />
+ }
+ breadcrumbs={ }
+ navigation={ }
+ tools={ToolsContent[toolsIndex]}
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }: { detail: { open: boolean } }) => setToolsOpen(detail.open)}
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/details-tabs/index.tsx b/src/pages/details-tabs/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/details-tabs/index.tsx
+++ b/src/pages/details-tabs/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/details/app.tsx b/src/pages/details/app.tsx
index 1e6335bc..3368b30a 100644
--- a/src/pages/details/app.tsx
+++ b/src/pages/details/app.tsx
@@ -4,8 +4,16 @@ import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { BehaviorsTable } from './components/behaviors-table';
import { Breadcrumbs } from './components/breadcrumbs';
import { DistSettings } from './components/dist-settings';
@@ -15,9 +23,13 @@ import { PageHeader } from './components/page-header';
import { TagsTable } from './components/tags-table';
import ToolsContent from './tools-content';
+import '../../styles/top-navigation.scss';
+
export function App() {
const [toolsIndex, setToolsIndex] = useState(0);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
const loadHelpPanelContent = (index: number): void => {
@@ -27,32 +39,45 @@ export function App() {
};
return (
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+ }
+ content={
+
+
+
+
+
+
+
+
+
-
- }
- breadcrumbs={ }
- navigation={ }
- tools={ToolsContent[toolsIndex]}
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }: { detail: { open: boolean } }) => setToolsOpen(detail.open)}
- contentType="default"
- notifications={ }
- />
+ }
+ breadcrumbs={ }
+ navigation={ }
+ tools={ToolsContent[toolsIndex]}
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }: { detail: { open: boolean } }) => setToolsOpen(detail.open)}
+ contentType="default"
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/details/index.tsx b/src/pages/details/index.tsx
index 03ef22a5..6c06981f 100644
--- a/src/pages/details/index.tsx
+++ b/src/pages/details/index.tsx
@@ -3,8 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
import '../../styles/base.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/edit/app.tsx b/src/pages/edit/app.tsx
index 3c368dca..fd0b4214 100644
--- a/src/pages/edit/app.tsx
+++ b/src/pages/edit/app.tsx
@@ -8,60 +8,84 @@ import Form from '@cloudscape-design/components/form';
import Header from '@cloudscape-design/components/header';
import Link from '@cloudscape-design/components/link';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
-import { CustomAppLayout, Navigation } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { Notifications } from '../commons/common-components';
import { Breadcrumbs } from './components/breadcrumbs';
import { Content } from './components/content';
import { TOOLS_CONTENT } from './edit-config';
+import '../../styles/top-navigation.scss';
+
export const App = () => {
const appLayoutRef = useRef(null);
const [toolsIndex, setToolsIndex] = React.useState(0);
const [toolsOpen, setToolsOpen] = React.useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const loadHelpPanelContent = (toolsIndex: number) => {
setToolsIndex(toolsIndex);
setToolsOpen(true);
appLayoutRef.current?.focusToolsClose();
};
return (
- event.preventDefault()}>
- loadHelpPanelContent(0)}>
- Info
-
- }
- >
- Edit SLCCSMWOHOFUY0
-
- }
- actions={
-
- Cancel
- Save changes
-
- }
- >
- loadHelpPanelContent(index)} />
-
-
- }
- breadcrumbs={ }
- navigation={ }
- tools={TOOLS_CONTENT[toolsIndex]}
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => {
- setToolsOpen(detail.open);
- }}
- notifications={ }
- />
+ <>
+
+
+
+
+ }
+ content={
+ event.preventDefault()}>
+ loadHelpPanelContent(0)}>
+ Info
+
+ }
+ >
+ Edit SLCCSMWOHOFUY0
+
+ }
+ actions={
+
+ Cancel
+ Save changes
+
+ }
+ >
+ loadHelpPanelContent(index)} />
+
+
+ }
+ breadcrumbs={ }
+ navigation={ }
+ tools={TOOLS_CONTENT[toolsIndex]}
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => {
+ setToolsOpen(detail.open);
+ }}
+ notifications={ }
+ />
+ >
);
};
diff --git a/src/pages/edit/index.tsx b/src/pages/edit/index.tsx
index ca9d4475..fc17c69b 100644
--- a/src/pages/edit/index.tsx
+++ b/src/pages/edit/index.tsx
@@ -3,8 +3,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
+import '../../styles/base.scss';
import '../../styles/form.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/form-unsaved-changes/app.tsx b/src/pages/form-unsaved-changes/app.tsx
index 8d71bf8e..df663bf1 100644
--- a/src/pages/form-unsaved-changes/app.tsx
+++ b/src/pages/form-unsaved-changes/app.tsx
@@ -9,15 +9,24 @@ import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
import Button from '@cloudscape-design/components/button';
import Modal from '@cloudscape-design/components/modal';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import { resourceCreateBreadcrumbs } from '../../common/breadcrumbs';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { FormHeader, LimitedForm } from '../form/components/form';
import ToolsContent from '../form/components/tools-content';
export function App() {
const [toolsIndex, setToolsIndex] = useState(0);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const [navigationOpen, setNavigationOpen] = useState(true);
const [dirty, setDirty] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
@@ -59,6 +68,16 @@ export function App() {
+
+
+ }
content={
<>
);
diff --git a/src/pages/form-validation/index.tsx b/src/pages/form-validation/index.tsx
index ca9d4475..fc17c69b 100644
--- a/src/pages/form-validation/index.tsx
+++ b/src/pages/form-validation/index.tsx
@@ -3,8 +3,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
+import '../../styles/base.scss';
import '../../styles/form.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/form/app.tsx b/src/pages/form/app.tsx
index be46889c..3c72fbd2 100644
--- a/src/pages/form/app.tsx
+++ b/src/pages/form/app.tsx
@@ -1,22 +1,25 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
-import { ButtonProps } from '@cloudscape-design/components/button';
-import { SelectProps } from '@cloudscape-design/components/select';
import SplitPanel from '@cloudscape-design/components/split-panel';
import { resourceCreateBreadcrumbs } from '../../common/breadcrumbs';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
-import useCreateCachePolicy from '../form/components/cache-behavior-panel/use-create-cache-policy';
-import UnsavedChangesModal from '../form/components/unsaved-changes-modal';
-import { EXISTING_CACHE_POLICIES } from '../form/form-config';
-import CreateCachePolicy from './components/cache-behavior-panel/create-cache-policy';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { FormFull, FormHeader } from './components/form';
import ToolsContent from './components/tools-content';
+import '../../styles/top-navigation.scss';
+
const Breadcrumbs = () => (
);
@@ -24,26 +27,20 @@ const Breadcrumbs = () => (
export function App() {
const [toolsIndex, setToolsIndex] = useState(0);
const [toolsOpen, setToolsOpen] = useState(false);
- const [splitPanelOpen, setSplitPanelOpen] = useState(false);
- const [
- createCachePolicyData,
- setCreateCachePolicyData,
- createCachePolicyDataErrors,
- setCreateCachePolicyErrors,
- isCreateCachePolicyDataChanged,
- resetCreateCachePolicyData,
- ] = useCreateCachePolicy();
- const [selectedCachePolicy, setSelectedCachePolicy] = useState(null);
- const [showUnsavedChangesModal, setShowUnsavedChangesModal] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
- const createCachePolicyButtonRef = useRef(null);
- useEffect(() => {
- // Focus is moved here instead of toggleSplitPanel to allow split panel to open before focusing
- if (splitPanelOpen) {
- appLayout.current?.focusSplitPanel();
- }
- }, [splitPanelOpen]);
+ // Minimal cache policy props for form compatibility
+ const [selectedCachePolicy, setSelectedCachePolicy] = useState(null);
+ const cachePolicyButtonRef = useRef(null);
+ const cachePolicyProps = {
+ buttonRef: cachePolicyButtonRef,
+ policies: [],
+ selectedPolicy: selectedCachePolicy,
+ setSelectedPolicy: setSelectedCachePolicy,
+ toggleSplitPanel: () => undefined, // No-op since we're using global split panel
+ };
const loadHelpPanelContent = (index: number) => {
setToolsIndex(index);
@@ -51,70 +48,36 @@ export function App() {
appLayout.current?.focusToolsClose();
};
- const toggleSplitPanel = (isOpen: boolean) => {
- if (!isOpen) {
- if (isCreateCachePolicyDataChanged && !showUnsavedChangesModal) {
- return setShowUnsavedChangesModal(true);
- }
-
- resetCreateCachePolicyData();
- createCachePolicyButtonRef.current?.focus();
- }
-
- return setSplitPanelOpen(isOpen);
- };
-
return (
-
+ <>
+
+ }
- cachePolicyProps={{
- buttonRef: createCachePolicyButtonRef,
- policies: EXISTING_CACHE_POLICIES,
- selectedPolicy: selectedCachePolicy,
- setSelectedPolicy: setSelectedCachePolicy,
- toggleSplitPanel,
- }}
- />
-
- setShowUnsavedChangesModal(false)}
- onLeave={() => {
- resetCreateCachePolicyData();
- setShowUnsavedChangesModal(false);
- toggleSplitPanel(false);
- }}
- />
- >
- }
- breadcrumbs={ }
- navigation={ }
- tools={ToolsContent[toolsIndex]}
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- notifications={ }
- splitPanelOpen={splitPanelOpen}
- splitPanel={
-
- ({})}
- onCancel={() => toggleSplitPanel(false)}
+ cachePolicyProps={cachePolicyProps}
/>
-
- }
- onSplitPanelToggle={({ detail }) => toggleSplitPanel(detail.open)}
- splitPanelPreferences={{ position: 'side' }}
- />
+ }
+ breadcrumbs={ }
+ navigation={ }
+ tools={ToolsContent[toolsIndex]}
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ notifications={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ />
+ >
);
}
diff --git a/src/pages/form/components/origin-panel.tsx b/src/pages/form/components/origin-panel.tsx
index 7584271f..c8511522 100644
--- a/src/pages/form/components/origin-panel.tsx
+++ b/src/pages/form/components/origin-panel.tsx
@@ -138,6 +138,10 @@ export default function OriginPanel({
)}
+
+ Provisioning less than 100 GiB of General Purpose (SSD) storage for high throughput workloads could result in
+ higher latencies upon exhaustion of the initial General Purpose (SSD) IO credit balance.
+
);
diff --git a/src/pages/form/index.tsx b/src/pages/form/index.tsx
index ca9d4475..fc17c69b 100644
--- a/src/pages/form/index.tsx
+++ b/src/pages/form/index.tsx
@@ -3,8 +3,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './app';
+import '../../styles/base.scss';
import '../../styles/form.scss';
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/manage-tags/index.tsx b/src/pages/manage-tags/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/manage-tags/index.tsx
+++ b/src/pages/manage-tags/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/manage-tags/root.tsx b/src/pages/manage-tags/root.tsx
index ffe6a892..f5f588c8 100644
--- a/src/pages/manage-tags/root.tsx
+++ b/src/pages/manage-tags/root.tsx
@@ -9,15 +9,25 @@ import Container from '@cloudscape-design/components/container';
import Form from '@cloudscape-design/components/form';
import Header from '@cloudscape-design/components/header';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import TagEditor, { TagEditorProps } from '@cloudscape-design/components/tag-editor';
import { resourceManageTagsBreadcrumbs } from '../../common/breadcrumbs';
import { tagEditorI18nStrings } from '../../i18n-strings/tag-editor';
import { TagsResource } from '../../resources/types';
-import { CustomAppLayout, InfoLink, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ InfoLink,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import ToolsContent from './components/tools-content';
import '../../styles/base.scss';
+import '../../styles/top-navigation.scss';
type Tag = TagsResource['resourceTags'][number];
@@ -28,6 +38,8 @@ const Breadcrumbs = () => (
export function App() {
const [toolsIndex, setToolsIndex] = useState(0);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const [tags, setTags] = useState([]);
const [loading, setLoading] = useState(true);
const appLayoutRef = useRef(null);
@@ -64,55 +76,68 @@ export function App() {
};
return (
- }
- navigation={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- tools={ToolsContent[toolsIndex]}
- notifications={ }
- content={
-
-
- event.preventDefault()}>
-
- Cancel
- Save changes
-
- }
- >
- }
- description="A tag is a label that you assign to an AWS resource. Each tag consists of a key and an optional value. You can use tags to search and filter your resources or track your AWS costs."
- >
- Tags
-
+ <>
+
+ }
+ navigation={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ tools={ToolsContent[toolsIndex]}
+ notifications={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+
+
+ event.preventDefault()}>
+
+ Cancel
+ Save changes
+
}
>
- window.FakeServer.GetTagKeys().then(({ TagKeys }) => TagKeys)}
- valuesRequest={key =>
- window.FakeServer.GetTagValues(key as keyof TagsResource['valueMap']).then(
- ({ TagValues }) => TagValues,
- )
+ }
+ description="A tag is a label that you assign to an AWS resource. Each tag consists of a key and an optional value. You can use tags to search and filter your resources or track your AWS costs."
+ >
+ Tags
+
}
- loading={loading}
- i18nStrings={tagEditorI18nStrings}
- />
-
-
-
-
- }
- />
+ >
+ window.FakeServer.GetTagKeys().then(({ TagKeys }) => TagKeys)}
+ valuesRequest={key =>
+ window.FakeServer.GetTagValues(key as keyof TagsResource['valueMap']).then(
+ ({ TagValues }) => TagValues,
+ )
+ }
+ loading={loading}
+ i18nStrings={tagEditorI18nStrings}
+ />
+
+
+
+
+ }
+ />
+ >
);
}
diff --git a/src/pages/non-console/index.tsx b/src/pages/non-console/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/non-console/index.tsx
+++ b/src/pages/non-console/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/non-console/root.tsx b/src/pages/non-console/root.tsx
index 6cbc927e..66fac876 100644
--- a/src/pages/non-console/root.tsx
+++ b/src/pages/non-console/root.tsx
@@ -1,21 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import React, { ReactNode, useState } from 'react';
-import { createPortal } from 'react-dom';
+import React from 'react';
import Box from '@cloudscape-design/components/box';
import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
import Button from '@cloudscape-design/components/button';
import Header from '@cloudscape-design/components/header';
-import Input from '@cloudscape-design/components/input';
import SideNavigation, { SideNavigationProps } from '@cloudscape-design/components/side-navigation';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table, { TableProps } from '@cloudscape-design/components/table';
-import TopNavigation from '@cloudscape-design/components/top-navigation';
import { isVisualRefresh } from '../../common/apply-mode';
-import { CustomAppLayout } from '../commons/common-components';
-import logo from './logo.svg';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { Notifications } from './notifications';
import '../../styles/base.scss';
@@ -55,37 +57,6 @@ const breadcrumbs = [
},
];
-const i18nStrings = {
- searchIconAriaLabel: 'Search',
- searchDismissIconAriaLabel: 'Close search',
- overflowMenuTriggerText: 'More',
- overflowMenuTitleText: 'All',
- overflowMenuBackIconAriaLabel: 'Back',
- overflowMenuDismissIconAriaLabel: 'Close menu',
-};
-
-const profileActions = [
- { id: 'profile', text: 'Profile' },
- { id: 'preferences', text: 'Preferences' },
- { id: 'security', text: 'Security' },
- {
- id: 'support-group',
- text: 'Support',
- items: [
- {
- id: 'documentation',
- text: 'Documentation',
- href: '#',
- external: true,
- externalIconAriaLabel: ' (opens in new tab)',
- },
- { id: 'feedback', text: 'Feedback', href: '#', external: true, externalIconAriaLabel: ' (opens in new tab)' },
- { id: 'support', text: 'Customer support' },
- ],
- },
- { id: 'signout', text: 'Sign out' },
-];
-
interface Item {
name: string;
type: string;
@@ -163,65 +134,26 @@ const Content = () => {
);
};
-/**
- * This Portal is for demo purposes only due to the additional
- * header used on the Demo page.
- */
-interface DemoHeaderPortalProps {
- children: ReactNode;
-}
-
-const DemoHeaderPortal = ({ children }: DemoHeaderPortalProps) => {
- const domNode = document.querySelector('#h')!;
- return createPortal(children, domNode);
-};
-
export function App() {
- const [searchValue, setSearchValue] = useState('');
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
+
return (
<>
-
- setSearchValue(detail.value)}
- />
- }
- utilities={[
- {
- type: 'button',
- iconName: 'notification',
- ariaLabel: 'Notifications',
- badge: true,
- disableUtilityCollapse: true,
- },
- { type: 'button', iconName: 'settings', title: 'Settings', ariaLabel: 'Settings' },
- {
- type: 'menu-dropdown',
- text: 'Customer name',
- description: 'customer@example.com',
- iconName: 'user-profile',
- items: profileActions,
- },
- ]}
- />
-
+
}
breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanel={
+
+
+
+ }
content={ }
notifications={ }
/>
diff --git a/src/pages/onboarding/index.tsx b/src/pages/onboarding/index.tsx
index a08b44b4..48991cbb 100644
--- a/src/pages/onboarding/index.tsx
+++ b/src/pages/onboarding/index.tsx
@@ -3,10 +3,16 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
import { Router } from './router';
import { StoreProvider } from './store';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render(
diff --git a/src/pages/product-detail-page/index.tsx b/src/pages/product-detail-page/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/product-detail-page/index.tsx
+++ b/src/pages/product-detail-page/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/product-detail-page/root.tsx b/src/pages/product-detail-page/root.tsx
index 3858e218..c18006bb 100644
--- a/src/pages/product-detail-page/root.tsx
+++ b/src/pages/product-detail-page/root.tsx
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
-import React, { useState } from 'react';
+import React from 'react';
import Badge from '@cloudscape-design/components/badge';
import Box from '@cloudscape-design/components/box';
@@ -8,18 +8,16 @@ import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group';
import Button from '@cloudscape-design/components/button';
import Container from '@cloudscape-design/components/container';
import ContentLayout from '@cloudscape-design/components/content-layout';
-import Flashbar from '@cloudscape-design/components/flashbar';
import Header from '@cloudscape-design/components/header';
-import { I18nProvider } from '@cloudscape-design/components/i18n';
-import enMessages from '@cloudscape-design/components/i18n/messages/all.en.json';
import Link from '@cloudscape-design/components/link';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table from '@cloudscape-design/components/table';
import TextContent from '@cloudscape-design/components/text-content';
import { applyTheme } from '@cloudscape-design/components/theming';
import { isVisualRefresh } from '../../common/apply-mode';
-import { useDisclaimerFlashbarItem } from '../commons/disclaimer-flashbar-item';
+import { CustomAppLayout, GlobalSplitPanelContent, useGlobalSplitPanel } from '../commons/common-components';
import { HeroHeader } from './hero-header';
import badgePartnerAdvanced from './images/aws-partner-badge.png';
import videoThumbnail from './images/video-thumbnail.jpg';
@@ -369,62 +367,76 @@ function RelatedProducts() {
}
export function App() {
- const [disclaimerDismissed, dismissDisclaimer] = useState(false);
- const disclaimerItem = useDisclaimerFlashbarItem(() => dismissDisclaimer(true));
- const showFlashbar = !disclaimerDismissed && disclaimerItem;
+ //const showFlashbar = !disclaimerDismissed && disclaimerItem;
const headerVariant = isVisualRefresh ? 'high-contrast' : 'divider';
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
return (
-
- }
- breadcrumbs={
-
- }
- headerVariant={headerVariant}
- header={ }
- defaultPadding={true}
- maxContentWidth={1040}
- disableOverlap={true}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+
}
+ defaultPadding={true}
+ maxContentWidth={1040}
+ disableOverlap={true}
+ >
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ />
);
}
diff --git a/src/pages/read-from-s3/index.tsx b/src/pages/read-from-s3/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/read-from-s3/index.tsx
+++ b/src/pages/read-from-s3/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/read-from-s3/root.tsx b/src/pages/read-from-s3/root.tsx
index f0c85d5e..1654ed0a 100644
--- a/src/pages/read-from-s3/root.tsx
+++ b/src/pages/read-from-s3/root.tsx
@@ -10,10 +10,18 @@ import FormField from '@cloudscape-design/components/form-field';
import Header from '@cloudscape-design/components/header';
import S3ResourceSelector, { S3ResourceSelectorProps } from '@cloudscape-design/components/s3-resource-selector';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import { readFromS3Breadcrumbs } from '../../common/breadcrumbs';
import { getItems, requestAsyncRegions, S3FetchError } from '../../common/s3-resource-selector/mock-request';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { ErrorAlert, ErrorAlertError } from './common';
import '../../styles/base.scss';
@@ -90,30 +98,44 @@ function S3ResourceSelectorContainer() {
}
export function App() {
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
+
return (
- event.preventDefault()}>
- Run simulation}
- actions={
-
- Cancel
- Run
-
- }
- >
- Simulations}>
-
-
-
-
- }
- breadcrumbs={ }
- navigation={ }
- toolsHide={true}
- notifications={ }
- />
+ <>
+
+
+
+
+ }
+ content={
+ event.preventDefault()}>
+ Run simulation}
+ actions={
+
+ Cancel
+ Run
+
+ }
+ >
+ Simulations}>
+
+
+
+
+ }
+ breadcrumbs={ }
+ navigation={ }
+ toolsHide={true}
+ notifications={ }
+ />
+ >
);
}
diff --git a/src/pages/server-side-table-property-filter/index.tsx b/src/pages/server-side-table-property-filter/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/server-side-table-property-filter/index.tsx
+++ b/src/pages/server-side-table-property-filter/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/server-side-table-property-filter/root.tsx b/src/pages/server-side-table-property-filter/root.tsx
index d18de66f..19d21db5 100644
--- a/src/pages/server-side-table-property-filter/root.tsx
+++ b/src/pages/server-side-table-property-filter/root.tsx
@@ -6,6 +6,7 @@ import intersection from 'lodash/intersection';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Pagination from '@cloudscape-design/components/pagination';
import PropertyFilter, { PropertyFilterProps } from '@cloudscape-design/components/property-filter';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table, { TableProps } from '@cloudscape-design/components/table';
import { parsePropertyFilterQuery } from '../../common/parse-property-filter';
@@ -19,7 +20,15 @@ import {
renderAriaLive,
} from '../../i18n-strings';
import { FullPageHeader } from '../commons';
-import { CustomAppLayout, Navigation, Notifications, TableNoMatchState } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ TableNoMatchState,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES, Preferences } from '../commons/table-config';
import { useColumnWidths } from '../commons/use-column-widths';
import { useLocalStorage } from '../commons/use-local-storage';
@@ -179,29 +188,44 @@ export function App() {
COLUMN_DEFINITIONS,
);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- stickyNotifications={true}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ stickyNotifications={true}
+ />
+ >
);
}
diff --git a/src/pages/server-side-table/index.tsx b/src/pages/server-side-table/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/server-side-table/index.tsx
+++ b/src/pages/server-side-table/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/server-side-table/root.tsx b/src/pages/server-side-table/root.tsx
index b91bf4d1..7f75e283 100644
--- a/src/pages/server-side-table/root.tsx
+++ b/src/pages/server-side-table/root.tsx
@@ -5,6 +5,7 @@ import intersection from 'lodash/intersection';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Pagination from '@cloudscape-design/components/pagination';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table, { TableProps } from '@cloudscape-design/components/table';
import TextFilter from '@cloudscape-design/components/text-filter';
@@ -16,7 +17,15 @@ import {
renderAriaLive,
} from '../../i18n-strings';
import { FullPageHeader } from '../commons';
-import { CustomAppLayout, Navigation, Notifications, TableNoMatchState } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ TableNoMatchState,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES, Preferences } from '../commons/table-config';
import { useColumnWidths } from '../commons/use-column-widths';
import { useLocalStorage } from '../commons/use-local-storage';
@@ -131,27 +140,42 @@ function ServerSideTable({ columnDefinitions, saveWidths, loadHelpPanelContent }
export function App() {
const [columnDefinitions, saveWidths] = useColumnWidths('React-TableServerSide-Widths', COLUMN_DEFINITIONS);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ />
+ >
);
}
diff --git a/src/pages/split-panel-comparison/index.tsx b/src/pages/split-panel-comparison/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/split-panel-comparison/index.tsx
+++ b/src/pages/split-panel-comparison/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/split-panel-multiple/index.tsx b/src/pages/split-panel-multiple/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/split-panel-multiple/index.tsx
+++ b/src/pages/split-panel-multiple/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/table-date-filter/index.tsx b/src/pages/table-date-filter/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/table-date-filter/index.tsx
+++ b/src/pages/table-date-filter/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/table-date-filter/root.tsx b/src/pages/table-date-filter/root.tsx
index 7ea43de1..f73f3fc1 100644
--- a/src/pages/table-date-filter/root.tsx
+++ b/src/pages/table-date-filter/root.tsx
@@ -3,8 +3,16 @@
import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
+import SplitPanel from '@cloudscape-design/components/split-panel';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { useColumnWidths } from '../commons/use-column-widths';
import { useLocalStorage } from '../commons/use-local-storage';
import { Breadcrumbs, ToolsContent } from '../table/common-components';
@@ -22,33 +30,48 @@ export function App() {
const [columnDefinitions, saveWidths] = useColumnWidths('React-TableDateFilter-Widths', COLUMN_DEFINITIONS);
const [preferences, setPreferences] = useLocalStorage('React-TableDateFilter-Preferences', DEFAULT_PREFERENCES);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- columnDefinitions={columnDefinitions}
- contentDisplayOptions={CONTENT_DISPLAY_OPTIONS}
- saveWidths={saveWidths}
- preferences={preferences}
- setPreferences={setPreferences}
- filteringProperties={FILTERING_PROPERTIES}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- stickyNotifications={true}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ columnDefinitions={columnDefinitions}
+ contentDisplayOptions={CONTENT_DISPLAY_OPTIONS}
+ saveWidths={saveWidths}
+ preferences={preferences}
+ setPreferences={setPreferences}
+ filteringProperties={FILTERING_PROPERTIES}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ stickyNotifications={true}
+ />
+ >
);
}
diff --git a/src/pages/table-editable/index.tsx b/src/pages/table-editable/index.tsx
index 300f7630..7397a3d9 100644
--- a/src/pages/table-editable/index.tsx
+++ b/src/pages/table-editable/index.tsx
@@ -3,12 +3,18 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { Distribution } from '../../fake-server/types';
import DataProvider from '../commons/data-provider';
import { App } from './root';
+import '../../styles/base.scss';
+
const dataProvider = new DataProvider();
dataProvider.getData('distributions').then(distributions => {
+ applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
});
diff --git a/src/pages/table-editable/root.tsx b/src/pages/table-editable/root.tsx
index bf98d9dc..886e4b27 100644
--- a/src/pages/table-editable/root.tsx
+++ b/src/pages/table-editable/root.tsx
@@ -5,6 +5,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { useCollection } from '@cloudscape-design/collection-hooks';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Pagination, { PaginationProps } from '@cloudscape-design/components/pagination';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table, { TableProps } from '@cloudscape-design/components/table';
import TextFilter, { TextFilterProps } from '@cloudscape-design/components/text-filter';
@@ -18,10 +19,12 @@ import {
import { FullPageHeader } from '../commons';
import {
CustomAppLayout,
+ GlobalSplitPanelContent,
Navigation,
Notifications,
TableEmptyState,
TableNoMatchState,
+ useGlobalSplitPanel,
} from '../commons/common-components';
import {
DEFAULT_PREFERENCES,
@@ -203,6 +206,8 @@ export interface AppProps {
export function App({ distributions }: AppProps) {
const appLayout = useRef(null);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
return (
}
notifications={ }
breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
content={
);
diff --git a/src/pages/table-expandable/root.tsx b/src/pages/table-expandable/root.tsx
index c69affff..cf50f1d2 100644
--- a/src/pages/table-expandable/root.tsx
+++ b/src/pages/table-expandable/root.tsx
@@ -14,7 +14,7 @@ import { TableProps } from '@cloudscape-design/components/table';
import { getHeaderCounterText, getTextFilterCounterText } from '../../i18n-strings';
import allInstances, { Instance } from '../../resources/related-instances';
-import { FullPageHeader, TableEmptyState, TableNoMatchState } from '../commons/common-components';
+import { DemoTopNavigation, FullPageHeader, TableEmptyState, TableNoMatchState } from '../commons/common-components';
import { PageLayout } from './page-components';
import { createColumns, filteringProperties, tableAriaLabels, TablePreferences } from './table-configs';
@@ -145,89 +145,92 @@ export function App() {
: undefined;
return (
-
- {({ openTools }) => (
- <>
- }
- submitEdit={() => {
- // Do nothing
- }}
- variant="full-page"
- renderAriaLive={renderAriaLive}
- header={
-
-
- Instance actions
-
- Restore from S3
- Launch DB instance
-
- }
- onInfoLinkClick={openTools}
- />
- }
- filter={
-
- }
- expandableRows={expandableRows}
- getLoadingStatus={getLoadingStatus}
- renderLoaderPending={({ item }) => (
- {
- const itemId = item?.name ?? 'ROOT';
- setPages(prev => ({ ...prev, [itemId]: (prev[itemId] ?? 1) + 1 }));
- }}
- ariaLabel={item ? `Show more items for ${item.name}` : 'Show more items'}
- >
- Show more items
-
- )}
- />
-
- {ariaLiveMessage}
- >
- )}
-
+ <>
+
+
+ {({ openTools }) => (
+ <>
+ }
+ submitEdit={() => {
+ // Do nothing
+ }}
+ variant="full-page"
+ renderAriaLive={renderAriaLive}
+ header={
+
+
+ Instance actions
+
+ Restore from S3
+ Launch DB instance
+
+ }
+ onInfoLinkClick={openTools}
+ />
+ }
+ filter={
+
+ }
+ expandableRows={expandableRows}
+ getLoadingStatus={getLoadingStatus}
+ renderLoaderPending={({ item }) => (
+ {
+ const itemId = item?.name ?? 'ROOT';
+ setPages(prev => ({ ...prev, [itemId]: (prev[itemId] ?? 1) + 1 }));
+ }}
+ ariaLabel={item ? `Show more items for ${item.name}` : 'Show more items'}
+ >
+ Show more items
+
+ )}
+ />
+
+ {ariaLiveMessage}
+ >
+ )}
+
+ >
);
}
diff --git a/src/pages/table-property-filter/index.tsx b/src/pages/table-property-filter/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/table-property-filter/index.tsx
+++ b/src/pages/table-property-filter/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/table-property-filter/root.tsx b/src/pages/table-property-filter/root.tsx
index 6b4ab6d6..3169441c 100644
--- a/src/pages/table-property-filter/root.tsx
+++ b/src/pages/table-property-filter/root.tsx
@@ -3,8 +3,16 @@
import React, { useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
+import SplitPanel from '@cloudscape-design/components/split-panel';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES } from '../commons/table-config';
import { useColumnWidths } from '../commons/use-column-widths';
import { useLocalStorage } from '../commons/use-local-storage';
@@ -18,32 +26,47 @@ export function App() {
const [columnDefinitions, saveWidths] = useColumnWidths('React-TablePropertyFilter-Widths', COLUMN_DEFINITIONS);
const [preferences, setPreferences] = useLocalStorage('React-TablePropertyFilter-Preferences', DEFAULT_PREFERENCES);
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- columnDefinitions={columnDefinitions}
- saveWidths={saveWidths}
- preferences={preferences}
- setPreferences={setPreferences}
- filteringProperties={FILTERING_PROPERTIES}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- stickyNotifications={true}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ columnDefinitions={columnDefinitions}
+ saveWidths={saveWidths}
+ preferences={preferences}
+ setPreferences={setPreferences}
+ filteringProperties={FILTERING_PROPERTIES}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ stickyNotifications={true}
+ />
+ >
);
}
diff --git a/src/pages/table-saved-filters/index.tsx b/src/pages/table-saved-filters/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/table-saved-filters/index.tsx
+++ b/src/pages/table-saved-filters/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/table-saved-filters/root.tsx b/src/pages/table-saved-filters/root.tsx
index b201e752..f9566178 100644
--- a/src/pages/table-saved-filters/root.tsx
+++ b/src/pages/table-saved-filters/root.tsx
@@ -9,6 +9,7 @@ import Flashbar from '@cloudscape-design/components/flashbar';
import Pagination from '@cloudscape-design/components/pagination';
import PropertyFilter from '@cloudscape-design/components/property-filter';
import Select from '@cloudscape-design/components/select';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table from '@cloudscape-design/components/table';
import { parsePropertyFilterQuery } from '../../common/parse-property-filter';
@@ -22,7 +23,15 @@ import {
renderAriaLive,
} from '../../i18n-strings';
import { FullPageHeader } from '../commons';
-import { CustomAppLayout, Navigation, TableEmptyState, TableNoMatchState } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Navigation,
+ TableEmptyState,
+ TableNoMatchState,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import DataProvider from '../commons/data-provider';
import { useDisclaimerFlashbarItem } from '../commons/disclaimer-flashbar-item';
import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES } from '../commons/table-config';
@@ -80,6 +89,8 @@ const PROPERTY_FILTERS_QUERY_PARAM_KEY = 'propertyFilter';
export function App() {
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
const [columnDefinitions, saveWidths] = useColumnWidths('React-TableSavedFilters-Widths', COLUMN_DEFINITIONS);
@@ -191,88 +202,101 @@ export function App() {
}, []); // Only run this hook on initial load
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- <>
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- loading={loading}
- loadingText="Loading distributions"
- filter={
-
- }
- customFilterActions={ }
- onChange={event => {
- /**
- * Avoid including sensitive information to the URL to prevent potential data exposure.
- * https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url
- * For further guidance, reach out to your organization’s security team.
- */
- const query = event.detail;
- if (!query.tokens?.length && !query?.tokenGroups?.length) {
- setQueryParam(PROPERTY_FILTERS_QUERY_PARAM_KEY, null);
- setQueryParam(SELECTED_FILTER_SET_QUERY_PARAM_KEY, null);
- } else {
- setQueryParam(PROPERTY_FILTERS_QUERY_PARAM_KEY, JSON.stringify(query));
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ <>
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ loading={loading}
+ loadingText="Loading distributions"
+ filter={
+
}
+ customFilterActions={ }
+ onChange={event => {
+ /**
+ * Avoid including sensitive information to the URL to prevent potential data exposure.
+ * https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url
+ * For further guidance, reach out to your organization’s security team.
+ */
+ const query = event.detail;
+ if (!query.tokens?.length && !query?.tokenGroups?.length) {
+ setQueryParam(PROPERTY_FILTERS_QUERY_PARAM_KEY, null);
+ setQueryParam(SELECTED_FILTER_SET_QUERY_PARAM_KEY, null);
+ } else {
+ setQueryParam(PROPERTY_FILTERS_QUERY_PARAM_KEY, JSON.stringify(query));
+ }
- propertyFilterProps.onChange(event);
- }}
- />
- }
- pagination={ }
- preferences={ }
- />
- {actionModal}
- >
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- />
+ propertyFilterProps.onChange(event);
+ }}
+ />
+ }
+ pagination={ }
+ preferences={ }
+ />
+ {actionModal}
+ >
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ />
+ >
);
}
diff --git a/src/pages/table-select-filter/index.tsx b/src/pages/table-select-filter/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/table-select-filter/index.tsx
+++ b/src/pages/table-select-filter/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/table-select-filter/root.tsx b/src/pages/table-select-filter/root.tsx
index efe0a22b..fcd34845 100644
--- a/src/pages/table-select-filter/root.tsx
+++ b/src/pages/table-select-filter/root.tsx
@@ -12,12 +12,21 @@ import Pagination from '@cloudscape-design/components/pagination';
import Select from '@cloudscape-design/components/select';
import { SelectProps } from '@cloudscape-design/components/select';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table from '@cloudscape-design/components/table';
import { getHeaderCounterText, getTextFilterCounterText, renderAriaLive } from '../../i18n-strings';
import DATA, { Instance } from '../../resources/instances';
import { FullPageHeader } from '../commons';
-import { CustomAppLayout, Notifications, TableEmptyState, TableNoMatchState } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
+ Notifications,
+ TableEmptyState,
+ TableNoMatchState,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { Preferences } from '../commons/table-config';
import { useColumnWidths } from '../commons/use-column-widths';
import { useLocalStorage } from '../commons/use-local-storage';
@@ -225,26 +234,41 @@ function TableSelectFilter({ loadHelpPanelContent }: TableSelectFilter) {
export function App() {
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ />
+ >
);
}
diff --git a/src/pages/table/index.tsx b/src/pages/table/index.tsx
index 63d7894f..5169b89a 100644
--- a/src/pages/table/index.tsx
+++ b/src/pages/table/index.tsx
@@ -3,10 +3,16 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { Distribution } from '../../fake-server/types';
import DataProvider from '../commons/data-provider';
import { App } from './root';
+import '../../styles/base.scss';
+
new DataProvider().getDataWithDates('distributions').then(distributions => {
+ applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
});
diff --git a/src/pages/table/root.tsx b/src/pages/table/root.tsx
index cb1055f4..13b0e34b 100644
--- a/src/pages/table/root.tsx
+++ b/src/pages/table/root.tsx
@@ -5,6 +5,7 @@ import React, { useRef, useState } from 'react';
import { useCollection } from '@cloudscape-design/collection-hooks';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import Pagination from '@cloudscape-design/components/pagination';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Table from '@cloudscape-design/components/table';
import TextFilter from '@cloudscape-design/components/text-filter';
@@ -18,10 +19,13 @@ import {
import { FullPageHeader } from '../commons';
import {
CustomAppLayout,
+ DemoTopNavigation,
+ GlobalSplitPanelContent,
Navigation,
Notifications,
TableEmptyState,
TableNoMatchState,
+ useGlobalSplitPanel,
} from '../commons/common-components';
import { COLUMN_DEFINITIONS, DEFAULT_PREFERENCES, Preferences } from '../commons/table-config';
import { useColumnWidths } from '../commons/use-column-widths';
@@ -29,6 +33,7 @@ import { useLocalStorage } from '../commons/use-local-storage';
import { Breadcrumbs, ToolsContent } from './common-components';
import '../../styles/base.scss';
+import '../../styles/top-navigation.scss';
interface TableContentProps {
distributions: Distribution[];
@@ -96,28 +101,43 @@ export interface AppProps {
export function App({ distributions }: AppProps) {
const [toolsOpen, setToolsOpen] = useState(false);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize, splitPanelPreferences } =
+ useGlobalSplitPanel();
const appLayout = useRef(null);
return (
- }
- notifications={ }
- breadcrumbs={ }
- content={
- {
- setToolsOpen(true);
- appLayout.current?.focusToolsClose();
- }}
- />
- }
- contentType="table"
- tools={ }
- toolsOpen={toolsOpen}
- onToolsChange={({ detail }) => setToolsOpen(detail.open)}
- stickyNotifications
- />
+ <>
+
+ }
+ notifications={ }
+ breadcrumbs={ }
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanelPreferences={splitPanelPreferences}
+ splitPanel={
+
+
+
+ }
+ content={
+ {
+ setToolsOpen(true);
+ appLayout.current?.focusToolsClose();
+ }}
+ />
+ }
+ contentType="table"
+ tools={ }
+ toolsOpen={toolsOpen}
+ onToolsChange={({ detail }) => setToolsOpen(detail.open)}
+ stickyNotifications
+ />
+ >
);
}
diff --git a/src/pages/wizard/index.tsx b/src/pages/wizard/index.tsx
index 6d959417..00510750 100644
--- a/src/pages/wizard/index.tsx
+++ b/src/pages/wizard/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import App from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/wizard/root.tsx b/src/pages/wizard/root.tsx
index 16d4814c..36405892 100644
--- a/src/pages/wizard/root.tsx
+++ b/src/pages/wizard/root.tsx
@@ -4,10 +4,12 @@ import React, { useCallback, useRef, useState } from 'react';
import { AppLayoutProps } from '@cloudscape-design/components/app-layout';
import HelpPanel from '@cloudscape-design/components/help-panel';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import Wizard, { WizardProps } from '@cloudscape-design/components/wizard';
import { ExternalLinkGroup, InfoLink, Notifications } from '../commons';
-import { CustomAppLayout } from '../commons/common-components';
+import { CustomAppLayout, DemoTopNavigation, GlobalSplitPanelContent } from '../commons/common-components';
+import { useGlobalSplitPanel } from '../commons/use-global-split-panel';
import { ToolsContent, WizardState } from './interfaces';
import Engine from './stepComponents/step1';
import Details from './stepComponents/step2';
@@ -17,6 +19,7 @@ import { DEFAULT_STEP_INFO, TOOLS_CONTENT } from './steps-config';
import { Breadcrumbs, Navigation } from './wizard-components';
import '../../styles/wizard.scss';
+import '../../styles/top-navigation.scss';
const steps = [
{
@@ -153,6 +156,7 @@ const App = () => {
onCancel,
onSubmit,
} = useWizard(closeTools, setFormattedToolsContent);
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
const wizardSteps = steps.map(({ title, stateKey, StepContent }) => ({
title,
@@ -167,26 +171,38 @@ const App = () => {
),
}));
return (
- }
- tools={toolsContent}
- toolsOpen={isToolsOpen}
- onToolsChange={onToolsChange}
- breadcrumbs={ }
- contentType="wizard"
- content={
-
- }
- notifications={ }
- />
+ <>
+
+ }
+ tools={toolsContent}
+ toolsOpen={isToolsOpen}
+ onToolsChange={onToolsChange}
+ breadcrumbs={ }
+ contentType="wizard"
+ splitPanelOpen={splitPanelOpen}
+ onSplitPanelToggle={onSplitPanelToggle}
+ splitPanelSize={splitPanelSize}
+ onSplitPanelResize={onSplitPanelResize}
+ splitPanel={
+
+
+
+ }
+ content={
+
+ }
+ notifications={ }
+ />
+ >
);
};
diff --git a/src/pages/write-to-s3/index.tsx b/src/pages/write-to-s3/index.tsx
index f7e11d3e..73764caa 100644
--- a/src/pages/write-to-s3/index.tsx
+++ b/src/pages/write-to-s3/index.tsx
@@ -3,6 +3,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
+import { applyTheme } from '@cloudscape-design/components/theming';
+
+import { themeCoreConfig } from '../../common/theme-core';
import { App } from './root';
+import '../../styles/base.scss';
+
+applyTheme({ theme: themeCoreConfig });
createRoot(document.getElementById('app')!).render( );
diff --git a/src/pages/write-to-s3/root.tsx b/src/pages/write-to-s3/root.tsx
index 9574f5ef..6fc2f16a 100644
--- a/src/pages/write-to-s3/root.tsx
+++ b/src/pages/write-to-s3/root.tsx
@@ -10,10 +10,17 @@ import FormField from '@cloudscape-design/components/form-field';
import Header from '@cloudscape-design/components/header';
import S3ResourceSelector, { S3ResourceSelectorProps } from '@cloudscape-design/components/s3-resource-selector';
import SpaceBetween from '@cloudscape-design/components/space-between';
+import SplitPanel from '@cloudscape-design/components/split-panel';
import { writeToS3Breadcrumbs } from '../../common/breadcrumbs';
import { getItems, requestAsyncRegions, S3FetchError } from '../../common/s3-resource-selector/mock-request';
-import { CustomAppLayout, Navigation, Notifications } from '../commons/common-components';
+import {
+ CustomAppLayout,
+ GlobalSplitPanelContent,
+ Navigation,
+ Notifications,
+ useGlobalSplitPanel,
+} from '../commons/common-components';
import { ErrorAlert, ErrorAlertError } from '../read-from-s3/common';
import '../../styles/base.scss';
@@ -96,9 +103,20 @@ const S3ResourceSelectorContainer = () => {
};
export const App = () => {
+ const { splitPanelOpen, onSplitPanelToggle, splitPanelSize, onSplitPanelResize } = useGlobalSplitPanel();
+
return (
+
+
+ }
content={
event.preventDefault()}>
li {
- padding: 0;
- margin: 0;
- margin-right: 8px;
+//////////////////////////////////////////////////
+/// Custom CSS only for Option Core new theme
+//////////////////////////////////////////////////
+body.custom-css-enabled {
+ --border-item-width-miijiw: 1px !important;
+ --font-box-value-large-weight-wr00sw: 600 !important;
+ //--size-vertical-input-p1d7xx: 30px !important;
+ --color-text-button-inline-icon-default-sm4ql6: #1b232d;
+ --space-button-icon-only-horizontal-i85hxi: 7px !important;
+ // Segmented control
+ .awsui_segment_8cbea_14tk5_161.awsui_selected_8cbea_14tk5_251:not(#\9) {
+ border-radius: 6px !important;
+ }
- > a {
- padding: 0 6px;
+ // Tertiary button background
+ .awsui_button_vjswe_19xwk_157.awsui_variant-link_vjswe_19xwk_264:not(.awsui_disabled_vjswe_19xwk_227) {
+ background: #F6F6F9 !important;
+ &:hover {
+ background: #EBEBF0 !important;
}
+ }
- a,
- div,
- button,
- input,
- label {
- float: left;
- color: cs.$color-text-interactive-default;
- line-height: 16px;
- }
+ .awsui_universal-toolbar_1kzri_iuh1s_153 > .awsui_toolbar-container_1kzri_iuh1s_267:not(#\9) {
+ //border-color: transparent !important;
+ }
- #visual-refresh-toggle {
- margin-right: 5px;
- margin-top: 1px;
- }
+ // Button space adjustment
+ .awsui_button_vjswe_19xwk_157:not(#\9):not(
+ .awsui_button_vjswe_19xwk_157.awsui_button-no-text_vjswe_19xwk_601:not(#\9)
+ ) {
+ padding-inline: 12px;
+ }
- a,
- a:hover {
- cursor: pointer;
- text-decoration: none;
- }
+ .awsui_button_vjswe_19xwk_157.awsui_button-no-text_vjswe_19xwk_601:not(#\9):not(.awsui_button_vjswe_19xwk_157.awsui_variant-inline-icon_vjswe_19xwk_322:not(#\9)) {
+ padding-block: 5px !important;
+ }
+
+ .awsui_button_vjswe_19xwk_157
+ > .awsui_icon-left_vjswe_19xwk_630:not(#\9):not(
+ .awsui_button_vjswe_19xwk_157.awsui_button-no-text_vjswe_19xwk_601 > .awsui_icon_vjswe_19xwk_630
+ ) {
+ inset-inline-start: -2px;
+ }
- &.title {
- font-weight: bold;
- }
+ .awsui_button_vjswe_19xwk_157 > .awsui_icon-right_vjswe_19xwk_635:not(#\9) {
+ inset-inline-end: 0px;
+ }
+
+ .awsui_alert_mx3cw_1l75r_193:not(#\9),
+ .awsui_type-info_mx3cw_1l75r_429:not(#\9) {
+ border-inline-start-width: 2px !important;
+ padding-block: 4px !important;
+ padding-inline: 12px !important;
+ }
+ .awsui_header_mx3cw_1l75r_316:not(#\9), .awsui_flash-header_1q84n_283yi_171:not(#\9) {
+ font-weight: 600;
}
- @media only screen and (max-width: 493px) {
- padding: 4px 20px;
- flex-wrap: wrap;
- height: fit-content;
+ // Tabs typography
+ .awsui_tabs-tab-label_14rmt_nlsa1_226:not(#\9) {
+ font-size: 14px !important;
+ font-weight: 600;
+ }
- .title {
- flex: 1 1 100%;
- margin-bottom: 8px;
- }
+ // Tile selected border width
+ .awsui_tile-container_vj6p7_1p9yl_394.awsui_refresh_vj6p7_1p9yl_419.awsui_selected_vj6p7_1p9yl_423:not(#\9):not(
+ .awsui_disabled_vj6p7_1p9yl_423
+ ):not(.awsui_readonly_vj6p7_1p9yl_423) {
+ box-shadow: none;
+ }
- li {
- width: min-content;
+ // Toolbar
+ .awsui_trigger_lpshu_10v1l_145.awsui_circle_lpshu_10v1l_167:not(#\9) {
+ border-radius: 8px !important;
+ }
- button,
- a {
- text-align: left;
- }
+ // Main content area margin
+ .awsui_main_7nfqu_cwk25_356:not(#\9) {
+ margin-block-start: 24px !important;
+ }
+
+ // Space betwee page title and filter [Just update component globally]
+ .awsui_tools_wih1l_zysm5_164:not(#\9) {
+ padding-block-start: 12px !important;
+ }
+ .awsui_main_2qdw9_m18q1_243.awsui_refresh_2qdw9_m18q1_221:not(#\9) {
+ row-gap: 8px !important;
+ }
+
+ // Side nav "New" dashed underline
+ .awsui_trigger-type-text_xjuzf_15ier_592:not(#\9) {
+ color: #1b232d;
+ }
+
+ // Breadcrumb font weight for current page
+ .awsui_breadcrumb_1kosq_cued1_149.awsui_last_1kosq_cued1_225 > .awsui_anchor_1kosq_cued1_159:not(#\9), .awsui_ghost-breadcrumb_1kosq_cued1_150.awsui_last_1kosq_cued1_225 > .awsui_anchor_1kosq_cued1_159:not(#\9) {
+ font-weight: 600 !important;
+ }
+
+ // Table column resizer
+ .awsui_divider-interactive_x7peu_an1qk_158:not(#\9), th:not(#\9):not(:last-child)>.awsui_divider_x7peu_an1qk_158 {
+ border-width: 2px !important;
+ }
+
+}
- a {
- padding: 0;
+body.custom-css-enabled {
+ &.awsui-dark-mode {
+ --color-text-button-inline-icon-default-sm4ql6: #F9F9FB;
+ // Tertiary button background
+ .awsui_button_vjswe_19xwk_157.awsui_variant-link_vjswe_19xwk_264:not(.awsui_disabled_vjswe_19xwk_227) {
+ background: #232B37 !important;
+ &:hover {
+ background: #424650 !important;
}
}
+ .awsui_trigger-type-text_xjuzf_15ier_592:not(#\9) {
+ color: #c6c6cd;
+ }
}
}
+
+//////////////////////////////////////////////////
+/// Custom CSS for font-smooth on/off
+//////////////////////////////////////////////////
+body.font-smooth-auto {
+ --font-smoothing-moz-osx-hbm0aq: auto !important;
+ --font-smoothing-webkit-oemolo: auto !important;
+}
+
+//////////////////////////////////////////////////
+/// General styles that should not be changed or removed
+//////////////////////////////////////////////////
+.custom-main-header {
+ display: block;
+ position: sticky;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ margin: 0;
+ background-color: #0f1b2a;
+ font-family: cs.$font-family-base;
+}
+
+
diff --git a/update-topnav.sh b/update-topnav.sh
new file mode 100644
index 00000000..039ffafd
--- /dev/null
+++ b/update-topnav.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+# List of files that need updating (excluding already updated ones)
+FILES=(
+ "src/pages/product-detail-page/root.tsx"
+ "src/pages/read-from-s3/root.tsx"
+ "src/pages/server-side-table-property-filter/root.tsx"
+ "src/pages/server-side-table/root.tsx"
+ "src/pages/split-panel-comparison/root.tsx"
+ "src/pages/split-panel-multiple/root.tsx"
+ "src/pages/table-date-filter/root.tsx"
+ "src/pages/table-editable/root.tsx"
+ "src/pages/table-expandable/root.tsx"
+ "src/pages/table-property-filter/root.tsx"
+ "src/pages/table-saved-filters/root.tsx"
+ "src/pages/table-select-filter/root.tsx"
+ "src/pages/write-to-s3/root.tsx"
+ "src/pages/form-unsaved-changes/app.tsx"
+ "src/pages/form-validation/app.tsx"
+)
+
+for file in "${FILES[@]}"; do
+ if [ -f "$file" ]; then
+ echo "Processing $file..."
+
+ # Check if file already has DemoTopNavigation
+ if grep -q "DemoTopNavigation" "$file"; then
+ echo " Skipping - already has DemoTopNavigation"
+ continue
+ fi
+
+ # Add DemoTopNavigation to imports
+ if grep -q "CustomAppLayout" "$file"; then
+ sed -i.bak 's/CustomAppLayout,/CustomAppLayout, DemoTopNavigation,/g' "$file"
+ fi
+
+ # Add top-navigation.scss import if not present
+ if ! grep -q "top-navigation.scss" "$file"; then
+ # Find the last import line and add after it
+ sed -i.bak '/^import.*scss.*;$/a\
+import '\''../../styles/top-navigation.scss'\'';
+' "$file"
+ fi
+
+ # Wrap CustomAppLayout with fragment and DemoTopNavigation
+ # This is a simplified approach - may need manual adjustment
+ sed -i.bak 's/return (/return (\
+ <>\
+ /g' "$file"
+
+ # Add closing fragment before last closing paren of return
+ sed -i.bak 's/ \/>$/ \/>\
+ <\/>/g' "$file"
+
+ echo " Updated $file"
+ else
+ echo " File not found: $file"
+ fi
+done
+
+# Clean up backup files
+find src/pages -name "*.bak" -delete
+
+echo "Done!"
diff --git a/webpack.config.mjs b/webpack.config.mjs
index 2c721b0f..47d8d2f1 100644
--- a/webpack.config.mjs
+++ b/webpack.config.mjs
@@ -179,6 +179,13 @@ const createWebpackConfig = (base, { includeDevServer }) => {
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
+ {
+ test: /\.(ttf|otf)$/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'fonts/[name][ext]',
+ },
+ },
{
test: /\.(png|jpg)$/,
use: ['url-loader'],