init: sudah ganti logo, hilangin setting, dan investigational use dialog
17
platform/ui-next/.webpack/template.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0"
|
||||
/>
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div
|
||||
id="root"
|
||||
class="min-h-screen"
|
||||
></div>
|
||||
</body>
|
||||
</html>
|
||||
11
platform/ui-next/.webpack/webpack.dev.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
const ENTRY = {
|
||||
app: `${SRC_DIR}/index.ts`,
|
||||
};
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
|
||||
};
|
||||
60
platform/ui-next/.webpack/webpack.prod.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const pkg = require('./../package.json');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
const ENTRY = {
|
||||
app: `${SRC_DIR}/index.ts`,
|
||||
};
|
||||
|
||||
const outputName = `ohif-${pkg.name.split('/').pop()}`;
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
|
||||
|
||||
return merge(commonConfig, {
|
||||
stats: {
|
||||
colors: true,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: false,
|
||||
chunkModules: false,
|
||||
modules: false,
|
||||
children: false,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: false,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'ohif-ui',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
externals: [
|
||||
/\b(dcmjs)/,
|
||||
/\b(gl-matrix)/,
|
||||
{
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: `./dist/${outputName}.css`,
|
||||
chunkFilename: `./dist/${outputName}.css`,
|
||||
}),
|
||||
// new BundleAnalyzerPlugin({}),
|
||||
],
|
||||
});
|
||||
};
|
||||
1717
platform/ui-next/CHANGELOG.md
Normal file
256
platform/ui-next/assets/data/index.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
const actionOptionsMap: { [key: string]: string[] } = {
|
||||
Measurement: ['Rename', 'Lock', 'Delete'],
|
||||
Segmentation: ['Rename', 'Lock', 'Export', 'Delete'],
|
||||
'ROI Tools': ['Rename', 'Lock', 'Delete'],
|
||||
'Organ Segmentation': ['Rename', 'Lock', 'Export', 'Delete'],
|
||||
// Add more types and their corresponding actions as needed
|
||||
};
|
||||
|
||||
const dataList = [
|
||||
{
|
||||
type: 'Measurement',
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Measurement Label',
|
||||
description: 'Description for Measurement One.',
|
||||
optionalField: 'Optional Info 1',
|
||||
details: { primary: ['Data'], secondary: [] },
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Measurement Label',
|
||||
description: 'Description for Measurement Two.',
|
||||
details: { primary: ['Data'], secondary: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Segmentation',
|
||||
items: [
|
||||
{
|
||||
id: 3,
|
||||
title: 'Segmentation One',
|
||||
colorHex: '#FF5733',
|
||||
description: 'Description for Segmentation One.',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Segmentation Two',
|
||||
colorHex: '#FF5733',
|
||||
description: 'Description for Segmentation Two.',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Segmentation Three',
|
||||
colorHex: '#FF5733',
|
||||
description: 'Description for Segmentation Three.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'ROI Tools',
|
||||
items: [
|
||||
{
|
||||
id: 6,
|
||||
title: 'Linear',
|
||||
description: 'Description for Linear.',
|
||||
details: { primary: ['49.2 mm'], secondary: ['S2 I:1'] },
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: 'Bidirectional',
|
||||
description: 'Description for Bidirectional.',
|
||||
details: { primary: ['L: 34.5 mm', 'W: 23.0 mm'], secondary: ['S:2 I:2'] },
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: 'Ellipse',
|
||||
description: 'Description for Ellipse.',
|
||||
details: { primary: ['2641 mm²', 'Max: 1087 HU'], secondary: ['S:2 I:4'] },
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: 'Rectangle',
|
||||
description: 'Description for Rectangle.',
|
||||
details: { primary: ['1426 mm²', 'Max: 718 HU'], secondary: ['S:2 I:5'] },
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: 'Circle',
|
||||
description: 'Description for Circle.',
|
||||
details: { primary: ['7339 mm²', 'Max: 871 HU'], secondary: ['S:2 I:6'] },
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: 'Freehand ROI',
|
||||
description: 'Description for Freehand ROI.',
|
||||
details: {
|
||||
primary: ['Mean: 215 HU', 'Max: 947 HU', 'Area: 839 mm²'],
|
||||
secondary: ['S:2 I:7', 'S:3 I:7'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: 'Spline Tool',
|
||||
description: 'Description for Spline Tool.',
|
||||
details: { primary: ['Area: 203 mm²'], secondary: ['S:2 I:8'] },
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
title: 'Livewire Tool',
|
||||
description: 'Description for Livewire Tool.',
|
||||
details: { primary: ['Area: 203 mm²'], secondary: ['S:2 I:3'] },
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
title: 'Annotation Lorem ipsum dolor sit amet long measurement name continues here',
|
||||
description: 'Description for Annotation.',
|
||||
details: { primary: ['Area: 203 mm²'], secondary: ['S:2 I:3'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Organ Segmentation',
|
||||
items: [
|
||||
{
|
||||
id: 15,
|
||||
title: 'Spleen',
|
||||
description: 'Description for Spleen.',
|
||||
colorHex: '#6B8E23',
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
title: 'Kidney',
|
||||
description: 'Description for Kidney.',
|
||||
colorHex: '#4682B4',
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
title: 'Kidney very long title name lorem ipsum dolor sit amet segmentation',
|
||||
description: 'Description for Kidney.',
|
||||
colorHex: '#9ACD32',
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
title: 'Gallbladder',
|
||||
description: 'Description for Gallbladder.',
|
||||
colorHex: '#20B2AA',
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
title: 'Esophagus',
|
||||
description: 'Description for Esophagus.',
|
||||
colorHex: '#DAA520',
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
title: 'Liver',
|
||||
description: 'Description for Liver.',
|
||||
colorHex: '#CD5C5C',
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
title: 'Stomach',
|
||||
description: 'Description for Stomach.',
|
||||
colorHex: '#778899',
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
title: 'Abdominal aorta',
|
||||
description: 'Description for Abdominal Aorta.',
|
||||
colorHex: '#B8860B',
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
title: 'Inferior vena cava',
|
||||
description: 'Description for Inferior Vena Cava.',
|
||||
colorHex: '#556B2F',
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
title: 'Portal vein',
|
||||
description: 'Description for Portal Vein.',
|
||||
colorHex: '#8B4513',
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
title: 'Pancreas',
|
||||
description: 'Description for Pancreas.',
|
||||
colorHex: '#2F4F4F',
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
title: 'Adrenal gland',
|
||||
description: 'Description for Adrenal Gland.',
|
||||
colorHex: '#708090',
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
title: 'Adrenal gland',
|
||||
description: 'Description for Adrenal Gland.',
|
||||
colorHex: '#6A5ACD',
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
title: 'New Seg Test New Seg Test New Seg Test New Seg Test New Seg Test New Seg Test ',
|
||||
description: 'Description for New Seg Test.',
|
||||
colorHex: '#4682B4',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'TMTV1',
|
||||
items: [
|
||||
{
|
||||
id: 29,
|
||||
title: 'Segment 1',
|
||||
colorHex: '#FF6F61',
|
||||
description: 'Description for Segmentation One.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
title: 'Segment 2',
|
||||
colorHex: '#00CED1',
|
||||
description: 'Description for Segmentation Two.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
title: 'Segment 3',
|
||||
colorHex: '#88B04B',
|
||||
description: 'Description for Segmentation One.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'TMTV2',
|
||||
items: [
|
||||
{
|
||||
id: 32,
|
||||
title: 'Segment A',
|
||||
colorHex: '#FF6F61',
|
||||
description: 'Description for Segmentation One.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
title: 'Segment B',
|
||||
colorHex: '#00CED1',
|
||||
description: 'Description for Segmentation Two.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
{
|
||||
id: 34,
|
||||
title: 'Segment C',
|
||||
colorHex: '#88B04B',
|
||||
description: 'Description for Segmentation One.',
|
||||
details: { primary: ['SUV Peak: NaN', 'Volume: 21.56mm³'], secondary: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export { actionOptionsMap, dataList };
|
||||
BIN
platform/ui-next/assets/images/CT-AAA.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
platform/ui-next/assets/images/CT-AAA2.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
platform/ui-next/assets/images/CT-Air.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
platform/ui-next/assets/images/CT-Bone.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
platform/ui-next/assets/images/CT-Bones.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
platform/ui-next/assets/images/CT-Cardiac.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
platform/ui-next/assets/images/CT-Cardiac2.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
platform/ui-next/assets/images/CT-Cardiac3.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
platform/ui-next/assets/images/CT-Chest-Contrast-Enhanced.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
platform/ui-next/assets/images/CT-Chest-Vessels.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
platform/ui-next/assets/images/CT-Coronary-Arteries-2.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
platform/ui-next/assets/images/CT-Coronary-Arteries-3.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
platform/ui-next/assets/images/CT-Coronary-Arteries.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
platform/ui-next/assets/images/CT-Cropped-Volume-Bone.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
platform/ui-next/assets/images/CT-Fat.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
platform/ui-next/assets/images/CT-Liver-Vasculature.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
platform/ui-next/assets/images/CT-Lung.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
platform/ui-next/assets/images/CT-MIP.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
platform/ui-next/assets/images/CT-Muscle.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
platform/ui-next/assets/images/CT-Pulmonary-Arteries.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
platform/ui-next/assets/images/CT-Soft-Tissue.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
platform/ui-next/assets/images/DTI-FA-Brain.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
platform/ui-next/assets/images/MR-Angio.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
platform/ui-next/assets/images/MR-Default.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
platform/ui-next/assets/images/MR-MIP.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
platform/ui-next/assets/images/MR-T2-Brain.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
platform/ui-next/assets/images/VolumeRendering.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
56
platform/ui-next/babel.config.js
Normal file
@@ -0,0 +1,56 @@
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
const { extendDefaultPlugins } = require('svgo');
|
||||
|
||||
module.exports = {
|
||||
babelrcRoots: ['./platform/*', './extensions/*', './modes/*'],
|
||||
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
|
||||
plugins: [
|
||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||
'@babel/plugin-transform-typescript',
|
||||
['@babel/plugin-proposal-private-methods', { loose: true }],
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
],
|
||||
env: {
|
||||
test: {
|
||||
presets: [
|
||||
[
|
||||
// TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: 'commonjs',
|
||||
debug: false,
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/transform-destructuring',
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-transform-typescript',
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
],
|
||||
},
|
||||
production: {
|
||||
presets: [
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
development: {
|
||||
presets: [
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
},
|
||||
};
|
||||
17
platform/ui-next/components.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src/tailwind.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "src/components",
|
||||
"utils": "src/lib/utils"
|
||||
}
|
||||
}
|
||||
70
platform/ui-next/package.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "@ohif/ui-next",
|
||||
"version": "3.10.0-beta.111",
|
||||
"description": "Next version of OHIF Viewers UI, more customizable using shadcn/ui",
|
||||
"main": "dist/ohif-ui-next.umd.js",
|
||||
"module": "src/index.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rm -rf node_modules/.cache/storybook && shx rm -rf dist",
|
||||
"clean:deep": "yarn run clean && shx rm -rf node_modules",
|
||||
"start": "yarn run build --watch",
|
||||
"dev": "cross-env NODE_ENV=development webpack serve --config .webpack/webpack.playground.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build"
|
||||
},
|
||||
"exports": {
|
||||
"./tailwind.config": "./tailwind.config.ts",
|
||||
"./lib/*": "./src/lib/*.ts",
|
||||
"./components/*": "./src/components/*.tsx",
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.0",
|
||||
"@radix-ui/react-checkbox": "^1.1.1",
|
||||
"@radix-ui/react-context-menu": "^2.2.4",
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slider": "^1.2.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "*",
|
||||
"cmdk": "^1.0.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"framer-motion": "6.2.4",
|
||||
"lucide-react": "^0.379.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-shepherd": "6.1.1",
|
||||
"shepherd.js": "13.0.3",
|
||||
"sonner": "^1.5.0",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"tailwindcss": "3.2.4",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.16.7"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "OHIF",
|
||||
"license": "MIT"
|
||||
}
|
||||
59
platform/ui-next/src/components/Accordion/Accordion.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AccordionItem.displayName = 'AccordionItem';
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-between py-2 px-2 text-base font-medium transition-transform duration-200',
|
||||
className,
|
||||
'[&[data-state=open]>svg]:rotate-270',
|
||||
'[&[data-state=closed]>svg]:rotate-90'
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-primary h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-base"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn(className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||
3
platform/ui-next/src/components/Accordion/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './Accordion';
|
||||
|
||||
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../Select';
|
||||
|
||||
const BackgroundColorSelect: React.FC = () => {
|
||||
const [selectedColor, setSelectedColor] = useState('#050615');
|
||||
|
||||
useEffect(() => {
|
||||
const rows = document.querySelectorAll('.row') as NodeListOf<HTMLElement>;
|
||||
rows.forEach(row => {
|
||||
row.style.backgroundColor = selectedColor;
|
||||
});
|
||||
}, [selectedColor]);
|
||||
|
||||
const handleColorChange = (value: string) => {
|
||||
setSelectedColor(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Select onValueChange={handleColorChange}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select Color" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="black">Viewport (Black)</SelectItem>
|
||||
<SelectItem value="#050615">Base</SelectItem>
|
||||
<SelectItem value="#090C29">Medium</SelectItem>
|
||||
<SelectItem value="#041C4A">Header</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackgroundColorSelect;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as BackgroundColorSelect } from './BackgroundColorSelect';
|
||||
54
platform/ui-next/src/components/Button/Button.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded text-base font-normal leading-tight transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary/60 text-primary-foreground hover:bg-primary/100',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-primary/25 bg-background hover:bg-primary/25 text-primary hover:text-primary',
|
||||
secondary: 'bg-primary/40 text-secondary-foreground hover:bg-primary/60',
|
||||
ghost: 'font-normal text-primary hover:bg-primary/25',
|
||||
link: 'font-normal text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-7 px-2 py-2',
|
||||
sm: 'h-6 rounded px-2',
|
||||
lg: 'h-9 rounded px-2',
|
||||
icon: 'h-6 w-6',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, forwardRef) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size }), className)}
|
||||
ref={forwardRef}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button, buttonVariants };
|
||||
1
platform/ui-next/src/components/Button/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { Button, buttonVariants } from './Button';
|
||||
61
platform/ui-next/src/components/Calendar/Calendar.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { DayPicker } from 'react-day-picker';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
import { buttonVariants } from '../Button';
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
||||
|
||||
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn('p-3', className)}
|
||||
captionLayout="dropdown"
|
||||
fromYear={1945}
|
||||
toYear={new Date().getFullYear()}
|
||||
labels={{
|
||||
labelMonthDropdown: () => undefined,
|
||||
labelYearDropdown: () => undefined,
|
||||
}}
|
||||
classNames={{
|
||||
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
|
||||
month: 'space-y-4',
|
||||
caption: 'flex justify-between items-center px-2',
|
||||
|
||||
caption_dropdowns: 'flex space-x-2 text-black',
|
||||
caption_label: 'hidden',
|
||||
nav: 'space-x-1 flex items-center',
|
||||
table: 'w-full border-collapse space-y-1',
|
||||
head_row: 'flex',
|
||||
head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
|
||||
row: 'flex w-full mt-2',
|
||||
cell: 'h-9 w-9 text-center text-base p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
|
||||
day: cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-9 w-9 p-0 font-normal aria-selected:opacity-100'
|
||||
),
|
||||
day_range_end: 'day-range-end',
|
||||
day_selected:
|
||||
'bg-primary/60 text-primary-foreground hover:bg-primary/80 hover:text-primary-foreground focus:bg-primary/80 focus:text-primary-foreground',
|
||||
day_today: 'bg-accent text-accent-foreground',
|
||||
day_outside:
|
||||
'day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30',
|
||||
day_disabled: 'text-muted-foreground opacity-50',
|
||||
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
|
||||
day_hidden: 'invisible',
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
|
||||
IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Calendar.displayName = 'Calendar';
|
||||
|
||||
export { Calendar };
|
||||
3
platform/ui-next/src/components/Calendar/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Calendar } from './Calendar';
|
||||
|
||||
export { Calendar};
|
||||
75
platform/ui-next/src/components/Card/Card.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-card text-card-foreground border-input rounded-lg border shadow',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
Card.displayName = 'Card';
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
CardHeader.displayName = 'CardHeader';
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn('font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
CardTitle.displayName = 'CardTitle';
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn('text-muted-foreground text-base', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardDescription.displayName = 'CardDescription';
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
CardContent.displayName = 'CardContent';
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex items-center p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
CardFooter.displayName = 'CardFooter';
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
||||
2
platform/ui-next/src/components/Card/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } from './Card';
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
||||
26
platform/ui-next/src/components/Checkbox/Checkbox.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import { CheckIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-primary hover:bg-primary/20 focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer h-4 w-4 shrink-0 rounded-sm border shadow focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator className={cn('text-background flex items-center justify-center')}>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||
|
||||
export { Checkbox };
|
||||
3
platform/ui-next/src/components/Checkbox/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
||||
export { Checkbox };
|
||||
55
platform/ui-next/src/components/Clipboard/Clipboard.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Button } from '../Button';
|
||||
import { Icons } from '../Icons';
|
||||
|
||||
interface ClipboardProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const Clipboard: React.FC<ClipboardProps> = ({ children }) => {
|
||||
const [copyState, setCopyState] = React.useState<'idle' | 'success' | 'error'>('idle');
|
||||
const copyText = React.useMemo(() => {
|
||||
if (typeof children === 'string') {
|
||||
return children.trim();
|
||||
}
|
||||
return '';
|
||||
}, [children]);
|
||||
|
||||
const handleCopy = React.useCallback(async () => {
|
||||
if (!copyText) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(copyText);
|
||||
setCopyState('success');
|
||||
} catch {
|
||||
setCopyState('error');
|
||||
} finally {
|
||||
setTimeout(() => setCopyState('idle'), 1500); // Reset state after feedback
|
||||
}
|
||||
}, [copyText]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
handleCopy();
|
||||
}}
|
||||
className="text-white"
|
||||
title="Copy"
|
||||
>
|
||||
{copyState === 'idle' && <Icons.Copy className="h-6 w-6" />}
|
||||
{copyState === 'success' && <Icons.FeedbackComplete className="h-6 w-6 text-white" />}
|
||||
{copyState === 'error' && (
|
||||
<Icons.ByName
|
||||
name="Error"
|
||||
className="h-6 w-6 text-white"
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export { Clipboard };
|
||||
3
platform/ui-next/src/components/Clipboard/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Clipboard } from './Clipboard';
|
||||
|
||||
export { Clipboard };
|
||||
66
platform/ui-next/src/components/Combobox/Combobox.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Button } from '../Button/Button';
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '../Command/Command';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover';
|
||||
|
||||
export function Combobox({ data = [], placeholder = 'Select item...' }) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [value, setValue] = React.useState('');
|
||||
|
||||
return (
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-[200px] justify-between"
|
||||
>
|
||||
{value ? data.find(item => item.value === value)?.label : placeholder}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder={`Search ${placeholder.toLowerCase()}...`} />
|
||||
<CommandEmpty>No {placeholder.toLowerCase()} found.</CommandEmpty>
|
||||
<CommandList>
|
||||
<CommandGroup>
|
||||
{data.map(item => (
|
||||
<CommandItem
|
||||
key={item.value}
|
||||
value={item.value}
|
||||
onSelect={currentValue => {
|
||||
setValue(currentValue === value ? '' : currentValue);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
value === item.value ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
{item.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
3
platform/ui-next/src/components/Combobox/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Combobox } from './Combobox';
|
||||
|
||||
export { Combobox};
|
||||
150
platform/ui-next/src/components/Command/Command.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import * as React from 'react';
|
||||
import { type DialogProps } from '@radix-ui/react-dialog';
|
||||
import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
|
||||
import { Command as CommandPrimitive } from 'cmdk';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Dialog, DialogContent } from '../Dialog/Dialog';
|
||||
|
||||
const Command = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Command.displayName = CommandPrimitive.displayName;
|
||||
|
||||
interface CommandDialogProps extends DialogProps {}
|
||||
|
||||
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogContent className="overflow-hidden p-0">
|
||||
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
{children}
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const CommandInput = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Input>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
className="flex items-center border-b px-3"
|
||||
cmdk-input-wrapper=""
|
||||
>
|
||||
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-base outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
|
||||
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
||||
|
||||
const CommandList = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.List
|
||||
ref={ref}
|
||||
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandList.displayName = CommandPrimitive.List.displayName;
|
||||
|
||||
const CommandEmpty = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Empty>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
|
||||
>((props, ref) => (
|
||||
<CommandPrimitive.Empty
|
||||
ref={ref}
|
||||
className="py-6 text-center text-base"
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
||||
|
||||
const CommandGroup = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Group>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Group
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-sm [&_[cmdk-group-heading]]:font-medium',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
||||
|
||||
const CommandSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('bg-border -mx-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
||||
|
||||
const CommandItem = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-base outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
||||
|
||||
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn('text-muted-foreground ml-auto text-sm tracking-widest', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
CommandShortcut.displayName = 'CommandShortcut';
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
};
|
||||
23
platform/ui-next/src/components/Command/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
} from './Command';
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
};
|
||||
187
platform/ui-next/src/components/ContextMenu/ContextMenu.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import * as React from 'react';
|
||||
import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from '@radix-ui/react-icons';
|
||||
|
||||
const ContextMenu = ContextMenuPrimitive.Root;
|
||||
|
||||
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
||||
|
||||
const ContextMenuGroup = ContextMenuPrimitive.Group;
|
||||
|
||||
const ContextMenuPortal = ContextMenuPrimitive.Portal;
|
||||
|
||||
const ContextMenuSub = ContextMenuPrimitive.Sub;
|
||||
|
||||
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
||||
|
||||
const ContextMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto h-4 w-4" />
|
||||
</ContextMenuPrimitive.SubTrigger>
|
||||
));
|
||||
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const ContextMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const ContextMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Portal>
|
||||
<ContextMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</ContextMenuPrimitive.Portal>
|
||||
));
|
||||
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
||||
|
||||
const ContextMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
||||
|
||||
const ContextMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const ContextMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<DotFilledIcon className="h-4 w-4 fill-current" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.RadioItem>
|
||||
));
|
||||
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const ContextMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('text-foreground px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
||||
|
||||
const ContextMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
||||
|
||||
const ContextMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
ContextMenuShortcut.displayName = 'ContextMenuShortcut';
|
||||
|
||||
export {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuGroup,
|
||||
ContextMenuPortal,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
};
|
||||
35
platform/ui-next/src/components/ContextMenu/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuGroup,
|
||||
ContextMenuPortal,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
} from './ContextMenu';
|
||||
|
||||
export {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuGroup,
|
||||
ContextMenuPortal,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
};
|
||||
338
platform/ui-next/src/components/DataRow/DataRow.tsx
Normal file
@@ -0,0 +1,338 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button } from '../../components/Button/Button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
} from '../../components/DropdownMenu';
|
||||
import { Icons } from '../../components/Icons/Icons';
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from '../../components/Tooltip/Tooltip';
|
||||
|
||||
/**
|
||||
* DataRow is a complex UI component that displays a selectable, interactive row with hierarchical data.
|
||||
* It's designed to show a numbered item with a title, optional color indicator, and expandable details.
|
||||
* The row supports various interactive features like visibility toggling, locking, and contextual actions.
|
||||
*
|
||||
* @component
|
||||
* @example
|
||||
* ```tsx
|
||||
* <DataRow
|
||||
* number={1}
|
||||
* title="My Item"
|
||||
* details={{
|
||||
* primary: ["Main detail", " Sub detail"],
|
||||
* secondary: []
|
||||
* }}
|
||||
* isVisible={true}
|
||||
* isLocked={false}
|
||||
* onToggleVisibility={() => {}}
|
||||
* onToggleLocked={() => {}}
|
||||
* onRename={() => {}}
|
||||
* onDelete={() => {}}
|
||||
* onColor={() => {}}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* Props for the DataRow component
|
||||
* @interface DataRowProps
|
||||
* @property {number} number - The display number/index of the row
|
||||
* @property {string} title - The main text label for the row
|
||||
* @property {boolean} disableEditing - When true, prevents rename and delete operations
|
||||
* @property {string} [colorHex] - Optional hex color code to display a color indicator
|
||||
* @property {Object} [details] - Optional hierarchical details to display below the row
|
||||
* @property {string[]} details.primary - Primary details shown immediately below the row
|
||||
* @property {string[]} details.secondary - Secondary details (currently unused)
|
||||
* @property {boolean} [isSelected] - Whether the row is currently selected
|
||||
* @property {() => void} [onSelect] - Callback when the row is clicked/selected
|
||||
* @property {boolean} isVisible - Controls the row's visibility state
|
||||
* @property {() => void} onToggleVisibility - Callback to toggle visibility
|
||||
* @property {boolean} isLocked - Controls the row's locked state
|
||||
* @property {() => void} onToggleLocked - Callback to toggle locked state
|
||||
* @property {() => void} onRename - Callback when rename is requested
|
||||
* @property {() => void} onDelete - Callback when delete is requested
|
||||
* @property {() => void} onColor - Callback when color change is requested
|
||||
*/
|
||||
interface DataRowProps {
|
||||
number: number;
|
||||
disableEditing: boolean;
|
||||
description: string;
|
||||
details?: { primary: string[]; secondary: string[] };
|
||||
//
|
||||
isSelected?: boolean;
|
||||
onSelect?: () => void;
|
||||
//
|
||||
isVisible: boolean;
|
||||
onToggleVisibility: () => void;
|
||||
//
|
||||
isLocked: boolean;
|
||||
onToggleLocked: () => void;
|
||||
//
|
||||
title: string;
|
||||
onRename: () => void;
|
||||
//
|
||||
onDelete: () => void;
|
||||
//
|
||||
colorHex?: string;
|
||||
onColor: () => void;
|
||||
}
|
||||
|
||||
const DataRow: React.FC<DataRowProps> = ({
|
||||
number,
|
||||
title,
|
||||
colorHex,
|
||||
details,
|
||||
onSelect,
|
||||
isLocked,
|
||||
onToggleVisibility,
|
||||
onToggleLocked,
|
||||
onRename,
|
||||
onDelete,
|
||||
onColor,
|
||||
isSelected = false,
|
||||
isVisible = true,
|
||||
disableEditing = false,
|
||||
}) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const isTitleLong = title?.length > 25;
|
||||
const rowRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSelected && rowRef.current) {
|
||||
rowRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}, [isSelected]);
|
||||
|
||||
const handleAction = (action: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
switch (action) {
|
||||
case 'Rename':
|
||||
onRename();
|
||||
break;
|
||||
case 'Lock':
|
||||
onToggleLocked();
|
||||
break;
|
||||
case 'Delete':
|
||||
onDelete();
|
||||
break;
|
||||
case 'Color':
|
||||
onColor();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const decodeHTML = (html: string) => {
|
||||
const txt = document.createElement('textarea');
|
||||
txt.innerHTML = html;
|
||||
return txt.value;
|
||||
};
|
||||
|
||||
const renderDetailText = (text: string, indent: number = 0) => {
|
||||
const indentation = ' '.repeat(indent);
|
||||
if (text === '') {
|
||||
return (
|
||||
<div
|
||||
key={`empty-${indent}`}
|
||||
className="h-2"
|
||||
></div>
|
||||
);
|
||||
}
|
||||
const cleanText = decodeHTML(text);
|
||||
return (
|
||||
<div
|
||||
key={cleanText}
|
||||
className="whitespace-pre-wrap"
|
||||
>
|
||||
{indentation}
|
||||
<span className="font-medium">{cleanText}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDetails = (details: string[]) => {
|
||||
const visibleLines = details.slice(0, 4);
|
||||
const hiddenLines = details.slice(4);
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="cursor-help">
|
||||
<div className="flex flex-col space-y-1">
|
||||
{visibleLines.map((line, lineIndex) =>
|
||||
renderDetailText(line, line.startsWith(' ') ? 1 : 0)
|
||||
)}
|
||||
</div>
|
||||
{hiddenLines.length > 0 && (
|
||||
<div className="text-muted-foreground mt-1 flex items-center text-sm">
|
||||
<span>...</span>
|
||||
<Icons.Info className="mr-1 h-5 w-5" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="right"
|
||||
align="start"
|
||||
className="max-w-md"
|
||||
>
|
||||
<div className="text-secondary-foreground flex flex-col space-y-1 text-sm leading-normal">
|
||||
{details.map((line, lineIndex) =>
|
||||
renderDetailText(line, line.startsWith(' ') ? 1 : 0)
|
||||
)}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rowRef}
|
||||
className={`flex flex-col ${isVisible ? '' : 'opacity-60'}`}>
|
||||
<div
|
||||
className={`flex items-center ${
|
||||
isSelected ? 'bg-popover' : 'bg-muted'
|
||||
} group relative cursor-pointer`}
|
||||
onClick={onSelect}
|
||||
data-cy="data-row"
|
||||
>
|
||||
{/* Hover Overlay */}
|
||||
<div className="bg-primary/20 pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100"></div>
|
||||
|
||||
{/* Number Box */}
|
||||
<div
|
||||
className={`flex h-7 max-h-7 w-7 flex-shrink-0 items-center justify-center rounded-l border-r border-black text-base ${
|
||||
isSelected ? 'bg-highlight text-black' : 'bg-muted text-muted-foreground'
|
||||
} overflow-hidden`}
|
||||
>
|
||||
{number}
|
||||
</div>
|
||||
|
||||
{/* Color Circle (Optional) */}
|
||||
{colorHex && (
|
||||
<div className="flex h-7 w-5 items-center justify-center">
|
||||
<span
|
||||
className="ml-2 h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: colorHex }}
|
||||
></span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Label with Conditional Tooltip */}
|
||||
<div className="ml-2 flex-1 overflow-hidden">
|
||||
{isTitleLong ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={`cursor-default text-base ${
|
||||
isSelected ? 'text-highlight' : 'text-muted-foreground'
|
||||
} [overflow:hidden] [display:-webkit-box] [-webkit-line-clamp:2] [-webkit-box-orient:vertical]`}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="top"
|
||||
align="center"
|
||||
>
|
||||
{title}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span
|
||||
className={`text-base ${
|
||||
isSelected ? 'text-highlight' : 'text-muted-foreground'
|
||||
} [overflow:hidden] [display:-webkit-box] [-webkit-line-clamp:2] [-webkit-box-orient:vertical]`}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Actions and Visibility Toggle */}
|
||||
<div className="relative ml-2 flex items-center space-x-1">
|
||||
{/* Visibility Toggle Icon */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={`h-6 w-6 transition-opacity ${
|
||||
isSelected || !isVisible ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
|
||||
}`}
|
||||
aria-label={isVisible ? 'Hide' : 'Show'}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleVisibility();
|
||||
}}
|
||||
>
|
||||
{isVisible ? <Icons.Hide className="h-6 w-6" /> : <Icons.Show className="h-6 w-6" />}
|
||||
</Button>
|
||||
|
||||
{/* Lock Icon (if needed) */}
|
||||
{isLocked && !disableEditing && <Icons.Lock className="text-muted-foreground h-6 w-6" />}
|
||||
|
||||
{/* Actions Dropdown Menu */}
|
||||
{disableEditing && <div className="h-6 w-6"></div>}
|
||||
{!disableEditing && (
|
||||
<DropdownMenu onOpenChange={open => setIsDropdownOpen(open)}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={`h-6 w-6 transition-opacity ${
|
||||
isSelected || isDropdownOpen
|
||||
? 'opacity-100'
|
||||
: 'opacity-0 group-hover:opacity-100'
|
||||
}`}
|
||||
aria-label="Actions"
|
||||
onClick={e => e.stopPropagation()} // Prevent row selection on button click
|
||||
>
|
||||
<Icons.More className="h-6 w-6" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<>
|
||||
<DropdownMenuItem onClick={e => handleAction('Rename', e)}>
|
||||
<Icons.Rename className="text-foreground" />
|
||||
<span className="pl-2">Rename</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={e => handleAction('Delete', e)}>
|
||||
<Icons.Delete className="text-foreground" />
|
||||
<span className="pl-2">Delete</span>
|
||||
</DropdownMenuItem>
|
||||
{onColor && (
|
||||
<DropdownMenuItem onClick={e => handleAction('Color', e)}>
|
||||
<Icons.ColorChange className="text-foreground" />
|
||||
<span className="pl-2">Change Color</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem onClick={e => handleAction('Lock', e)}>
|
||||
<Icons.Lock className="text-foreground" />
|
||||
<span className="pl-2">{isLocked ? 'Unlock' : 'Lock'}</span>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Details Section */}
|
||||
{details && (details.primary?.length > 0 || details.secondary?.length > 0) && (
|
||||
<div className="ml-7 px-2 py-2">
|
||||
<div className="text-secondary-foreground flex items-center gap-1 text-base leading-normal">
|
||||
{details.primary?.length > 0 && renderDetails(details.primary)}
|
||||
{details.secondary?.length > 0 && (
|
||||
<div className="text-muted-foreground ml-auto text-sm">
|
||||
{renderDetails(details.secondary)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataRow;
|
||||
3
platform/ui-next/src/components/DataRow/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import DataRow from './DataRow';
|
||||
|
||||
export { DataRow };
|
||||
31
platform/ui-next/src/components/DataRow/types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Represents a single data item in a list or table structure
|
||||
*
|
||||
* @interface DataItem
|
||||
*/
|
||||
export type DataItem = {
|
||||
/** Unique identifier for the data item */
|
||||
id: number;
|
||||
/** Primary text or name of the data item */
|
||||
title: string;
|
||||
/** Detailed text description of the data item */
|
||||
description: string;
|
||||
/** Additional optional field for extra information */
|
||||
optionalField?: string;
|
||||
/** Hex color code (e.g., '#FF0000') for visual representation */
|
||||
colorHex?: string;
|
||||
/** Additional details or metadata about the item */
|
||||
details?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a group of related data items with a common type
|
||||
*
|
||||
* @interface ListGroup
|
||||
*/
|
||||
export type ListGroup = {
|
||||
/** The type or category of the group */
|
||||
type: string;
|
||||
/** Array of DataItem objects belonging to this group */
|
||||
items: DataItem[];
|
||||
};
|
||||
153
platform/ui-next/src/components/DateRange/DateRange.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import * as React from 'react';
|
||||
import { format, parse, isValid } from 'date-fns';
|
||||
import { Calendar as CalendarIcon } from 'lucide-react';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Calendar } from '../Calendar';
|
||||
import * as Popover from '../Popover';
|
||||
|
||||
export type DatePickerWithRangeProps = {
|
||||
id: string;
|
||||
/** YYYYMMDD (19921022) */
|
||||
startDate: string;
|
||||
/** YYYYMMDD (19921022) */
|
||||
endDate: string;
|
||||
/** Callback that received { startDate: string(YYYYMMDD), endDate: string(YYYYMMDD)} */
|
||||
onChange: (value: { startDate: string; endDate: string }) => void;
|
||||
};
|
||||
|
||||
export function DatePickerWithRange({
|
||||
className,
|
||||
id,
|
||||
startDate,
|
||||
endDate,
|
||||
onChange,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement> & DatePickerWithRangeProps) {
|
||||
const [start, setStart] = React.useState<string>(
|
||||
startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : ''
|
||||
);
|
||||
const [end, setEnd] = React.useState<string>(
|
||||
endDate ? format(parse(endDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : ''
|
||||
);
|
||||
const [openEnd, setOpenEnd] = React.useState(false);
|
||||
|
||||
const handleStartSelect = (selectedDate: Date | undefined) => {
|
||||
if (selectedDate) {
|
||||
const formattedDate = format(selectedDate, 'yyyy-MM-dd');
|
||||
setStart(formattedDate);
|
||||
setOpenEnd(true);
|
||||
onChange({
|
||||
startDate: format(selectedDate, 'yyyyMMdd'),
|
||||
endDate: end.replace(/-/g, ''),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleEndSelect = (selectedDate: Date | undefined) => {
|
||||
if (selectedDate) {
|
||||
const formattedDate = format(selectedDate, 'yyyy-MM-dd');
|
||||
setEnd(formattedDate);
|
||||
setOpenEnd(false);
|
||||
onChange({
|
||||
startDate: start.replace(/-/g, ''),
|
||||
endDate: format(selectedDate, 'yyyyMMdd'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>, type: 'start' | 'end') => {
|
||||
const value = e.target.value;
|
||||
const date = parse(value, 'yyyy-MM-dd', new Date());
|
||||
if (type === 'start') {
|
||||
setStart(value);
|
||||
if (isValid(date)) {
|
||||
handleStartSelect(date);
|
||||
}
|
||||
} else {
|
||||
setEnd(value);
|
||||
if (isValid(date)) {
|
||||
handleEndSelect(date);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
setStart(startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : '');
|
||||
setEnd(endDate ? format(parse(endDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : '');
|
||||
}, [startDate, endDate]);
|
||||
|
||||
return (
|
||||
<div className={cn('flex gap-2', className)}>
|
||||
<Popover.Popover>
|
||||
<Popover.PopoverTrigger asChild>
|
||||
<div className="relative w-full">
|
||||
<CalendarIcon className="absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 transform text-white" />
|
||||
<input
|
||||
id={`${id}-start`}
|
||||
type="text"
|
||||
placeholder="Start date"
|
||||
autoComplete="off"
|
||||
value={start}
|
||||
onChange={e => handleInputChange(e, 'start')}
|
||||
className={cn(
|
||||
'border-inputfield-main focus:border-inputfield-focus h-[32px] w-full justify-start rounded border bg-black py-[6.5px] pl-[6.5px] pr-[6.5px] text-left text-base font-normal hover:bg-black hover:text-white',
|
||||
!start && 'text-muted-foreground'
|
||||
)}
|
||||
data-cy="input-date-range-start"
|
||||
/>
|
||||
</div>
|
||||
</Popover.PopoverTrigger>
|
||||
<Popover.PopoverContent
|
||||
className="w-auto p-0"
|
||||
align="start"
|
||||
>
|
||||
<Calendar
|
||||
initialFocus
|
||||
mode="single"
|
||||
defaultMonth={start ? parse(start, 'yyyy-MM-dd', new Date()) : new Date()}
|
||||
selected={start ? parse(start, 'yyyy-MM-dd', new Date()) : undefined}
|
||||
onSelect={handleStartSelect}
|
||||
numberOfMonths={1}
|
||||
/>
|
||||
</Popover.PopoverContent>
|
||||
</Popover.Popover>
|
||||
|
||||
<Popover.Popover
|
||||
open={openEnd}
|
||||
onOpenChange={setOpenEnd}
|
||||
>
|
||||
<Popover.PopoverTrigger asChild>
|
||||
<div className="relative w-full">
|
||||
<CalendarIcon className="absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 transform text-white" />
|
||||
<input
|
||||
id={`${id}-end`}
|
||||
type="text"
|
||||
placeholder="End date"
|
||||
autoComplete="off"
|
||||
value={end}
|
||||
onChange={e => handleInputChange(e, 'end')}
|
||||
className={cn(
|
||||
'border-inputfield-main focus:border-inputfield-focus h-full w-full justify-start rounded border bg-black py-[6.5px] pl-[6.5px] pr-[6.5px] text-left text-base font-normal hover:bg-black hover:text-white',
|
||||
!end && 'text-muted-foreground'
|
||||
)}
|
||||
data-cy="input-date-range-end"
|
||||
/>
|
||||
</div>
|
||||
</Popover.PopoverTrigger>
|
||||
<Popover.PopoverContent
|
||||
className="w-auto p-0"
|
||||
align="start"
|
||||
>
|
||||
<Calendar
|
||||
initialFocus
|
||||
mode="single"
|
||||
defaultMonth={start ? parse(start, 'yyyy-MM-dd', new Date()) : new Date()}
|
||||
selected={end ? parse(end, 'yyyy-MM-dd', new Date()) : undefined}
|
||||
onSelect={handleEndSelect}
|
||||
numberOfMonths={1}
|
||||
/>
|
||||
</Popover.PopoverContent>
|
||||
</Popover.Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
platform/ui-next/src/components/DateRange/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { DatePickerWithRange } from './DateRange';
|
||||
|
||||
export { DatePickerWithRange };
|
||||
105
platform/ui-next/src/components/Dialog/Dialog.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import * as React from 'react';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { Cross2Icon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = DialogPrimitive.Portal;
|
||||
|
||||
const DialogClose = DialogPrimitive.Close;
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogHeader.displayName = 'DialogHeader';
|
||||
|
||||
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogFooter.displayName = 'DialogFooter';
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn('text-xl font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn('text-muted-foreground text-base', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
};
|
||||
25
platform/ui-next/src/components/Dialog/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from './Dialog';
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icons } from '../Icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from '../Tooltip';
|
||||
|
||||
/**
|
||||
* Displays a tooltip with a list of messages of a displaySet
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const DisplaySetMessageListTooltip = ({ messages, id }): React.ReactNode => {
|
||||
const { t } = useTranslation('Messages');
|
||||
if (messages?.size()) {
|
||||
return (
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger id={id}>
|
||||
<Icons.StatusWarning
|
||||
className="h-[20px] w-[20px]"
|
||||
aria-hidden="true"
|
||||
role="presentation"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
<div className="max-w-68 text-left text-lg text-white">
|
||||
<div
|
||||
className="break-normal text-lg font-semibold text-blue-300"
|
||||
style={{
|
||||
marginLeft: '4px',
|
||||
marginTop: '4px',
|
||||
}}
|
||||
>
|
||||
{t('Display Set Messages')}
|
||||
</div>
|
||||
<ol
|
||||
style={{
|
||||
marginLeft: '4px',
|
||||
marginRight: '4px',
|
||||
}}
|
||||
>
|
||||
{messages.messages.map((message, index) => (
|
||||
<li
|
||||
style={{
|
||||
marginTop: '6px',
|
||||
marginBottom: '6px',
|
||||
}}
|
||||
key={index}
|
||||
>
|
||||
{index + 1}. {t(message.id)}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
};
|
||||
|
||||
DisplaySetMessageListTooltip.propTypes = {
|
||||
messages: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
export { DisplaySetMessageListTooltip };
|
||||
@@ -0,0 +1,3 @@
|
||||
import { DisplaySetMessageListTooltip } from './DisplaySetMessageListTooltip';
|
||||
|
||||
export { DisplaySetMessageListTooltip };
|
||||
136
platform/ui-next/src/components/DoubleSlider/DoubleSlider.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import React from 'react';
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Input } from '../Input';
|
||||
|
||||
interface DoubleSliderProps {
|
||||
className?: string;
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
defaultValue?: [number, number];
|
||||
onValueChange?: (value: [number, number]) => void;
|
||||
showNumberInputs?: boolean;
|
||||
}
|
||||
|
||||
const DoubleSlider = React.forwardRef<HTMLDivElement, DoubleSliderProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
min,
|
||||
max,
|
||||
onValueChange,
|
||||
step = 1,
|
||||
defaultValue = [min, max],
|
||||
showNumberInputs = false,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [value, setValue] = React.useState<[number, number]>(defaultValue);
|
||||
|
||||
const prevDefaultValueRef = React.useRef<[number, number] | null>(null);
|
||||
|
||||
const isInteger = step % 1 === 0;
|
||||
|
||||
React.useEffect(() => {
|
||||
// Only update if defaultValue has actually changed
|
||||
if (
|
||||
!prevDefaultValueRef.current ||
|
||||
prevDefaultValueRef.current[0] !== defaultValue[0] ||
|
||||
prevDefaultValueRef.current[1] !== defaultValue[1]
|
||||
) {
|
||||
setValue(defaultValue);
|
||||
prevDefaultValueRef.current = defaultValue;
|
||||
}
|
||||
}, [defaultValue]);
|
||||
|
||||
const roundToStep = (num: number): number => {
|
||||
const inverse = 1 / step;
|
||||
return Math.round(num * inverse) / inverse;
|
||||
};
|
||||
|
||||
const handleSliderChange = React.useCallback(
|
||||
(newValue: number[]) => {
|
||||
const clampedValue: [number, number] = [
|
||||
roundToStep(Math.max(min, Math.min(newValue[0], max))),
|
||||
roundToStep(Math.min(max, Math.max(newValue[1], min))),
|
||||
];
|
||||
setValue(clampedValue);
|
||||
onValueChange?.(clampedValue);
|
||||
},
|
||||
[min, max, onValueChange, step]
|
||||
);
|
||||
|
||||
const handleInputChange = React.useCallback(
|
||||
(index: 0 | 1, inputValue: string) => {
|
||||
const newValue = parseFloat(inputValue);
|
||||
if (!isNaN(newValue)) {
|
||||
const clampedValue: [number, number] = [...value];
|
||||
clampedValue[index] = roundToStep(Math.min(Math.max(newValue, min), max));
|
||||
if (index === 0 && clampedValue[0] > clampedValue[1]) {
|
||||
clampedValue[1] = clampedValue[0];
|
||||
} else if (index === 1 && clampedValue[1] < clampedValue[0]) {
|
||||
clampedValue[0] = clampedValue[1];
|
||||
}
|
||||
setValue(clampedValue);
|
||||
onValueChange?.(clampedValue);
|
||||
}
|
||||
},
|
||||
[value, min, max, onValueChange, step]
|
||||
);
|
||||
|
||||
const formatValue = (val: number) => {
|
||||
return isInteger ? Math.round(val) : val;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex w-full items-center space-x-2', className)}
|
||||
>
|
||||
{showNumberInputs && (
|
||||
<Input
|
||||
type="number"
|
||||
value={formatValue(value[0])}
|
||||
onChange={e => handleInputChange(0, e.target.value)}
|
||||
onBlur={() => handleInputChange(0, value[0].toString())}
|
||||
className="w-14"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
/>
|
||||
)}
|
||||
<SliderPrimitive.Root
|
||||
className="relative flex h-4 w-full touch-none select-none items-center"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={value}
|
||||
onValueChange={handleSliderChange}
|
||||
>
|
||||
<SliderPrimitive.Track className="bg-primary/30 relative h-1 w-full grow overflow-hidden rounded-full">
|
||||
<SliderPrimitive.Range className="bg-primary absolute h-full" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="border-background bg-primary focus-visible:ring-ring block h-4 w-4 rounded-full border-2 shadow transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50" />
|
||||
<SliderPrimitive.Thumb className="border-background bg-primary focus-visible:ring-ring block h-4 w-4 rounded-full border-2 shadow transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
{showNumberInputs && (
|
||||
<Input
|
||||
type="number"
|
||||
value={formatValue(value[1])}
|
||||
onChange={e => handleInputChange(1, e.target.value)}
|
||||
onBlur={() => handleInputChange(1, value[1].toString())}
|
||||
className="w-14"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
DoubleSlider.displayName = 'DoubleSlider';
|
||||
|
||||
export { DoubleSlider };
|
||||
3
platform/ui-next/src/components/DoubleSlider/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { DoubleSlider } from './DoubleSlider';
|
||||
|
||||
export { DoubleSlider };
|
||||
191
platform/ui-next/src/components/DropdownMenu/DropdownMenu.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import * as React from 'react';
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, disabled, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded px-2 py-1 text-base outline-none',
|
||||
inset && 'pl-8',
|
||||
disabled && 'pointer-events-none opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto h-4 w-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-input z-50 min-w-[8rem] overflow-hidden rounded border p-1 shadow-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground border-input z-50 min-w-[8rem] overflow-hidden rounded border p-1 shadow-md',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded px-1 py-1 text-base outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded py-1 pl-8 pr-2 text-base outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded py-1 pl-8 pr-2 text-base outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<DotFilledIcon className="h-4 w-4 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('text-muted-foreground px-2 py-1 text-sm', inset && 'pl-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('bg-muted my-1 mx-2 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn('ml-auto text-sm tracking-widest opacity-60', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
};
|
||||
35
platform/ui-next/src/components/DropdownMenu/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
} from './DropdownMenu';
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
};
|
||||
198
platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from '../Dialog/Dialog';
|
||||
import { ScrollArea } from '../ScrollArea/ScrollArea';
|
||||
import { Icons } from '../Icons';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
interface ErrorBoundaryError extends Error {
|
||||
message: string;
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
interface DefaultFallbackProps {
|
||||
error: ErrorBoundaryError;
|
||||
context: string;
|
||||
resetErrorBoundary: () => void;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
context?: string;
|
||||
onReset?: () => void;
|
||||
onError?: (error: ErrorBoundaryError, componentStack: string, context: string) => void;
|
||||
fallbackComponent?: React.ComponentType<DefaultFallbackProps>;
|
||||
children: React.ReactNode;
|
||||
fallbackRoute?: string | null;
|
||||
isPage?: boolean;
|
||||
}
|
||||
|
||||
const DefaultFallback = ({
|
||||
error,
|
||||
context,
|
||||
resetErrorBoundary = () => {},
|
||||
}: DefaultFallbackProps) => {
|
||||
const { t } = useTranslation('ErrorBoundary');
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const title = `${t('Something went wrong')}${!isProduction && ` ${t('in')} ${context}`}.`;
|
||||
const subtitle = t('Sorry, something went wrong there. Try again.');
|
||||
|
||||
const copyErrorDetails = () => {
|
||||
const errorDetails = `
|
||||
Context: ${context}
|
||||
Error Message: ${error.message}
|
||||
Stack: ${error.stack}
|
||||
`;
|
||||
navigator.clipboard.writeText(errorDetails);
|
||||
toast.success(t('Copied to clipboard'));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
toast.error(title, {
|
||||
description: subtitle,
|
||||
action: {
|
||||
label: t('Show Details'),
|
||||
onClick: () => setShowDetails(true),
|
||||
},
|
||||
duration: 0,
|
||||
});
|
||||
}, [error]);
|
||||
|
||||
if (isProduction) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={showDetails}
|
||||
onOpenChange={setShowDetails}
|
||||
>
|
||||
<DialogContent className="border-input h-[50vh] w-[90vw] border-2 sm:max-w-[900px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-muted-foreground flex justify-between text-xl">
|
||||
<div className="flex items-center">{title}</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
copyErrorDetails();
|
||||
setShowDetails(false);
|
||||
}}
|
||||
className="text-aqua-pale hover:text-aqua-pale/80 flex items-center gap-2 rounded bg-gray-800 px-4 py-2 font-light"
|
||||
>
|
||||
<Icons.Code className="h-4 w-4" />
|
||||
{t('Copy Details')}
|
||||
</button>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription className="text-lg">{subtitle}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<ScrollArea className="h-[100%]">
|
||||
<div className="space-y-4 pr-4 font-mono text-base">
|
||||
<div className="space-y-4">
|
||||
<p className="text-aqua-pale break-words text-lg">
|
||||
{t('Context')}: {context}
|
||||
</p>
|
||||
<p className="text-aqua-pale break-words text-lg">
|
||||
{t('Error Message')}: {error.message}
|
||||
</p>
|
||||
<pre className="text-aqua-pale whitespace-pre-wrap break-words rounded bg-gray-900 p-4">
|
||||
Stack: {error.stack}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ErrorBoundary = ({
|
||||
context = 'OHIF',
|
||||
onReset = () => {},
|
||||
onError = () => {},
|
||||
fallbackComponent: FallbackComponent = DefaultFallback,
|
||||
children,
|
||||
fallbackRoute = null,
|
||||
isPage,
|
||||
}: ErrorBoundaryProps) => {
|
||||
const [error, setError] = useState<ErrorBoundaryError | null>(null);
|
||||
|
||||
const onResetHandler = () => {
|
||||
setError(null);
|
||||
onReset();
|
||||
};
|
||||
|
||||
// Add error event listener to window
|
||||
useEffect(() => {
|
||||
let errorTimeout: NodeJS.Timeout;
|
||||
|
||||
const handleError = (event: ErrorEvent) => {
|
||||
event.preventDefault();
|
||||
clearTimeout(errorTimeout);
|
||||
errorTimeout = setTimeout(() => {
|
||||
setError(event.error);
|
||||
onErrorHandler(event.error, null);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleRejection = (event: PromiseRejectionEvent) => {
|
||||
event.preventDefault();
|
||||
clearTimeout(errorTimeout);
|
||||
errorTimeout = setTimeout(() => {
|
||||
setError(event.reason);
|
||||
onErrorHandler(event.reason, null);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
window.addEventListener('error', handleError);
|
||||
window.addEventListener('unhandledrejection', handleRejection);
|
||||
|
||||
return () => {
|
||||
clearTimeout(errorTimeout);
|
||||
window.removeEventListener('error', handleError);
|
||||
window.removeEventListener('unhandledrejection', handleRejection);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onErrorHandler = (error: ErrorBoundaryError, componentStack: string) => {
|
||||
console.debug(`${context} Error Boundary`, error, componentStack, context);
|
||||
onError(error, componentStack, context);
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactErrorBoundary
|
||||
fallbackRender={props => (
|
||||
<FallbackComponent
|
||||
error={props.error}
|
||||
context={context}
|
||||
resetErrorBoundary={props.resetErrorBoundary}
|
||||
/>
|
||||
)}
|
||||
onReset={onResetHandler}
|
||||
onError={onErrorHandler}
|
||||
>
|
||||
<>
|
||||
{children}
|
||||
{error && (
|
||||
<FallbackComponent
|
||||
error={error}
|
||||
context={context}
|
||||
resetErrorBoundary={() => setError(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</ReactErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export { ErrorBoundary };
|
||||
3
platform/ui-next/src/components/Errorboundary/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
|
||||
export { ErrorBoundary };
|
||||
121
platform/ui-next/src/components/Header/Header.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
Icons,
|
||||
Button,
|
||||
} from '../';
|
||||
|
||||
import NavBar from '../NavBar';
|
||||
|
||||
// Todo: we should move this component to composition and remove props base
|
||||
|
||||
interface HeaderProps {
|
||||
children?: ReactNode;
|
||||
menuOptions: Array<{
|
||||
title: string;
|
||||
icon?: string;
|
||||
onClick: () => void;
|
||||
}>;
|
||||
isReturnEnabled?: boolean;
|
||||
onClickReturnButton?: () => void;
|
||||
isSticky?: boolean;
|
||||
WhiteLabeling?: {
|
||||
createLogoComponentFn?: (React: any, props: any) => ReactNode;
|
||||
};
|
||||
PatientInfo?: ReactNode;
|
||||
Secondary?: ReactNode;
|
||||
}
|
||||
|
||||
function Header({
|
||||
children,
|
||||
menuOptions,
|
||||
isReturnEnabled = true,
|
||||
onClickReturnButton,
|
||||
isSticky = false,
|
||||
WhiteLabeling,
|
||||
PatientInfo,
|
||||
Secondary,
|
||||
...props
|
||||
}: HeaderProps): ReactNode {
|
||||
const { t } = useTranslation('Header');
|
||||
|
||||
const onClickReturn = () => {
|
||||
if (isReturnEnabled && onClickReturnButton) {
|
||||
onClickReturnButton();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NavBar
|
||||
isSticky={isSticky}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative h-[48px] items-center">
|
||||
<div className="absolute left-0 top-1/2 flex -translate-y-1/2 items-center">
|
||||
<div
|
||||
className={classNames(
|
||||
'mr-3 inline-flex items-center',
|
||||
isReturnEnabled && 'cursor-pointer'
|
||||
)}
|
||||
onClick={onClickReturn}
|
||||
data-cy="return-to-work-list"
|
||||
>
|
||||
{isReturnEnabled && <Icons.ArrowLeft className="text-primary-active w-8" />}
|
||||
<div className="ml-1">
|
||||
{WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-1/2 left-[250px] h-8 -translate-y-1/2">{Secondary}</div>
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transform">
|
||||
<div className="flex items-center justify-center space-x-2">{children}</div>
|
||||
</div>
|
||||
<div className="absolute right-0 top-1/2 flex -translate-y-1/2 select-none items-center">
|
||||
{PatientInfo}
|
||||
<div className="border-primary-dark mx-1.5 h-[25px] border-r"></div>
|
||||
<div className="flex-shrink-0">
|
||||
{/* <DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-primary-active hover:bg-primary-dark mt-2 h-full w-full"
|
||||
>
|
||||
<Icons.GearSettings />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{menuOptions.map((option, index) => {
|
||||
const IconComponent = option.icon
|
||||
? Icons[option.icon as keyof typeof Icons]
|
||||
: null;
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onSelect={option.onClick}
|
||||
className="flex items-center gap-2 py-2"
|
||||
>
|
||||
{IconComponent && (
|
||||
<span className="flex h-4 w-4 items-center justify-center">
|
||||
<Icons.ByName name={IconComponent.name} />
|
||||
</span>
|
||||
)}
|
||||
<span className="flex-1">{option.title}</span>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NavBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
2
platform/ui-next/src/components/Header/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import Header from './Header';
|
||||
export { Header };
|
||||
741
platform/ui-next/src/components/Icons/Icons.tsx
Normal file
@@ -0,0 +1,741 @@
|
||||
import React from 'react';
|
||||
import Actions from './Sources/Actions';
|
||||
import Add from './Sources/Add';
|
||||
import Cancel from './Sources/Cancel';
|
||||
import ChevronClosed from './Sources/ChevronClosed';
|
||||
import ChevronOpen from './Sources/ChevronOpen';
|
||||
import Code from './Sources/Code';
|
||||
import ColorChange from './Sources/ColorChange';
|
||||
import Controls from './Sources/Controls';
|
||||
import Copy from './Sources/Copy';
|
||||
import Delete from './Sources/Delete';
|
||||
import DicomTagBrowser from './Sources/DicomTagBrowser';
|
||||
import DisplayFillAndOutline from './Sources/DisplayFillAndOutline';
|
||||
import DisplayFillOnly from './Sources/DisplayFillOnly';
|
||||
import DisplayOutlineOnly from './Sources/DisplayOutlineOnly';
|
||||
import Download from './Sources/Download';
|
||||
import Export from './Sources/Export';
|
||||
import EyeHidden from './Sources/EyeHidden';
|
||||
import EyeVisible from './Sources/EyeVisible';
|
||||
import FeedbackComplete from './Sources/FeedbackComplete';
|
||||
import GearSettings from './Sources/GearSettings';
|
||||
import Hide from './Sources/Hide';
|
||||
import IconMPR from './Sources/IconMPR';
|
||||
import Info from './Sources/Info';
|
||||
import InfoLink from './Sources/InfoLink';
|
||||
import InfoSeries from './Sources/InfoSeries';
|
||||
import ListView from './Sources/ListView';
|
||||
import LoadingSpinner from './Sources/LoadingSpinner';
|
||||
import Lock from './Sources/Lock';
|
||||
import Minus from './Sources/Minus';
|
||||
import MissingIcon from './Sources/MissingIcon';
|
||||
import More from './Sources/More';
|
||||
import MultiplePatients from './Sources/MultiplePatients';
|
||||
import NavigationPanelReveal from './Sources/NavigationPanelReveal';
|
||||
import OHIFLogo from './Sources/OHIFLogo';
|
||||
import Patient from './Sources/Patient';
|
||||
import Pin from './Sources/Pin';
|
||||
import PinFill from './Sources/PinFill';
|
||||
import Plus from './Sources/Plus';
|
||||
import PowerOff from './Sources/PowerOff';
|
||||
import Refresh from './Sources/Refresh';
|
||||
import Rename from './Sources/Rename';
|
||||
import Series from './Sources/Series';
|
||||
import Settings from './Sources/Settings';
|
||||
import Show from './Sources/Show';
|
||||
import SidePanelCloseLeft from './Sources/SidePanelCloseLeft';
|
||||
import SidePanelCloseRight from './Sources/SidePanelCloseRight';
|
||||
import SortingAscending from './Sources/SortingAscending';
|
||||
import SortingDescending from './Sources/SortingDescending';
|
||||
import StatusError from './Sources/StatusError';
|
||||
import StatusSuccess from './Sources/StatusSuccess';
|
||||
import StatusTracking from './Sources/StatusTracking';
|
||||
import StatusUntracked from './Sources/StatusUntracked';
|
||||
import StatusWarning from './Sources/StatusWarning';
|
||||
import Tab4D from './Sources/Tab4D';
|
||||
import TabLinear from './Sources/TabLinear';
|
||||
import TabPatientInfo from './Sources/TabPatientInfo';
|
||||
import TabRoiThreshold from './Sources/TabRoiThreshold';
|
||||
import TabSegmentation from './Sources/TabSegmentation';
|
||||
import TabStudies from './Sources/TabStudies';
|
||||
import ThumbnailView from './Sources/ThumbnailView';
|
||||
import Trash from './Sources/Trash';
|
||||
import ViewportViews from './Sources/ViewportViews';
|
||||
import Sorting from './Sources/Sorting';
|
||||
import Upload from './Sources/Upload';
|
||||
import LaunchArrow from './Sources/LaunchArrow';
|
||||
import LaunchInfo from './Sources/LaunchInfo';
|
||||
import GroupLayers from './Sources/GroupLayers';
|
||||
import Database from './Sources/Database';
|
||||
import InvestigationalUse from './Sources/InvestigationalUse';
|
||||
import IconTransferring from './Sources/IconTransferring';
|
||||
import Alert from './Sources/Alert';
|
||||
import AlertOutline from './Sources/AlertOutline';
|
||||
import Clipboard from './Sources/Clipboard';
|
||||
import {
|
||||
Tool3DRotate,
|
||||
ToolAngle,
|
||||
ToolAnnotate,
|
||||
ToolBidirectional,
|
||||
ToolCalibrate,
|
||||
ToolCapture,
|
||||
ToolCine,
|
||||
ToolCircle,
|
||||
ToolCobbAngle,
|
||||
ToolCreateThreshold,
|
||||
ToolCrosshair,
|
||||
ToolDicomTagBrowser,
|
||||
ToolFlipHorizontal,
|
||||
ToolFreehandPolygon,
|
||||
ToolFreehandRoi,
|
||||
ToolFreehand,
|
||||
ToolFusionColor,
|
||||
ToolInvert,
|
||||
ToolLayoutDefault,
|
||||
ToolLength,
|
||||
ToolMagneticRoi,
|
||||
ToolMagnify,
|
||||
ToolMeasureEllipse,
|
||||
ToolMoreMenu,
|
||||
ToolMove,
|
||||
ToolPolygon,
|
||||
ToolQuickMagnify,
|
||||
ToolRectangle,
|
||||
ToolReferenceLines,
|
||||
ToolReset,
|
||||
ToolRotateRight,
|
||||
ToolSegBrush,
|
||||
ToolSegEraser,
|
||||
ToolSegShape,
|
||||
ToolSegThreshold,
|
||||
ToolSplineRoi,
|
||||
ToolStackImageSync,
|
||||
ToolStackScroll,
|
||||
ToolToggleDicomOverlay,
|
||||
ToolUltrasoundBidirectional,
|
||||
ToolWindowLevel,
|
||||
ToolWindowRegion,
|
||||
ToolZoom,
|
||||
ToolLayout,
|
||||
ToolProbe,
|
||||
ToolEraser,
|
||||
ToolBrush,
|
||||
ToolThreshold,
|
||||
ToolShape,
|
||||
ToolLabelmapAssist,
|
||||
ToolPETSegment,
|
||||
ToolInterpolation,
|
||||
ToolBidirectionalSegment,
|
||||
ToolSegmentAnything,
|
||||
ToolContract,
|
||||
ToolExpand,
|
||||
} from './Sources/Tools';
|
||||
import ActionNewDialog from './Sources/ActionNewDialog';
|
||||
import NotificationInfo from './Sources/NotificationInfo';
|
||||
import StatusLocked from './Sources/StatusLocked';
|
||||
import ContentPrev from './Sources/ContentPrev';
|
||||
import ContentNext from './Sources/ContentNext';
|
||||
import CheckBoxChecked from './Sources/CheckBoxChecked';
|
||||
import CheckBoxUnchecked from './Sources/CheckBoxUnChecked';
|
||||
import Close from './Sources/Close';
|
||||
import Pause from './Sources/Pause';
|
||||
import Play from './Sources/Play';
|
||||
import ViewportWindowLevel from './Sources/ViewportWindowLevel';
|
||||
import Search from './Sources/Search';
|
||||
import Clear from './Sources/Clear';
|
||||
import {
|
||||
LayoutAdvanced3DOnly,
|
||||
LayoutAdvanced3DPrimary,
|
||||
LayoutAdvancedAxialPrimary,
|
||||
LayoutAdvancedMPR,
|
||||
LayoutCommon2x2,
|
||||
LayoutCommon1x1,
|
||||
LayoutCommon1x2,
|
||||
LayoutCommon2x3,
|
||||
LayoutAdvanced3DFourUp,
|
||||
LayoutAdvanced3DMain,
|
||||
} from './Sources/Layout';
|
||||
import Link from './Sources/Link';
|
||||
import IconColorLUT from './Sources/IconColorLUT';
|
||||
import CTAAA from '../../../assets/images/CT-AAA.png';
|
||||
import CTAAA2 from '../../../assets/images/CT-AAA2.png';
|
||||
import CTAir from '../../../assets/images/CT-Air.png';
|
||||
import CTBone from '../../../assets/images/CT-Bone.png';
|
||||
import CTBones from '../../../assets/images/CT-Bones.png';
|
||||
import CTCardiac from '../../../assets/images/CT-Cardiac.png';
|
||||
import CTCardiac2 from '../../../assets/images/CT-Cardiac2.png';
|
||||
import CTCardiac3 from '../../../assets/images/CT-Cardiac3.png';
|
||||
import CTChestContrastEnhanced from '../../../assets/images/CT-Chest-Contrast-Enhanced.png';
|
||||
import CTChestVessels from '../../../assets/images/CT-Chest-Vessels.png';
|
||||
import CTCoronaryArteries from '../../../assets/images/CT-Coronary-Arteries.png';
|
||||
import CTCoronaryArteries2 from '../../../assets/images/CT-Coronary-Arteries-2.png';
|
||||
import CTCoronaryArteries3 from '../../../assets/images/CT-Coronary-Arteries-3.png';
|
||||
import CTCroppedVolumeBone from '../../../assets/images/CT-Cropped-Volume-Bone.png';
|
||||
import CTFat from '../../../assets/images/CT-Fat.png';
|
||||
import CTLiverVasculature from '../../../assets/images/CT-Liver-Vasculature.png';
|
||||
import CTLung from '../../../assets/images/CT-Lung.png';
|
||||
import CTMIP from '../../../assets/images/CT-MIP.png';
|
||||
import CTMuscle from '../../../assets/images/CT-Muscle.png';
|
||||
import CTPulmonaryArteries from '../../../assets/images/CT-Pulmonary-Arteries.png';
|
||||
import CTSoftTissue from '../../../assets/images/CT-Soft-Tissue.png';
|
||||
import DTIFABrain from '../../../assets/images/DTI-FA-Brain.png';
|
||||
import MRAngio from '../../../assets/images/MR-Angio.png';
|
||||
import MRDefault from '../../../assets/images/MR-Default.png';
|
||||
import MRMIP from '../../../assets/images/MR-MIP.png';
|
||||
import MRT2Brain from '../../../assets/images/MR-T2-Brain.png';
|
||||
import VolumeRendering from '../../../assets/images/VolumeRendering.png';
|
||||
import ExternalLink from './Sources/ExternalLink';
|
||||
import OHIFLogoColorDarkBackground from './Sources/OHIFLogoColorDarkBackground';
|
||||
import Magnifier from './Sources/Magnifier';
|
||||
import LoadingOHIFMark from './Sources/LoadingOHIFMark';
|
||||
import ArrowLeftBold from './Sources/ArrowLeftBold';
|
||||
import Pencil from './Sources/Pencil';
|
||||
import NotificationWarning from './Sources/NotificationWarning';
|
||||
import ArrowRight from './Sources/ArrowRight';
|
||||
import ChevronLeft from './Sources/ChevronLeft';
|
||||
//
|
||||
//
|
||||
type IconProps = React.HTMLAttributes<SVGElement>;
|
||||
type ImageIconProps = React.ImgHTMLAttributes<HTMLImageElement>;
|
||||
|
||||
const ImageWrapper = ({ src, ...props }: { src: string } & ImageIconProps) => {
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
{...props}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Icons = {
|
||||
'CT-AAA': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTAAA}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-AAA2': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTAAA2}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Air': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTAir}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Bone': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTBone}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Bones': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTBones}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Cardiac': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCardiac}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Cardiac2': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCardiac2}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Cardiac3': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCardiac3}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Chest-Contrast-Enhanced': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTChestContrastEnhanced}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Chest-Vessels': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTChestVessels}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Coronary-Arteries': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCoronaryArteries}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Coronary-Arteries-2': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCoronaryArteries2}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Coronary-Arteries-3': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCoronaryArteries3}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Cropped-Volume-Bone': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTCroppedVolumeBone}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Fat': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTFat}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Liver-Vasculature': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTLiverVasculature}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Lung': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTLung}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-MIP': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTMIP}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Muscle': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTMuscle}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Pulmonary-Arteries': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTPulmonaryArteries}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'CT-Soft-Tissue': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={CTSoftTissue}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'DTI-FA-Brain': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={DTIFABrain}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'MR-Angio': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={MRAngio}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'MR-Default': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={MRDefault}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'MR-MIP': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={MRMIP}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
'MR-T2-Brain': (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={MRT2Brain}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
VolumeRendering: (props: ImageIconProps) => (
|
||||
<ImageWrapper
|
||||
src={VolumeRendering}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
// Icons
|
||||
Clipboard,
|
||||
ActionNewDialog,
|
||||
GroupLayers,
|
||||
Database,
|
||||
InvestigationalUse,
|
||||
Tool3DRotate,
|
||||
ToolAngle,
|
||||
ToolAnnotate,
|
||||
ToolBidirectional,
|
||||
ToolCalibrate,
|
||||
ToolCapture,
|
||||
ToolCine,
|
||||
ToolCircle,
|
||||
ToolCobbAngle,
|
||||
ToolCreateThreshold,
|
||||
ToolCrosshair,
|
||||
ToolDicomTagBrowser,
|
||||
ToolFlipHorizontal,
|
||||
ToolFreehandPolygon,
|
||||
ToolFreehandRoi,
|
||||
ToolFreehand,
|
||||
ToolFusionColor,
|
||||
ToolInvert,
|
||||
ToolLayoutDefault,
|
||||
ToolLength,
|
||||
ToolMagneticRoi,
|
||||
ToolMagnify,
|
||||
ToolMeasureEllipse,
|
||||
ToolMoreMenu,
|
||||
ToolMove,
|
||||
ToolPolygon,
|
||||
ToolQuickMagnify,
|
||||
ToolRectangle,
|
||||
ToolReferenceLines,
|
||||
ToolReset,
|
||||
ToolRotateRight,
|
||||
ToolSegBrush,
|
||||
ToolSegEraser,
|
||||
ToolSegShape,
|
||||
ToolSegThreshold,
|
||||
ToolSplineRoi,
|
||||
ToolStackImageSync,
|
||||
ToolStackScroll,
|
||||
ToolToggleDicomOverlay,
|
||||
ToolUltrasoundBidirectional,
|
||||
ToolWindowLevel,
|
||||
ToolWindowRegion,
|
||||
ToolZoom,
|
||||
LaunchArrow,
|
||||
LaunchInfo,
|
||||
Upload,
|
||||
Actions,
|
||||
Add,
|
||||
Cancel,
|
||||
Code,
|
||||
ColorChange,
|
||||
Controls,
|
||||
Copy,
|
||||
Delete,
|
||||
DicomTagBrowser,
|
||||
DisplayFillAndOutline,
|
||||
DisplayFillOnly,
|
||||
DisplayOutlineOnly,
|
||||
FillAndOutline: DisplayFillAndOutline,
|
||||
FillOnly: DisplayFillOnly,
|
||||
OutlineOnly: DisplayOutlineOnly,
|
||||
Download,
|
||||
Export,
|
||||
EyeHidden,
|
||||
EyeVisible,
|
||||
FeedbackComplete,
|
||||
GearSettings,
|
||||
Hide,
|
||||
IconMPR,
|
||||
Info,
|
||||
InfoLink,
|
||||
InfoSeries,
|
||||
ListView,
|
||||
LoadingSpinner,
|
||||
Lock,
|
||||
Minus,
|
||||
MissingIcon,
|
||||
More,
|
||||
MultiplePatients,
|
||||
NavigationPanelReveal,
|
||||
OHIFLogo,
|
||||
Patient,
|
||||
Pin,
|
||||
PinFill,
|
||||
Plus,
|
||||
PowerOff,
|
||||
Refresh,
|
||||
Rename,
|
||||
Series,
|
||||
Settings,
|
||||
Show,
|
||||
SidePanelCloseLeft,
|
||||
SidePanelCloseRight,
|
||||
SortingAscending,
|
||||
SortingDescending,
|
||||
Sorting,
|
||||
StatusError,
|
||||
StatusSuccess,
|
||||
StatusTracking,
|
||||
StatusWarning,
|
||||
StatusUntracked,
|
||||
Tab4D,
|
||||
TabLinear,
|
||||
TabPatientInfo,
|
||||
TabRoiThreshold,
|
||||
TabSegmentation,
|
||||
TabStudies,
|
||||
ThumbnailView,
|
||||
Trash,
|
||||
ViewportViews,
|
||||
ChevronClosed,
|
||||
ChevronOpen,
|
||||
ChevronRight: (props: IconProps) => {
|
||||
return (
|
||||
<ChevronLeft
|
||||
{...props}
|
||||
className={`${props.className || ''} rotate-180`.trim()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
ChevronLeft,
|
||||
ChevronDown: (props: IconProps) => {
|
||||
return (
|
||||
<ChevronLeft
|
||||
{...props}
|
||||
className={`${props.className || ''} -rotate-90`.trim()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
Alert,
|
||||
AlertOutline,
|
||||
NotificationInfo,
|
||||
StatusLocked,
|
||||
ContentPrev,
|
||||
ContentNext,
|
||||
CheckBoxChecked,
|
||||
CheckBoxUnchecked,
|
||||
Close,
|
||||
Pause,
|
||||
Play,
|
||||
Link,
|
||||
LoadingOHIFMark,
|
||||
ArrowLeft: ChevronClosed,
|
||||
ArrowRight,
|
||||
ArrowLeftBold,
|
||||
ArrowRightBold: (props: IconProps) => {
|
||||
return (
|
||||
<ArrowLeftBold
|
||||
{...props}
|
||||
className={`${props.className || ''} rotate-180`.trim()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
ArrowDown: (props: IconProps) => {
|
||||
return (
|
||||
<ChevronOpen
|
||||
{...props}
|
||||
className={`${props.className || ''} -rotate-90`.trim()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
ViewportWindowLevel,
|
||||
Search,
|
||||
Clear,
|
||||
LayoutCommon2x3,
|
||||
LayoutCommon2x2,
|
||||
LayoutCommon1x1,
|
||||
LayoutCommon1x2,
|
||||
LayoutAdvanced3DFourUp,
|
||||
LayoutAdvanced3DMain,
|
||||
LayoutAdvanced3DOnly,
|
||||
LayoutAdvanced3DPrimary,
|
||||
LayoutAdvancedAxialPrimary,
|
||||
LayoutAdvancedMPR,
|
||||
ToolLayout,
|
||||
IconColorLUT,
|
||||
ToolEraser,
|
||||
ToolBrush,
|
||||
ToolThreshold,
|
||||
ToolShape,
|
||||
ToolLabelmapAssist,
|
||||
ToolSegmentAnything,
|
||||
ToolPETSegment,
|
||||
ToolInterpolation,
|
||||
ToolBidirectionalSegment,
|
||||
ToolContract,
|
||||
ToolExpand,
|
||||
ExternalLink,
|
||||
OHIFLogoColorDarkBackground,
|
||||
Magnifier,
|
||||
Pencil,
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// Aliases
|
||||
'prev-arrow': (props: IconProps) => Icons.ArrowLeftBold(props),
|
||||
'next-arrow': (props: IconProps) => Icons.ArrowRightBold(props),
|
||||
'loading-ohif-mark': (props: IconProps) => LoadingOHIFMark(props),
|
||||
magnifier: (props: IconProps) => Magnifier(props),
|
||||
'status-alert-warning': (props: IconProps) => StatusWarning(props),
|
||||
'logo-dark-background': (props: IconProps) => OHIFLogoColorDarkBackground(props),
|
||||
'external-link': (props: IconProps) => ExternalLink(props),
|
||||
'checkbox-checked': (props: IconProps) => CheckBoxChecked(props),
|
||||
'checkbox-unchecked': (props: IconProps) => CheckBoxUnchecked(props),
|
||||
'checkbox-default': (props: IconProps) => CheckBoxUnchecked(props),
|
||||
'checkbox-active': (props: IconProps) => CheckBoxChecked(props),
|
||||
'icon-tool-eraser': (props: IconProps) => ToolEraser(props),
|
||||
'icon-tool-brush': (props: IconProps) => ToolBrush(props),
|
||||
'icon-tool-labelmap-assist': (props: IconProps) => ToolLabelmapAssist(props),
|
||||
'icon-tool-segment-anything': (props: IconProps) => ToolSegmentAnything(props),
|
||||
'icon-tool-threshold': (props: IconProps) => ToolThreshold(props),
|
||||
'icon-tool-pet-segment': (props: IconProps) => ToolPETSegment(props),
|
||||
'icon-tool-interpolation': (props: IconProps) => ToolInterpolation(props),
|
||||
'icon-tool-bidirectional-segment': (props: IconProps) => ToolBidirectionalSegment(props),
|
||||
'icon-tool-expand': (props: IconProps) => ToolExpand(props),
|
||||
'icon-tool-contract': (props: IconProps) => ToolContract(props),
|
||||
'icon-tool-shape': (props: IconProps) => ToolShape(props),
|
||||
link: (props: IconProps) => Link(props),
|
||||
'icon-color-lut': (props: IconProps) => IconColorLUT(props),
|
||||
'icon-link': (props: IconProps) => Link(props),
|
||||
'icon-clear': (props: IconProps) => Clear(props),
|
||||
'icon-search': (props: IconProps) => Search(props),
|
||||
'viewport-window-level': (props: IconProps) => ViewportWindowLevel(props),
|
||||
'action-new-dialog': (props: IconProps) => ActionNewDialog(props),
|
||||
'arrow-left': (props: IconProps) => Icons.ArrowLeft(props),
|
||||
'arrow-right': (props: IconProps) => Icons.ArrowRight(props),
|
||||
'arrow-down': (props: IconProps) => Icons.ArrowDown(props),
|
||||
'status-tracked': (props: IconProps) => StatusTracking(props),
|
||||
'status-untracked': (props: IconProps) => StatusUntracked(props),
|
||||
'status-locked': (props: IconProps) => StatusLocked(props),
|
||||
'tab-segmentation': (props: IconProps) => TabSegmentation(props),
|
||||
'tab-studies': (props: IconProps) => TabStudies(props),
|
||||
'tab-linear': (props: IconProps) => TabLinear(props),
|
||||
'tab-4d': (props: IconProps) => Tab4D(props),
|
||||
'tab-patient-info': (props: IconProps) => TabPatientInfo(props),
|
||||
'tab-roi-threshold': (props: IconProps) => TabRoiThreshold(props),
|
||||
'icon-mpr': (props: IconProps) => IconMPR(props),
|
||||
'power-off': (props: IconProps) => PowerOff(props),
|
||||
'icon-multiple-patients': (props: IconProps) => MultiplePatients(props),
|
||||
'icon-patient': (props: IconProps) => Patient(props),
|
||||
'chevron-down': (props: IconProps) => ChevronOpen(props),
|
||||
'tool-length': (props: IconProps) => ToolLength(props),
|
||||
'tool-3d-rotate': (props: IconProps) => Tool3DRotate(props),
|
||||
'tool-angle': (props: IconProps) => ToolAngle(props),
|
||||
'tool-annotate': (props: IconProps) => ToolAnnotate(props),
|
||||
'tool-bidirectional': (props: IconProps) => ToolBidirectional(props),
|
||||
'tool-calibration': (props: IconProps) => ToolCalibrate(props),
|
||||
'tool-capture': (props: IconProps) => ToolCapture(props),
|
||||
'tool-cine': (props: IconProps) => ToolCine(props),
|
||||
'tool-circle': (props: IconProps) => ToolCircle(props),
|
||||
'tool-cobb-angle': (props: IconProps) => ToolCobbAngle(props),
|
||||
'tool-create-threshold': (props: IconProps) => ToolCreateThreshold(props),
|
||||
'tool-crosshair': (props: IconProps) => ToolCrosshair(props),
|
||||
'dicom-tag-browser': (props: IconProps) => ToolDicomTagBrowser(props),
|
||||
'tool-flip-horizontal': (props: IconProps) => ToolFlipHorizontal(props),
|
||||
'tool-freehand-polygon': (props: IconProps) => ToolFreehandPolygon(props),
|
||||
'tool-freehand-roi': (props: IconProps) => ToolFreehandRoi(props),
|
||||
'icon-tool-freehand-roi': (props: IconProps) => ToolFreehandRoi(props),
|
||||
'icon-tool-spline-roi': (props: IconProps) => ToolSplineRoi(props),
|
||||
'tool-freehand': (props: IconProps) => ToolFreehand(props),
|
||||
'tool-fusion-color': (props: IconProps) => ToolFusionColor(props),
|
||||
'tool-invert': (props: IconProps) => ToolInvert(props),
|
||||
'tool-layout-default': (props: IconProps) => ToolLayoutDefault(props),
|
||||
'tool-magnetic-roi': (props: IconProps) => ToolMagneticRoi(props),
|
||||
'icon-tool-livewire': (props: IconProps) => ToolMagneticRoi(props),
|
||||
'tool-magnify': (props: IconProps) => ToolMagnify(props),
|
||||
'tool-measure-ellipse': (props: IconProps) => ToolMeasureEllipse(props),
|
||||
'tool-more-menu': (props: IconProps) => ToolMoreMenu(props),
|
||||
'tool-move': (props: IconProps) => ToolMove(props),
|
||||
'tool-polygon': (props: IconProps) => ToolPolygon(props),
|
||||
'tool-ellipse': (props: IconProps) => ToolMeasureEllipse(props),
|
||||
'tool-quick-magnify': (props: IconProps) => ToolQuickMagnify(props),
|
||||
'tool-rectangle': (props: IconProps) => ToolRectangle(props),
|
||||
'tool-referenceLines': (props: IconProps) => ToolReferenceLines(props),
|
||||
'tool-reset': (props: IconProps) => ToolReset(props),
|
||||
'tool-rotate-right': (props: IconProps) => ToolRotateRight(props),
|
||||
'tool-seg-brush': (props: IconProps) => ToolSegBrush(props),
|
||||
'tool-seg-eraser': (props: IconProps) => ToolSegEraser(props),
|
||||
'tool-seg-shape': (props: IconProps) => ToolSegShape(props),
|
||||
'tool-seg-threshold': (props: IconProps) => ToolSegThreshold(props),
|
||||
'tool-spline-roi': (props: IconProps) => ToolSplineRoi(props),
|
||||
'tool-stack-image-sync': (props: IconProps) => ToolStackImageSync(props),
|
||||
'tool-stack-scroll': (props: IconProps) => ToolStackScroll(props),
|
||||
'toggle-dicom-overlay': (props: IconProps) => ToolToggleDicomOverlay(props),
|
||||
'tool-ultrasound-bidirectional': (props: IconProps) => ToolUltrasoundBidirectional(props),
|
||||
'tool-window-level': (props: IconProps) => ToolWindowLevel(props),
|
||||
'tool-window-region': (props: IconProps) => ToolWindowRegion(props),
|
||||
'icon-tool-window-region': (props: IconProps) => ToolWindowRegion(props),
|
||||
'icon-tool-ultrasound-bidirectional': (props: IconProps) => ToolUltrasoundBidirectional(props),
|
||||
'icon-tool-cobb-angle': (props: IconProps) => ToolCobbAngle(props),
|
||||
'icon-tool-loupe': (props: IconProps) => ToolMagnify(props),
|
||||
'tool-probe': (props: IconProps) => ToolProbe(props),
|
||||
'icon-tool-probe': (props: IconProps) => ToolProbe(props),
|
||||
'tool-zoom': (props: IconProps) => ToolZoom(props),
|
||||
'tool-layout': (props: IconProps) => ToolLayout(props),
|
||||
'icon-transferring': (props: IconProps) => IconTransferring(props),
|
||||
'icon-alert-small': (props: IconProps) => Alert(props),
|
||||
'icon-alert-outline': (props: IconProps) => AlertOutline(props),
|
||||
'status-alert': (props: IconProps) => Alert(props),
|
||||
info: (props: IconProps) => Info(props),
|
||||
'notifications-info': (props: IconProps) => NotificationInfo(props),
|
||||
'notificationwarning-diamond': (props: IconProps) => NotificationWarning(props),
|
||||
'content-prev': (props: IconProps) => ContentPrev(props),
|
||||
'content-next': (props: IconProps) => ContentNext(props),
|
||||
'icon-settings': (props: IconProps) => Settings(props),
|
||||
close: (props: IconProps) => Close(props),
|
||||
pause: (props: IconProps) => Pause(props),
|
||||
'icon-pause': (props: IconProps) => Pause(props),
|
||||
settings: (props: IconProps) => Settings(props),
|
||||
play: (props: IconProps) => Play(props),
|
||||
'icon-play': (props: IconProps) => Play(props),
|
||||
'layout-advanced-3d-four-up': (props: IconProps) => LayoutAdvanced3DFourUp(props),
|
||||
'layout-advanced-3d-main': (props: IconProps) => LayoutAdvanced3DMain(props),
|
||||
'layout-advanced-3d-only': (props: IconProps) => LayoutAdvanced3DOnly(props),
|
||||
'layout-advanced-3d-primary': (props: IconProps) => LayoutAdvanced3DPrimary(props),
|
||||
'layout-advanced-axial-primary': (props: IconProps) => LayoutAdvancedAxialPrimary(props),
|
||||
'layout-advanced-mpr': (props: IconProps) => LayoutAdvancedMPR(props),
|
||||
'layout-common-1x1': (props: IconProps) => LayoutCommon1x1(props),
|
||||
'layout-common-1x2': (props: IconProps) => LayoutCommon1x2(props),
|
||||
'layout-common-2x2': (props: IconProps) => LayoutCommon2x2(props),
|
||||
'layout-common-2x3': (props: IconProps) => LayoutCommon2x3(props),
|
||||
pencil: (props: IconProps) => Pencil(props),
|
||||
'icon-list-view': (props: IconProps) => ListView(props),
|
||||
'chevron-menu': 'chevron-down',
|
||||
'icon-status-alert': (props: IconProps) => Alert(props),
|
||||
'info-link': (props: IconProps) => InfoLink(props),
|
||||
'launch-info': (props: IconProps) => LaunchInfo(props),
|
||||
'old-trash': (props: IconProps) => Trash(props),
|
||||
'tool-point': (props: IconProps) => ToolCircle(props),
|
||||
'tool-freehand-line': (props: IconProps) => ToolFreehand(props),
|
||||
clipboard: (props: IconProps) => Clipboard(props),
|
||||
|
||||
/** Adds an icon to the set of icons */
|
||||
addIcon: (name: string, icon) => {
|
||||
if (Icons[name]) {
|
||||
console.warn('Replacing icon', name);
|
||||
}
|
||||
Icons[name] = icon;
|
||||
},
|
||||
|
||||
ByName: ({ name, className, ...props }: { name: string; className?: string }) => {
|
||||
const IconComponent = Icons[name];
|
||||
|
||||
if (!IconComponent) {
|
||||
console.debug(`Icon "${name}" not found.`);
|
||||
return <div>Missing Icon</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<IconComponent
|
||||
{...props}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ActionNewDialog = (props: IconProps) => (
|
||||
<svg
|
||||
width="18px"
|
||||
height="19px"
|
||||
viewBox="0 0 18 19"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="*****Volume-Render-Dialog"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g
|
||||
id="volume-rendering-dialog"
|
||||
transform="translate(-696, -178)"
|
||||
>
|
||||
<g
|
||||
id="action-new-dialog"
|
||||
transform="translate(696, 178.5)"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="18"
|
||||
height="18"
|
||||
></rect>
|
||||
<path
|
||||
d="M6.73167982,5 L6.73167982,6 L3.23167982,6 C2.40325269,6 1.73167982,6.67157288 1.73167982,7.5 L1.73167982,14.5 C1.73167982,15.3284271 2.40325269,16 3.23167982,16 L10.2316798,16 C11.0601069,16 11.7316798,15.3284271 11.7316798,14.5 L11.7316798,11.5 L12.7316798,11.5 L12.7316798,14.5 C12.7316798,15.8807119 11.6123917,17 10.2316798,17 L3.23167982,17 C1.85096795,17 0.73167982,15.8807119 0.73167982,14.5 L0.73167982,7.5 C0.73167982,6.11928813 1.85096795,5 3.23167982,5 L6.73167982,5 Z"
|
||||
id="Path"
|
||||
fill="currentColor"
|
||||
fillRule="nonzero"
|
||||
></path>
|
||||
<line
|
||||
x1="8.23167982"
|
||||
y1="9.5"
|
||||
x2="16.2316798"
|
||||
y2="1.5"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></line>
|
||||
<polyline
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
points="11.2316798 1.5 16.2316798 1.5 16.2316798 6.5"
|
||||
></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ActionNewDialog;
|
||||
47
platform/ui-next/src/components/Icons/Sources/Actions.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Actions = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="more-dropdown"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<g
|
||||
id="Group-2"
|
||||
transform="translate(5, 4)"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle
|
||||
id="Oval"
|
||||
cx="6.74621802"
|
||||
cy="7.5938001"
|
||||
r="1.68710308"
|
||||
></circle>
|
||||
<path
|
||||
d="M8.17972732,1.06493425 L8.67634848,2.69920529 C8.84616867,3.26259112 9.42371506,3.59782471 9.99714943,3.46585687 L11.6537273,3.08194406 C12.2982043,2.9366791 12.9621245,3.22831522 13.2911435,3.80120174 C13.6201625,4.37408825 13.5374848,5.09450899 13.0872366,5.57796434 L11.9284539,6.82714853 C11.528377,7.25995707 11.528377,7.92764314 11.9284539,8.36045168 L13.0872366,9.60963586 C13.5374848,10.0930912 13.6201625,10.813512 13.2911435,11.3863985 C12.9621245,11.959285 12.2982043,12.2509211 11.6537273,12.1056561 L9.99714943,11.7217433 C9.42371506,11.5897755 8.84616867,11.9250091 8.67634848,12.4883949 L8.17972732,14.122666 C7.98874475,14.7549849 7.40616232,15.1876002 6.745631,15.1876002 C6.08509968,15.1876002 5.50251725,14.7549849 5.31153468,14.122666 L4.81491352,12.4883949 C4.64509333,11.9250091 4.06754694,11.5897755 3.49411257,11.7217433 L1.83753467,12.1056561 C1.19305769,12.2509211 0.529137506,11.959285 0.200118485,11.3863985 C-0.128900535,10.813512 -0.0462227777,10.0930912 0.404025372,9.60963586 L1.56280807,8.36045168 C1.96288499,7.92764314 1.96288499,7.25995707 1.56280807,6.82714853 L0.404025372,5.57796434 C-0.0462227777,5.09450899 -0.128900535,4.37408825 0.200118485,3.80120174 C0.529137506,3.22831522 1.19305769,2.9366791 1.83753467,3.08194406 L3.49411257,3.46585687 C4.06754694,3.59782471 4.64509333,3.26259112 4.81491352,2.69920529 L5.31153468,1.06493425 C5.50251725,0.43261528 6.08509968,0 6.745631,0 C7.40616232,0 7.98874475,0.43261528 8.17972732,1.06493425 Z"
|
||||
id="Path"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Actions;
|
||||
51
platform/ui-next/src/components/Icons/Sources/Add.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Add = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Add"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<g
|
||||
id="Group"
|
||||
transform="translate(6, 6)"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line
|
||||
x1="6"
|
||||
y1="0"
|
||||
x2="6"
|
||||
y2="12"
|
||||
id="Path"
|
||||
></line>
|
||||
<line
|
||||
x1="12"
|
||||
y1="6"
|
||||
x2="0"
|
||||
y2="6"
|
||||
id="Path"
|
||||
></line>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Add;
|
||||
32
platform/ui-next/src/components/Icons/Sources/Alert.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Alert = (props: IconProps) => (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<path
|
||||
d="M24 11.794c.017 6.667-5.333 12.108-12 12.205a11.823 11.823 0 0 1-12-11.79C-.019 5.541 5.331.1 12 .001a11.824 11.824 0 0 1 12 11.793z"
|
||||
fill="#B70D11"
|
||||
/>
|
||||
<g
|
||||
stroke="#FFF"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M11.494 17.158a.245.245 0 0 0-.241.255.254.254 0 0 0 .253.245h0a.246.246 0 0 0 .241-.255.253.253 0 0 0-.244-.245h-.005M11.503 13V6" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Alert;
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const AlertOutline = (props: IconProps) => (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
stroke="#5ACCE6"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M24 11.794a12.176 12.176 0 0 1-12 12.204 11.823 11.823 0 0 1-12-11.79A12.176 12.176 0 0 1 12 .003a11.824 11.824 0 0 1 12 11.793z"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<g strokeWidth="2">
|
||||
<path d="M11.74 18.658a.47.47 0 0 0-.26.075c-.068.05-.105.114-.1.18.007.135.175.244.38.244h0c.099 0 .193-.03.26-.076.07-.048.105-.113.1-.18-.006-.132-.165-.24-.366-.243h-.008M11.754 13V6" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default AlertOutline;
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ArrowLeftBold = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>arrow-left</title>
|
||||
<g
|
||||
id="Icons"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g
|
||||
id="Artboard"
|
||||
transform="translate(-268, -87)"
|
||||
>
|
||||
<g
|
||||
id="arrow-left"
|
||||
transform="translate(280, 99) scale(-1, 1) translate(-280, -99)translate(268, 87)"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
opacity="0.5"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<polygon
|
||||
id="Path"
|
||||
fill="currentColor"
|
||||
fillRule="nonzero"
|
||||
points="10.41 6 9 7.41 13.58 12 9 16.59 10.41 18 16.41 12"
|
||||
></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ArrowLeftBold;
|
||||
25
platform/ui-next/src/components/Icons/Sources/ArrowRight.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ArrowLeftBold = (props: IconProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g fillRule="evenodd">
|
||||
<path
|
||||
fill="currentcolor"
|
||||
fillRule="nonzero"
|
||||
d="M17.207 10.793c.36.36.388.928.083 1.32l-.083.094-5 5c-.39.39-1.024.39-1.414 0-.36-.36-.388-.928-.083-1.32l.083-.094 4.292-4.293-4.292-4.293c-.36-.36-.388-.928-.083-1.32l.083-.094c.36-.36.928-.388 1.32-.083l.094.083 5 5z"
|
||||
/>
|
||||
<path
|
||||
fill="currentcolor"
|
||||
fillRule="nonzero"
|
||||
d="M17.5 11.5c0 .513-.386.936-.883.993l-.117.007H6c-.552 0-1-.448-1-1 0-.513.386-.936.883-.993L6 10.5h10.5c.552 0 1 .448 1 1z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ArrowLeftBold;
|
||||
37
platform/ui-next/src/components/Icons/Sources/Cancel.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Cancel = (props: IconProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="19"
|
||||
height="19"
|
||||
viewBox="0 0 19 19"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<circle
|
||||
cx="9.5"
|
||||
cy="9.5"
|
||||
r="9.5"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<g
|
||||
stroke="#000"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
>
|
||||
<path
|
||||
d="M.188.187L8.813 8.812M8.813.187L.188 8.812"
|
||||
transform="translate(5 5)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Cancel;
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const CheckBoxChecked = (props: IconProps) => (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
id="3nvolf8jsa"
|
||||
d="M4.9 8.45 2.4 5.97l.795-.785L4.9 6.875 8.605 3.2l.795.79z"
|
||||
/>
|
||||
</defs>
|
||||
<g
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
stroke="#348CFD"
|
||||
fill="#348CFD"
|
||||
x=".5"
|
||||
y=".5"
|
||||
width="11"
|
||||
height="11"
|
||||
rx="3"
|
||||
/>
|
||||
<use
|
||||
fill="#000"
|
||||
xlinkHref="#3nvolf8jsa"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default CheckBoxChecked;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const CheckBoxUnchecked = (props: IconProps) => (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<rect
|
||||
x=".5"
|
||||
y=".5"
|
||||
width="11"
|
||||
height="11"
|
||||
rx="3"
|
||||
stroke="#348CFD"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default CheckBoxUnchecked;
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ChevronClosed = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="icon-chevron-closed"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
opacity="0.2"
|
||||
transform="translate(12, 12) rotate(90) translate(-12, -12)"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
></rect>
|
||||
<polyline
|
||||
id="Path-2"
|
||||
stroke="currentColor"
|
||||
transform="translate(12.0902, 12.0451) rotate(90) translate(-12.0902, -12.0451)"
|
||||
points="8 10 12.090229 14.090229 16.1804581 10"
|
||||
></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ChevronClosed;
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ChevronLeft = (props: IconProps) => (
|
||||
<svg
|
||||
width="7"
|
||||
height="12"
|
||||
viewBox="0 0 7 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<path d="M0 0h7v12H0z" />
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M5.757 1.757 1.515 6l4.242 4.243"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ChevronLeft;
|
||||
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ChevronOpen = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="icon-chevron-open"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
opacity="0.199999988"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="4"
|
||||
></rect>
|
||||
<polyline
|
||||
id="Path-2"
|
||||
stroke="currentColor"
|
||||
points="8 10 12.090229 14.090229 16.1804581 10"
|
||||
></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ChevronOpen;
|
||||
34
platform/ui-next/src/components/Icons/Sources/Clear.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Clear = (props: IconProps) => (
|
||||
<svg
|
||||
width="19"
|
||||
height="19"
|
||||
viewBox="0 0 19 19"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<circle
|
||||
fill="#0944B3"
|
||||
cx="9.5"
|
||||
cy="9.5"
|
||||
r="9.5"
|
||||
/>
|
||||
<g
|
||||
stroke="#000"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="m5.188 5.187 8.625 8.625M13.813 5.187l-8.625 8.625" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Clear;
|
||||
15
platform/ui-next/src/components/Icons/Sources/Clipboard.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Clipboard = (props: IconProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path d="M6 22v-16h16v7.543c0 4.107-6 2.457-6 2.457s1.518 6-2.638 6h-7.362zm18-7.614v-10.386h-20v20h10.189c3.163 0 9.811-7.223 9.811-9.614zm-10 1.614h-5v-1h5v1zm5-4h-10v1h10v-1zm0-3h-10v1h10v-1zm2-7h-19v19h-2v-21h21v2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Clipboard;
|
||||
20
platform/ui-next/src/components/Icons/Sources/Close.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Close = (props: IconProps) => (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M12 1.05 10.95 0 6 4.95 1.05 0 0 1.05 4.95 6 0 10.95 1.05 12 6 7.05 10.95 12 12 10.95 7.05 6z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Close;
|
||||
7
platform/ui-next/src/components/Icons/Sources/Code.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Code as LucideCode, LucideProps } from 'lucide-react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Code = (props: LucideProps) => <LucideCode {...props} />;
|
||||
|
||||
export default Code;
|
||||
100
platform/ui-next/src/components/Icons/Sources/ColorChange.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ColorChange = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="ColorChange"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<g
|
||||
id="Group-4"
|
||||
transform="translate(4, 5)"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M11.9830989,1.93861826 C10.5789104,0.665654409 8.7439168,-0.0269214719 6.84881419,0.000801239617 C3.06621664,0.000801239617 0,2.55531723 0,5.70792489 C0.13353775,8.17267636 1.81407658,10.2821527 4.18664171,10.963157 C5.1582084,11.3047338 5.96098033,11.0256244 6.52318651,9.75767005 C6.72995019,9.15827585 7.22615951,8.70434675 7.84154981,8.55163978 C8.45694011,8.39893281 9.10779864,8.568221 9.57079585,9.00141636 L9.70370511,9.13432562"
|
||||
id="Path"
|
||||
></path>
|
||||
<line
|
||||
x1="13.2723188"
|
||||
y1="6.75657895"
|
||||
x2="9.69838874"
|
||||
y2="13.9017808"
|
||||
id="Path"
|
||||
></line>
|
||||
<path
|
||||
d="M12.5054323,4.46389421 C12.1415749,5.29815229 12.4941364,6.27127189 13.3079188,6.67887077 C14.1217013,7.08646964 15.1121527,6.7860243 15.5623453,5.99500889 C16.4969889,4.55848545 16.3097611,2.6657723 15.1117829,1.44020853 C15.0640204,1.39470251 14.9942555,1.38091172 14.9327711,1.40482233 C14.8712867,1.42873293 14.8291685,1.48603381 14.8246989,1.55185231 C14.8366608,3.08695427 13.4517463,2.56727906 12.5054323,4.46389421 Z"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M3.52608268,6.59044238 C3.70959208,6.59044238 3.85835583,6.73920613 3.85835583,6.92271553"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M3.19380953,6.92404462 C3.19345611,6.83569024 3.22830728,6.75083342 3.29065893,6.68823236 C3.35301058,6.62563131 3.4377276,6.59044167 3.52608268,6.59044238"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M3.52608268,7.25498868 C3.34257329,7.25498868 3.19380953,7.10622492 3.19380953,6.92271553"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M3.85835583,6.92404462 C3.85762387,7.10703476 3.70907428,7.25499014 3.52608268,7.25498868"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M4.52290214,3.26771086 C4.70641153,3.26771086 4.85517529,3.41647462 4.85517529,3.59998401"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M4.19062898,3.60131311 C4.19062898,3.41780371 4.33939274,3.26903996 4.52290214,3.26903996"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M4.52290214,3.93225717 C4.33939274,3.93225717 4.19062898,3.78349341 4.19062898,3.59998401"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M4.85517529,3.60131311 C4.85444332,3.78430325 4.70589374,3.93225863 4.52290214,3.93225717"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M7.84563365,2.60316456 C8.02914304,2.60316456 8.1779068,2.75192832 8.1779068,2.93543771"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M7.5133605,2.9367668 C7.51300708,2.84841243 7.54785824,2.76355561 7.61020989,2.70095455 C7.67256154,2.63835349 7.75727857,2.60316385 7.84563365,2.60316456"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M7.84563365,3.26771086 C7.66212425,3.26771086 7.5133605,3.11894711 7.5133605,2.93543771"
|
||||
id="Path"
|
||||
></path>
|
||||
<path
|
||||
d="M8.1779068,2.9367668 C8.1779068,3.1202762 8.02914304,3.26903996 7.84563365,3.26903996"
|
||||
id="Path"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ColorChange;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ContentNext = (props: IconProps) => (
|
||||
<svg
|
||||
width="6px"
|
||||
height="10px"
|
||||
viewBox="0 0 6 10"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Icons"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g
|
||||
id="Artboard"
|
||||
transform="translate(-460, -343)"
|
||||
fill="#FFFFFF"
|
||||
>
|
||||
<polygon
|
||||
id="chevron-next"
|
||||
transform="translate(463, 348) scale(-1, 1) rotate(90) translate(-463, -348)"
|
||||
points="463 351 458 345.736842 458.7 345 463 349.526316 467.3 345 468 345.736842"
|
||||
></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ContentNext;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const ContentPrev = (props: IconProps) => (
|
||||
<svg
|
||||
width="6px"
|
||||
height="10px"
|
||||
viewBox="0 0 6 10"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Icons"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g
|
||||
id="Artboard"
|
||||
transform="translate(-435, -343)"
|
||||
fill="#FFFFFF"
|
||||
>
|
||||
<polygon
|
||||
id="chevron-prev"
|
||||
transform="translate(438, 348) rotate(90) translate(-438, -348)"
|
||||
points="438 351 433 345.736842 433.7 345 438 349.526316 442.3 345 443 345.736842"
|
||||
></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ContentPrev;
|
||||
99
platform/ui-next/src/components/Icons/Sources/Controls.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Controls = (props: IconProps) => (
|
||||
<svg
|
||||
width="18px"
|
||||
height="18px"
|
||||
viewBox="0 0 18 18"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Controls"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle
|
||||
id="Oval"
|
||||
stroke="currentColor"
|
||||
cx="9"
|
||||
cy="3.34782609"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="10.7373913"
|
||||
y1="3.34782609"
|
||||
x2="16.4095652"
|
||||
y2="3.34782609"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
></line>
|
||||
<line
|
||||
x1="1.62695652"
|
||||
y1="3.34782609"
|
||||
x2="7.2573913"
|
||||
y2="3.34782609"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
></line>
|
||||
<g
|
||||
id="Group-26"
|
||||
transform="translate(9.0183, 9) scale(-1, 1) translate(-9.0183, -9)translate(1.627, 7.2609)"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<circle
|
||||
id="Oval"
|
||||
transform="translate(3.8948, 1.7391) scale(-1, 1) translate(-3.8948, -1.7391)"
|
||||
cx="3.89478261"
|
||||
cy="1.73913043"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="5.63043478"
|
||||
y1="1.73913043"
|
||||
x2="14.7826087"
|
||||
y2="1.73913043"
|
||||
id="Path"
|
||||
transform="translate(10.2065, 1.7391) scale(-1, 1) translate(-10.2065, -1.7391)"
|
||||
></line>
|
||||
<line
|
||||
x1="0"
|
||||
y1="1.73913043"
|
||||
x2="2.15217391"
|
||||
y2="1.73913043"
|
||||
id="Path"
|
||||
transform="translate(1.0761, 1.7391) scale(-1, 1) translate(-1.0761, -1.7391)"
|
||||
></line>
|
||||
</g>
|
||||
<circle
|
||||
id="Oval"
|
||||
stroke="currentColor"
|
||||
cx="7.26086957"
|
||||
cy="14.6521739"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="8.99652174"
|
||||
y1="14.6521739"
|
||||
x2="16.4095652"
|
||||
y2="14.6521739"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
></line>
|
||||
<line
|
||||
x1="1.62695652"
|
||||
y1="14.6521739"
|
||||
x2="5.52173913"
|
||||
y2="14.6521739"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
></line>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Controls;
|
||||
44
platform/ui-next/src/components/Icons/Sources/Copy.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Copy = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Copy"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
stroke="currentColor"
|
||||
x="8.95205173"
|
||||
y="4.5"
|
||||
width="10"
|
||||
height="10"
|
||||
rx="2"
|
||||
></rect>
|
||||
<path
|
||||
d="M7.05569885,9.5 L5.5,9.5 C4.67157288,9.5 4,10.1715729 4,11 L4,17.8271183 C4,18.6555454 4.67157288,19.3271183 5.5,19.3271183 L12.4520517,19.3271183 C13.2804789,19.3271183 13.9520517,18.6555454 13.9520517,17.8271183 L13.9520517,16.3489489 L13.9520517,16.3489489"
|
||||
id="Path-4"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Copy;
|
||||
33
platform/ui-next/src/components/Icons/Sources/Database.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Database = (props: IconProps) => (
|
||||
<svg
|
||||
width="19"
|
||||
height="19"
|
||||
viewBox="0 0 19 19"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
transform="matrix(0.84873874,0,0,0.84873874,0.13586772,0.14249413)"
|
||||
>
|
||||
<ellipse
|
||||
cx="11"
|
||||
cy="4"
|
||||
rx="9"
|
||||
ry="3"
|
||||
/>
|
||||
<path d="m 2,4 v 14 a 9,3 0 0 0 18,0 V 4" />
|
||||
<path d="m 2,11 a 9,3 0 0 0 18,0" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Database;
|
||||
58
platform/ui-next/src/components/Icons/Sources/Delete.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const Delete = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Delete"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
></rect>
|
||||
<circle
|
||||
id="Oval"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="7"
|
||||
></circle>
|
||||
<line
|
||||
x1="8.95652174"
|
||||
y1="8.95652174"
|
||||
x2="15.0434783"
|
||||
y2="15.0434783"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></line>
|
||||
<line
|
||||
x1="15.0434783"
|
||||
y1="8.95652174"
|
||||
x2="8.95652174"
|
||||
y2="15.0434783"
|
||||
id="Path"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></line>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Delete;
|
||||
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const DicomTagBrowser = (props: IconProps) => (
|
||||
<svg
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 28 28"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="tool-dicom-tag-browser"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="28"
|
||||
height="28"
|
||||
></rect>
|
||||
<g
|
||||
id="Group"
|
||||
transform="translate(4, 5.5)"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
>
|
||||
<circle
|
||||
id="Oval"
|
||||
cx="1.73913043"
|
||||
cy="1.73913043"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="6.95652174"
|
||||
y1="1.73913043"
|
||||
x2="20"
|
||||
y2="1.73913043"
|
||||
id="Path"
|
||||
></line>
|
||||
<circle
|
||||
id="Oval"
|
||||
cx="1.73913043"
|
||||
cy="8.69565217"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="6.95652174"
|
||||
y1="8.69565217"
|
||||
x2="20"
|
||||
y2="8.69565217"
|
||||
id="Path"
|
||||
></line>
|
||||
<circle
|
||||
id="Oval"
|
||||
cx="1.73913043"
|
||||
cy="15.6521739"
|
||||
r="1.73913043"
|
||||
></circle>
|
||||
<line
|
||||
x1="6.95652174"
|
||||
y1="15.6521739"
|
||||
x2="20"
|
||||
y2="15.6521739"
|
||||
id="Path"
|
||||
></line>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default DicomTagBrowser;
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const DisplayFillAndOutline = (props: IconProps) => (
|
||||
<svg
|
||||
width="18px"
|
||||
height="18px"
|
||||
viewBox="0 0 18 18"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="view-outline-fill"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g id="Group-13">
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="18"
|
||||
height="18"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
stroke="currentColor"
|
||||
x="1.5"
|
||||
y="1.5"
|
||||
width="15"
|
||||
height="15"
|
||||
rx="1"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
fill="currentColor"
|
||||
x="3.5"
|
||||
y="3.5"
|
||||
width="11"
|
||||
height="11"
|
||||
rx="1"
|
||||
></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default DisplayFillAndOutline;
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '../types';
|
||||
|
||||
export const DisplayFillOnly = (props: IconProps) => (
|
||||
<svg
|
||||
width="18px"
|
||||
height="18px"
|
||||
viewBox="0 0 18 18"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="view-fill"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g id="Group-13">
|
||||
<rect
|
||||
id="Rectangle"
|
||||
x="0"
|
||||
y="0"
|
||||
width="18"
|
||||
height="18"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle"
|
||||
fill="currentColor"
|
||||
x="2"
|
||||
y="2"
|
||||
width="14"
|
||||
height="14"
|
||||
rx="1"
|
||||
></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default DisplayFillOnly;
|
||||