@finos/legend-application-studio
Version:
Legend Studio application core
101 lines • 6.64 kB
JavaScript
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