UNPKG

@finos/legend-application-studio

Version:
101 lines 6.64 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; /** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { BasePopover, clsx, HashtagIcon, TagIcon } from '@finos/legend-art'; import { useState } from 'react'; /** * Read-only display for `stereotypes` + `taggedValues` on any * `AnnotatedElement` (Database, Schema, Table, View, Column). Mirrors the * Pure DSL form `<<profile.stereo>> { tag = "value" }` but rendered as small * inline pills so it can sit next to a relation name in a header row, in a * tree row, or in a dedicated section. * * Three layout modes: * - `inline` (default): pills flow next to the surrounding row content. * - `block`: pills wrap into their own block (used in side-panel sections * where the parent has its own column structure). * - `compact`: only renders a single combined badge with the count — used * when horizontal space is tight (e.g. the canvas table-node header). * Hover shows the full content via `title`. * * The component is dependency-free (no MobX) so it can be used from any of * the editor's sub-components without observability concerns — the inputs * are static metamodel arrays. */ const formatStereotype = (stereotype) => { // Pure surface syntax: `profile.stereotypeName`. We mimic that for the // tooltip + compact-mode label so users see the same identifier the // grammar uses. const profilePath = stereotype.ownerReference.valueForSerialization ?? stereotype.value._OWNER.path; return `${profilePath}.${stereotype.value.value}`; }; const formatTaggedValue = (taggedValue) => { // Same rationale as above — match the grammar form `profile.tag = "value"`. const profilePath = taggedValue.tag.ownerReference.valueForSerialization ?? taggedValue.tag.value._OWNER.path; return `${profilePath}.${taggedValue.tag.value.value} = "${taggedValue.value}"`; }; /** * Click-to-open popover badge used in compact mode. Kept separate from the * parent component so the `useState` hook for the popover anchor is * unconditionally called (the parent has an early-return for the empty case * which would otherwise put this hook below a conditional). */ function CompactAnnotationBadge(props) { const { stereotypes, taggedValues, className } = props; const [anchor, setAnchor] = useState(null); const total = stereotypes.length + taggedValues.length; return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: clsx('database-annotation', 'database-annotation--compact', className), onClick: (event) => { // Stop propagation so clicking the badge inside a clickable canvas // node header (or tree row) doesn't also trigger the parent's // selection handler. event.stopPropagation(); setAnchor(event.currentTarget); }, title: `${total} annotation${total === 1 ? '' : 's'} \u2014 click to view`, children: [_jsx(TagIcon, {}), _jsx("span", { className: "database-annotation__count", children: total })] }), _jsx(BasePopover, { open: Boolean(anchor), anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, transformOrigin: { vertical: 'top', horizontal: 'left' }, children: _jsx("div", { className: "database-annotation__popover", // Stop click events on the popover content from reaching the // ReactFlow canvas underneath. onClick: (event) => event.stopPropagation(), children: _jsx(DatabaseAnnotationDisplay, { stereotypes: stereotypes, taggedValues: taggedValues, layout: "block" }) }) })] })); } export const DatabaseAnnotationDisplay = ({ stereotypes, taggedValues, layout = 'inline', className }) => { if (stereotypes.length === 0 && taggedValues.length === 0) { return null; } // Compact: collapse everything into a single badge with the total count. // Useful inside dense headers (canvas table-node) where a row of pills // would crowd out the relation name. Click opens a popover with the full // list rendered in `block` layout. Extracted into its own component so // the `useState` hook is unconditionally called (we can't put a hook // after the early-return at the top of the parent). if (layout === 'compact') { return (_jsx(CompactAnnotationBadge, { stereotypes: stereotypes, taggedValues: taggedValues, className: className })); } return (_jsxs("div", { className: clsx('database-annotation', `database-annotation--${layout}`, className), children: [stereotypes.map((stereotype) => { const label = formatStereotype(stereotype); return ( // Profiles can re-use stereotype names across owners, so prefix the // key with the owner path to keep keys unique. _jsxs("span", { className: "database-annotation__stereotype", title: ${label}»`, children: [_jsx(HashtagIcon, {}), _jsx("span", { className: "database-annotation__stereotype__label", children: stereotype.value.value })] }, `stereo:${label}`)); }), taggedValues.map((taggedValue) => { const tagName = taggedValue.tag.value.value; return ( // Tagged values aren't unique by tag (the same tag can appear // twice with different values), so we key off `_UUID` rather than // a composite of tag path + name. _jsxs("span", { className: "database-annotation__tagged-value", title: formatTaggedValue(taggedValue), children: [_jsx(TagIcon, {}), _jsx("span", { className: "database-annotation__tagged-value__name", children: tagName }), _jsx("span", { className: "database-annotation__tagged-value__sep", children: "=" }), _jsx("span", { className: "database-annotation__tagged-value__value", children: taggedValue.value })] }, taggedValue._UUID)); })] })); }; //# sourceMappingURL=DatabaseAnnotationDisplay.js.map