diff --git a/hadoop-ozone/dev-support/checks/lint.sh b/hadoop-ozone/dev-support/checks/lint.sh index 7902058452da..a573bc2d082e 100755 --- a/hadoop-ozone/dev-support/checks/lint.sh +++ b/hadoop-ozone/dev-support/checks/lint.sh @@ -24,9 +24,9 @@ REPORT_FILE="$REPORT_DIR/summary.txt" declare -i rc -if [[ -d "${BASE_DIR}/ozone-ui/src" ]]; then +if [[ -d "${BASE_DIR}/ozone-ui" ]]; then ( - cd "${BASE_DIR}/ozone-ui/src" || exit 1 + cd "${BASE_DIR}/ozone-ui" || exit 1 # Install dependencies if node_modules doesn't exist if [[ ! -d "node_modules" ]]; then pnpm install --frozen-lockfile @@ -57,7 +57,7 @@ if [[ -d "${BASE_DIR}/ozone-ui/src" ]]; then fi else rc=0 - echo "ozone-ui/src not found. Skipping UI lint." + echo "ozone-ui not found. Skipping UI lint." fi ERROR_PATTERN="error" diff --git a/ozone-ui/src/.prettierignore b/ozone-ui/.prettierignore similarity index 100% rename from ozone-ui/src/.prettierignore rename to ozone-ui/.prettierignore diff --git a/ozone-ui/README.md b/ozone-ui/README.md index 54b071673e4e..d8e808a19d30 100644 --- a/ozone-ui/README.md +++ b/ozone-ui/README.md @@ -17,99 +17,163 @@ # Ozone UI Monorepo -This monorepo contains three React applications for Apache Ozone UI components: +A Vite + React 18 + TypeScript monorepo (managed with **pnpm workspaces**) that +hosts the Apache Ozone web applications and a shared component library. Styling +is built on **Ant Design v5** themed with the Ozone UI design tokens. -## Applications +## Applications & packages -- **Recon** - Data node management and monitoring -- **SCM** - Storage Container Manager interface -- **OM** - Ozone Manager interface +| Package | Directory | Description | +| ----------------------- | ----------------- | ---------------------------------------------- | +| `@ozone-ui/shared` | `packages/shared` | Design system: theme + reusable components | +| `@ozone-ui/ozone-recon` | `packages/recon` | Recon – datanode management and monitoring | +| `@ozone-ui/ozone-scm` | `packages/scm` | SCM – Storage Container Manager interface | +| `@ozone-ui/ozone-om` | `packages/om` | OM – Ozone Manager interface | -## Structure +## Folder layout ``` -hadoop-ui/src/ -├── packages/ -│ ├── shared/ # Shared components and utilities -│ ├── recon/ # Recon application -│ ├── scm/ # SCM application -│ └── om/ # OM application -├── package.json # Root package.json with workspace configuration -└── pnpm-workspace.yaml # PNPM workspace configuration +ozone-ui/ # pnpm workspace root +├── README.md +├── package.json # Root scripts + shared dev dependencies +├── pnpm-workspace.yaml # Workspace globs (./packages/**) +├── pnpm-lock.yaml +├── tsconfig.json # Base TS config, extended by each package +├── vite.config.shared.ts # Shared Vite config helpers for the apps +├── eslint.config.js # Flat ESLint config +└── packages/ + ├── shared/ # @ozone-ui/shared (design system) + │ └── src/ + │ ├── theme/ # tokens, Ant Design theme, ThemeProvider + │ ├── components/ # Sidebar, UtilityBar, PageHeader, Card, ... + │ ├── utils/ # menuUtils, ... + │ └── index.ts # Public entry point (barrel) + ├── recon/ # @ozone-ui/ozone-recon (Vite app) + ├── scm/ # @ozone-ui/ozone-scm (Vite app) + └── om/ # @ozone-ui/ozone-om (Vite app) ``` -## Development +## Prerequisites -### Prerequisites +- Node.js `>= 20` (Node 20 LTS recommended) +- pnpm `>= 8.15.7` (`corepack enable` provides the pinned version) -- Node.js >= 20.0.0 (Node 20 LTS recommended) -- PNPM >= 8.0.0 - -### Installation +## Install ```bash -# Install all dependencies -pnpm install +cd ozone-ui +pnpm install # installs all workspace dependencies ``` -### Development +## Develop + +The `shared` package is consumed as a built artifact, so build it once (and +after any change to it) before/while running an app: ```bash -# Start development server for a specific app -pnpm dev:recon -pnpm dev:scm -pnpm dev:om +cd ozone-ui + +pnpm build:shared # compile @ozone-ui/shared -> packages/shared/dist -# Build shared components (run this first if you make changes to shared) -pnpm build:shared +pnpm dev:recon # start the Recon app dev server +pnpm dev:scm # start the SCM app dev server +pnpm dev:om # start the OM app dev server ``` -### Building +## Build ```bash -# Build all applications -pnpm build +cd ozone-ui -# Build specific application -pnpm build:recon +pnpm build # build shared, then all three apps +pnpm build:recon # build a single app pnpm build:scm pnpm build:om - -# Build only shared components -pnpm build:shared +pnpm build:shared # build only the shared library ``` -Build outputs are placed in: -- `build/recon/` - Recon application build -- `build/scm/` - SCM application build -- `build/om/` - OM application build +Application build output is written to `build/{recon,scm,om}/`. -### Clean +## Lint & clean ```bash -# Clean all build artifacts and node_modules -pnpm clean +pnpm lint # ESLint across the workspace +pnpm clean # remove build/ and all dist/ + node_modules +pnpm clean:cache # clear Vite caches +pnpm clean:all # clean + clean:cache ``` -## Architecture - -### Shared Components - -The `@hadoop-ui/shared` package contains: - -- **Components**: Reusable React components (e.g., Sidebar) -- **Utils**: Shared utility functions (e.g., menu utilities) -- **Types**: TypeScript type definitions - -### Individual Applications - -Each application (`recon`, `scm`, `om`) is a standalone Vite + React + TypeScript application that can import from the shared package. - -## Technology Stack +## Using the design system + +Each app mounts the theme once near its root (already wired in +`packages/{om,scm,recon}/src/main.tsx`), then consumes shared components and +tokens from `@ozone-ui/shared`: + +```tsx +import { + ThemeProvider, + AppLayout, + Sidebar, + PageHeader, + Card, + KeyValuePair, + Chip, +} from '@ozone-ui/shared'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; + +export default function App() { + return ( + + {}} />}> + + Healthy} + > + + + + + + ); +} +``` -- **Build Tool**: Vite +### What's in `@ozone-ui/shared` + +- **`theme/`** + - `colors`, `semanticColors`, `textStyles`, `fontFamilies`, `spacing`, + `radius` — design tokens (source of truth for colour and typography). + - `ozoneTheme` — an Ant Design v5 `ThemeConfig` derived from the tokens. + - `ThemeProvider` — wraps `ConfigProvider` with the theme and accepts optional + per-app `themeOverrides`. +- **`components/`** (derived from the components recurring across the mockups) + - `UtilityBar` — global top bar (leading/title, centre, actions). + - `Sidebar` — collapsible navigation rail. + - `AppLayout` — page shell (sider + header + content). + - `PageHeader` — page title with breadcrumb, subtitle and actions. + - `Card` — surface with `outlined`/`elevated`/`filled` emphasis and an + optional `collapsible` header. + - `KeyValuePair` — label/value pair (vertical or horizontal, optional link/copy). + - `Chip` — pill: `full`/`dot` variant, `standard`/`small` size, colour and + `selected`/`closable` states. + - `Alert` — inline status banner (info/success/warning/error). + - `TextLink` — themed inline link with optional external affordance. + - `IconButton` — square icon-only button with accessible label + tooltip. + - `Icon` — inline-SVG icon set (`currentColor`, tree-shakeable). + +Prefer the tokens/theme over hard-coded colours or font sizes so re-theming +stays centralised. + +## Technology stack + +- **Build**: Vite 6 (apps), `tsc` (shared library) - **Framework**: React 18 -- **Language**: TypeScript -- **UI Library**: Ant Design v5 -- **Package Manager**: PNPM (with workspaces) -- **Monorepo**: PNPM Workspaces \ No newline at end of file +- **Language**: TypeScript 5.6 +- **UI**: Ant Design v5, themed with the Ozone UI design tokens +- **Fonts**: Roboto / Roboto Mono (`@fontsource/roboto`), Plus Jakarta Sans (app titles) +- **Package manager / monorepo**: pnpm workspaces diff --git a/ozone-ui/src/eslint.config.js b/ozone-ui/eslint.config.js similarity index 98% rename from ozone-ui/src/eslint.config.js rename to ozone-ui/eslint.config.js index 295c32b520af..195fd5d4eee4 100644 --- a/ozone-ui/src/eslint.config.js +++ b/ozone-ui/eslint.config.js @@ -24,7 +24,7 @@ const compat = new FlatCompat({ module.exports = [ { - ignores: ['dist', 'build', 'node_modules'], + ignores: ['**/dist/**', '**/build/**', '**/node_modules/**'], }, ...compat.config({ env: { diff --git a/ozone-ui/src/package.json b/ozone-ui/package.json similarity index 74% rename from ozone-ui/src/package.json rename to ozone-ui/package.json index 7c0aec7a04d3..5a1b5ddfc22d 100644 --- a/ozone-ui/src/package.json +++ b/ozone-ui/package.json @@ -1,18 +1,18 @@ { "packageManager": "pnpm@8.15.7", - "name": "hadoop-ui", + "name": "ozone-ui", "version": "1.0.0", "description": "The home of HDDS UI code.", "scripts": { "build": "pnpm run build:shared && pnpm run build:all", - "build:shared": "pnpm --filter @hadoop-ui/shared run build", - "build:all": "pnpm --filter @hadoop-ui/ozone-recon --filter @hadoop-ui/ozone-scm --filter @hadoop-ui/ozone-om run build", - "build:recon": "pnpm --filter @hadoop-ui/ozone-recon run build", - "build:scm": "pnpm --filter @hadoop-ui/ozone-scm run build", - "build:om": "pnpm --filter @hadoop-ui/ozone-om run build", - "dev:recon": "pnpm --filter @hadoop-ui/ozone-recon run dev", - "dev:scm": "pnpm --filter @hadoop-ui/ozone-scm run dev", - "dev:om": "pnpm --filter @hadoop-ui/ozone-om run dev", + "build:shared": "pnpm --filter @ozone-ui/shared run build", + "build:all": "pnpm --filter @ozone-ui/ozone-recon --filter @ozone-ui/ozone-scm --filter @ozone-ui/ozone-om run build", + "build:recon": "pnpm --filter @ozone-ui/ozone-recon run build", + "build:scm": "pnpm --filter @ozone-ui/ozone-scm run build", + "build:om": "pnpm --filter @ozone-ui/ozone-om run build", + "dev:recon": "pnpm --filter @ozone-ui/ozone-recon run dev", + "dev:scm": "pnpm --filter @ozone-ui/ozone-scm run dev", + "dev:om": "pnpm --filter @ozone-ui/ozone-om run dev", "clean": "rm -rf build && pnpm -r exec rm -rf dist node_modules", "clean:cache": "rm -rf node_modules/.vite && pnpm -r exec rm -rf node_modules/.vite", "clean:all": "pnpm run clean && pnpm run clean:cache", @@ -51,7 +51,7 @@ "prettier": "^3.8.1", "typescript": "~5.6.2", "typescript-eslint": "8.54.0", - "vite": "^5.4.10", + "vite": "^6.4.3", "vite-plugin-svgr": "4.5.0", "vite-tsconfig-paths": "^3.6.0", "vitest": "^1.6.1" diff --git a/ozone-ui/src/packages/om/index.html b/ozone-ui/packages/om/index.html similarity index 100% rename from ozone-ui/src/packages/om/index.html rename to ozone-ui/packages/om/index.html diff --git a/ozone-ui/src/packages/om/package.json b/ozone-ui/packages/om/package.json similarity index 95% rename from ozone-ui/src/packages/om/package.json rename to ozone-ui/packages/om/package.json index 030281623430..06f5991be535 100644 --- a/ozone-ui/src/packages/om/package.json +++ b/ozone-ui/packages/om/package.json @@ -1,5 +1,5 @@ { - "name": "@hadoop-ui/ozone-om", + "name": "@ozone-ui/ozone-om", "private": true, "version": "1.0.0", "scripts": { @@ -8,7 +8,7 @@ "lint": "eslint ." }, "dependencies": { - "@hadoop-ui/shared": "workspace:*", + "@ozone-ui/shared": "workspace:*", "@ant-design/icons": "^5.6.1", "@fontsource/roboto": "^4.5.8", "ag-charts-community": "^7.3.0", diff --git a/ozone-ui/src/packages/om/src/App.css b/ozone-ui/packages/om/src/App.css similarity index 100% rename from ozone-ui/src/packages/om/src/App.css rename to ozone-ui/packages/om/src/App.css diff --git a/ozone-ui/src/packages/om/src/App.tsx b/ozone-ui/packages/om/src/App.tsx similarity index 100% rename from ozone-ui/src/packages/om/src/App.tsx rename to ozone-ui/packages/om/src/App.tsx diff --git a/ozone-ui/src/packages/om/src/index.css b/ozone-ui/packages/om/src/index.css similarity index 100% rename from ozone-ui/src/packages/om/src/index.css rename to ozone-ui/packages/om/src/index.css diff --git a/ozone-ui/src/packages/scm/src/main.tsx b/ozone-ui/packages/om/src/main.tsx similarity index 82% rename from ozone-ui/src/packages/scm/src/main.tsx rename to ozone-ui/packages/om/src/main.tsx index e5de33f5d7ba..e4b310bf1344 100644 --- a/ozone-ui/src/packages/scm/src/main.tsx +++ b/ozone-ui/packages/om/src/main.tsx @@ -17,11 +17,17 @@ */ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { ThemeProvider } from '@ozone-ui/shared'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; import App from './App'; import './index.css'; createRoot(document.getElementById('root')!).render( - + + + ); diff --git a/ozone-ui/src/packages/om/tsconfig.json b/ozone-ui/packages/om/tsconfig.json similarity index 100% rename from ozone-ui/src/packages/om/tsconfig.json rename to ozone-ui/packages/om/tsconfig.json diff --git a/ozone-ui/src/packages/om/vite.config.ts b/ozone-ui/packages/om/vite.config.ts similarity index 98% rename from ozone-ui/src/packages/om/vite.config.ts rename to ozone-ui/packages/om/vite.config.ts index 3130d4398cc0..43accb318006 100644 --- a/ozone-ui/src/packages/om/vite.config.ts +++ b/ozone-ui/packages/om/vite.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ ], build: { target: 'es2015', - outDir: '../../../build/om', + outDir: '../../build/om', rollupOptions: { output: { chunkFileNames: 'static/js/[name]-[hash].js', diff --git a/ozone-ui/src/packages/recon/index.html b/ozone-ui/packages/recon/index.html similarity index 100% rename from ozone-ui/src/packages/recon/index.html rename to ozone-ui/packages/recon/index.html diff --git a/ozone-ui/src/packages/scm/package.json b/ozone-ui/packages/recon/package.json similarity index 95% rename from ozone-ui/src/packages/scm/package.json rename to ozone-ui/packages/recon/package.json index 5a720d64d442..560bf596b095 100644 --- a/ozone-ui/src/packages/scm/package.json +++ b/ozone-ui/packages/recon/package.json @@ -1,5 +1,5 @@ { - "name": "@hadoop-ui/ozone-scm", + "name": "@ozone-ui/ozone-recon", "private": true, "version": "1.0.0", "scripts": { @@ -8,7 +8,7 @@ "lint": "eslint ." }, "dependencies": { - "@hadoop-ui/shared": "workspace:*", + "@ozone-ui/shared": "workspace:*", "@ant-design/icons": "^5.6.1", "@fontsource/roboto": "^4.5.8", "ag-charts-community": "^7.3.0", diff --git a/ozone-ui/src/packages/recon/src/App.css b/ozone-ui/packages/recon/src/App.css similarity index 100% rename from ozone-ui/src/packages/recon/src/App.css rename to ozone-ui/packages/recon/src/App.css diff --git a/ozone-ui/src/packages/recon/src/App.tsx b/ozone-ui/packages/recon/src/App.tsx similarity index 63% rename from ozone-ui/src/packages/recon/src/App.tsx rename to ozone-ui/packages/recon/src/App.tsx index c107a41b64b4..e26483204c0b 100644 --- a/ozone-ui/src/packages/recon/src/App.tsx +++ b/ozone-ui/packages/recon/src/App.tsx @@ -16,32 +16,24 @@ * limitations under the License. */ import { useState } from 'react'; -import reactLogo from './assets/react.svg'; -import viteLogo from '/vite.svg'; +import { Button } from 'antd'; import './App.css'; function App() { const [count, setCount] = useState(0); return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

+
+

Ozone Recon

- +

Edit src/App.tsx and save to test HMR

-

Click on the Vite and React logos to learn more

- +
); } diff --git a/ozone-ui/src/packages/recon/src/index.css b/ozone-ui/packages/recon/src/index.css similarity index 100% rename from ozone-ui/src/packages/recon/src/index.css rename to ozone-ui/packages/recon/src/index.css diff --git a/ozone-ui/src/packages/recon/src/main.tsx b/ozone-ui/packages/recon/src/main.tsx similarity index 82% rename from ozone-ui/src/packages/recon/src/main.tsx rename to ozone-ui/packages/recon/src/main.tsx index 1c6bbc3b61e1..814e0415e532 100644 --- a/ozone-ui/src/packages/recon/src/main.tsx +++ b/ozone-ui/packages/recon/src/main.tsx @@ -17,11 +17,17 @@ */ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { ThemeProvider } from '@ozone-ui/shared'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; import './index.css'; import App from './App'; createRoot(document.getElementById('root')!).render( - + + + ); diff --git a/ozone-ui/src/packages/recon/src/vite-env.d.ts b/ozone-ui/packages/recon/src/vite-env.d.ts similarity index 100% rename from ozone-ui/src/packages/recon/src/vite-env.d.ts rename to ozone-ui/packages/recon/src/vite-env.d.ts diff --git a/ozone-ui/src/packages/recon/tsconfig.json b/ozone-ui/packages/recon/tsconfig.json similarity index 100% rename from ozone-ui/src/packages/recon/tsconfig.json rename to ozone-ui/packages/recon/tsconfig.json diff --git a/ozone-ui/src/packages/recon/vite.config.ts b/ozone-ui/packages/recon/vite.config.ts similarity index 98% rename from ozone-ui/src/packages/recon/vite.config.ts rename to ozone-ui/packages/recon/vite.config.ts index 9726123e879e..d0ed72b742fa 100644 --- a/ozone-ui/src/packages/recon/vite.config.ts +++ b/ozone-ui/packages/recon/vite.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ ], build: { target: 'es2015', - outDir: '../../../build/recon', + outDir: '../../build/recon', rollupOptions: { output: { chunkFileNames: 'static/js/[name]-[hash].js', diff --git a/ozone-ui/src/packages/scm/index.html b/ozone-ui/packages/scm/index.html similarity index 100% rename from ozone-ui/src/packages/scm/index.html rename to ozone-ui/packages/scm/index.html diff --git a/ozone-ui/src/packages/recon/package.json b/ozone-ui/packages/scm/package.json similarity index 94% rename from ozone-ui/src/packages/recon/package.json rename to ozone-ui/packages/scm/package.json index 653da3d02455..d07ab0bbb4c6 100644 --- a/ozone-ui/src/packages/recon/package.json +++ b/ozone-ui/packages/scm/package.json @@ -1,5 +1,5 @@ { - "name": "@hadoop-ui/ozone-recon", + "name": "@ozone-ui/ozone-scm", "private": true, "version": "1.0.0", "scripts": { @@ -8,7 +8,7 @@ "lint": "eslint ." }, "dependencies": { - "@hadoop-ui/shared": "workspace:*", + "@ozone-ui/shared": "workspace:*", "@ant-design/icons": "^5.6.1", "@fontsource/roboto": "^4.5.8", "ag-charts-community": "^7.3.0", diff --git a/ozone-ui/src/packages/scm/src/App.css b/ozone-ui/packages/scm/src/App.css similarity index 100% rename from ozone-ui/src/packages/scm/src/App.css rename to ozone-ui/packages/scm/src/App.css diff --git a/ozone-ui/src/packages/scm/src/App.tsx b/ozone-ui/packages/scm/src/App.tsx similarity index 100% rename from ozone-ui/src/packages/scm/src/App.tsx rename to ozone-ui/packages/scm/src/App.tsx diff --git a/ozone-ui/src/packages/scm/src/index.css b/ozone-ui/packages/scm/src/index.css similarity index 100% rename from ozone-ui/src/packages/scm/src/index.css rename to ozone-ui/packages/scm/src/index.css diff --git a/ozone-ui/src/packages/om/src/main.tsx b/ozone-ui/packages/scm/src/main.tsx similarity index 82% rename from ozone-ui/src/packages/om/src/main.tsx rename to ozone-ui/packages/scm/src/main.tsx index e5de33f5d7ba..e4b310bf1344 100644 --- a/ozone-ui/src/packages/om/src/main.tsx +++ b/ozone-ui/packages/scm/src/main.tsx @@ -17,11 +17,17 @@ */ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { ThemeProvider } from '@ozone-ui/shared'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; import App from './App'; import './index.css'; createRoot(document.getElementById('root')!).render( - + + + ); diff --git a/ozone-ui/src/packages/scm/tsconfig.json b/ozone-ui/packages/scm/tsconfig.json similarity index 100% rename from ozone-ui/src/packages/scm/tsconfig.json rename to ozone-ui/packages/scm/tsconfig.json diff --git a/ozone-ui/src/packages/scm/vite.config.ts b/ozone-ui/packages/scm/vite.config.ts similarity index 98% rename from ozone-ui/src/packages/scm/vite.config.ts rename to ozone-ui/packages/scm/vite.config.ts index b817af1296fd..a1b7859a7d26 100644 --- a/ozone-ui/src/packages/scm/vite.config.ts +++ b/ozone-ui/packages/scm/vite.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ ], build: { target: 'es2015', - outDir: '../../../build/scm', + outDir: '../../build/scm', rollupOptions: { output: { chunkFileNames: 'static/js/[name]-[hash].js', diff --git a/ozone-ui/src/packages/shared/icons/favicon.ico b/ozone-ui/packages/shared/icons/favicon.ico similarity index 100% rename from ozone-ui/src/packages/shared/icons/favicon.ico rename to ozone-ui/packages/shared/icons/favicon.ico diff --git a/ozone-ui/src/packages/shared/package.json b/ozone-ui/packages/shared/package.json similarity index 95% rename from ozone-ui/src/packages/shared/package.json rename to ozone-ui/packages/shared/package.json index a8f720c1f296..ba451ac43127 100644 --- a/ozone-ui/src/packages/shared/package.json +++ b/ozone-ui/packages/shared/package.json @@ -1,5 +1,5 @@ { - "name": "@hadoop-ui/shared", + "name": "@ozone-ui/shared", "private": true, "version": "1.0.0", "type": "module", diff --git a/ozone-ui/packages/shared/src/components/Alert/Alert.tsx b/ozone-ui/packages/shared/src/components/Alert/Alert.tsx new file mode 100644 index 000000000000..67e1b86cd263 --- /dev/null +++ b/ozone-ui/packages/shared/src/components/Alert/Alert.tsx @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Alert as AntAlert, type AlertProps as AntAlertProps } from 'antd'; +import { radius } from '../../theme/tokens'; + +export type AlertProps = AntAlertProps; + +/** + * Inline status banner. Thin wrapper over Ant Design's `Alert` applying the + * design-system radius; use the standard `type` (`info` / `success` / + * `warning` / `error`), `message`, `description`, `showIcon` and `closable` + * props. Matches the "Alert / Standard" component in the mockups. + */ +export const Alert: React.FC = ({ showIcon = true, style, ...rest }) => ( + +); + +export default Alert; diff --git a/ozone-ui/packages/shared/src/components/AppLayout/AppLayout.tsx b/ozone-ui/packages/shared/src/components/AppLayout/AppLayout.tsx new file mode 100644 index 000000000000..8933c48e3ba1 --- /dev/null +++ b/ozone-ui/packages/shared/src/components/AppLayout/AppLayout.tsx @@ -0,0 +1,109 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Layout, Typography } from 'antd'; +import { semanticColors, spacing, textStyles } from '../../theme/tokens'; + +const { Header, Content } = Layout; + +export interface AppLayoutProps { + /** Navigation rail, typically the shared `Sidebar`. */ + sider?: React.ReactNode; + /** Page/section title rendered in the header. */ + title?: React.ReactNode; + /** Right-aligned header content (actions, user menu, breadcrumbs, ...). */ + headerExtra?: React.ReactNode; + /** Main page content. */ + children?: React.ReactNode; + /** Constrain content width and centre it (useful for form/detail pages). */ + maxContentWidth?: number; +} + +/** + * Application shell: a fixed navigation rail on the left, a top header with a + * title and optional actions, and a scrollable content area on the design-system + * layout background. Compose with the shared `Sidebar` for the `sider` slot. + */ +export const AppLayout: React.FC = ({ + sider, + title, + headerExtra, + children, + maxContentWidth, +}) => { + return ( + + {sider} + + {(title || headerExtra) && ( +
+ {typeof title === 'string' ? ( + + {title} + + ) : ( + title + )} + {headerExtra && ( +
+ {headerExtra} +
+ )} +
+ )} + +
+ {children} +
+
+
+
+ ); +}; + +export default AppLayout; diff --git a/ozone-ui/packages/shared/src/components/Card/Card.tsx b/ozone-ui/packages/shared/src/components/Card/Card.tsx new file mode 100644 index 000000000000..a9f9a01b597e --- /dev/null +++ b/ozone-ui/packages/shared/src/components/Card/Card.tsx @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState } from 'react'; +import { Button, Card as AntCard, type CardProps as AntCardProps } from 'antd'; +import { DownOutlined, UpOutlined } from '@ant-design/icons'; +import { radius, semanticColors } from '../../theme/tokens'; + +export interface CardProps extends AntCardProps { + /** + * Visual emphasis of the card surface. + * - `outlined` (default): white surface with a subtle border. + * - `elevated`: white surface with a soft shadow and no border. + * - `filled`: muted pewter surface, useful for nested/secondary panels. + */ + emphasis?: 'outlined' | 'elevated' | 'filled'; + /** + * Enable the "Card With Header" collapse affordance: a caret toggle in the + * header hides/shows the card body. Requires a `title`. + */ + collapsible?: boolean; + /** Initial collapsed state when `collapsible` (uncontrolled). */ + defaultCollapsed?: boolean; +} + +const emphasisStyles: Record, React.CSSProperties> = { + outlined: { + border: `1px solid ${semanticColors.border}`, + boxShadow: 'none', + background: semanticColors.bgContainer, + }, + elevated: { + border: 'none', + boxShadow: '0 1px 2px rgba(35, 43, 48, 0.06), 0 4px 12px rgba(35, 43, 48, 0.08)', + background: semanticColors.bgElevated, + }, + filled: { + border: `1px solid ${semanticColors.border}`, + boxShadow: 'none', + background: semanticColors.bgLayout, + }, +}; + +/** + * Surface container. Thin wrapper over Ant Design's `Card` that applies the + * design-system radius, borders and elevation presets, plus an optional + * collapsible header. All standard `Card` props are supported. + */ +export const Card: React.FC = ({ + emphasis = 'outlined', + collapsible = false, + defaultCollapsed = false, + extra, + children, + style, + styles, + ...rest +}) => { + const [collapsed, setCollapsed] = useState(defaultCollapsed); + + const collapseToggle = collapsible ? ( +