mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Badge: option to show short/full versions of contents (#1397)
* add button to expand and view whole badge contents Signed-off-by: Sebastian Malton <sebastian@malton.name> * revert font changes due to update elsewhere already existing Signed-off-by: Sebastian Malton <sebastian@malton.name> * make expanding icon smaller and not focusable Signed-off-by: Sebastian Malton <sebastian@malton.name> * extended badge content by click, selecting text from label doesn't trigger auto-closing Signed-off-by: Roman <ixrock@gmail.com> * fix: hover only intercative badges (with extra content) Signed-off-by: Roman <ixrock@gmail.com> * added common document/selectionchange watcher Signed-off-by: Roman <ixrock@gmail.com> * lint fixes Signed-off-by: Roman <ixrock@gmail.com> * Convert Badge to CSS Modules Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Align Badge styles across components Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Cleaning up Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Not closing badge if user started to select text Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> Co-authored-by: Sebastian Malton <sebastian@malton.name> Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
ebb54facf9
commit
ca1ad425ab
@ -27,13 +27,7 @@
|
||||
}
|
||||
|
||||
.status {
|
||||
.Badge {
|
||||
@include release-status-bgs;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@include release-status-bgs;
|
||||
}
|
||||
|
||||
.chart {
|
||||
|
||||
@ -272,7 +272,7 @@ export class ReleaseDetails extends Component<Props> {
|
||||
<DrawerItem name="Status" className="status" labelsOnly>
|
||||
<Badge
|
||||
label={release.getStatus()}
|
||||
className={cssNames("status", kebabCase(release.getStatus()))}
|
||||
className={kebabCase(release.getStatus())}
|
||||
/>
|
||||
</DrawerItem>
|
||||
{this.renderValues()}
|
||||
|
||||
@ -88,6 +88,7 @@ export class CatalogEntityItem<T extends CatalogEntity> implements ItemObject {
|
||||
onClick?.(event);
|
||||
event.stopPropagation();
|
||||
}}
|
||||
expandable={false}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
@import "autoscaler.mixins";
|
||||
|
||||
.HpaDetails {
|
||||
.Badge {
|
||||
.status {
|
||||
@include hpa-status-bgc;
|
||||
}
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ export class HpaDetails extends React.Component<Props> {
|
||||
{hpa.getReplicas()}
|
||||
</DrawerItem>
|
||||
|
||||
<DrawerItem name="Status" labelsOnly>
|
||||
<DrawerItem name="Status" className="status" labelsOnly>
|
||||
{hpa.getConditions().map(({ type, tooltip, isReady }) => {
|
||||
if (!isReady) return null;
|
||||
|
||||
|
||||
@ -38,10 +38,7 @@
|
||||
&.status {
|
||||
flex: 1.5;
|
||||
@include table-cell-labels-offsets;
|
||||
|
||||
.Badge {
|
||||
@include hpa-status-bgc;
|
||||
}
|
||||
@include hpa-status-bgc;
|
||||
}
|
||||
|
||||
&.age {
|
||||
|
||||
@ -104,6 +104,7 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
|
||||
label={type}
|
||||
tooltip={tooltip}
|
||||
className={cssNames(type.toLowerCase())}
|
||||
expandable={false}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
||||
@ -79,7 +79,7 @@ export class Secrets extends React.Component<Props> {
|
||||
secret.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={secret} />,
|
||||
secret.getNs(),
|
||||
secret.getLabels().map(label => <Badge key={label} label={label}/>),
|
||||
secret.getLabels().map(label => <Badge key={label} label={label} expandable={false}/>),
|
||||
secret.getKeys().join(", "),
|
||||
secret.type,
|
||||
secret.getAge(),
|
||||
|
||||
@ -23,11 +23,9 @@
|
||||
|
||||
.CRDDetails {
|
||||
.conditions {
|
||||
.Badge {
|
||||
@include crd-condition-bgc;
|
||||
}
|
||||
@include crd-condition-bgc;
|
||||
}
|
||||
|
||||
|
||||
.Table {
|
||||
margin-left: -$margin * 2;
|
||||
margin-right: -$margin * 2;
|
||||
@ -41,21 +39,8 @@
|
||||
flex: 0.5;
|
||||
}
|
||||
|
||||
.description {
|
||||
flex: 1.5;
|
||||
|
||||
.Badge {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.json-path {
|
||||
flex: 3;
|
||||
|
||||
.Badge {
|
||||
white-space: pre-line;
|
||||
text-overflow: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { observer } from "mobx-react";
|
||||
import type { CustomResourceDefinition } from "../../api/endpoints/crd.api";
|
||||
import { cssNames } from "../../utils";
|
||||
import { AceEditor } from "../ace-editor";
|
||||
import { Badge } from "../badge";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
@ -86,7 +85,8 @@ export class CRDDetails extends React.Component<Props> {
|
||||
<Badge
|
||||
key={type}
|
||||
label={type}
|
||||
className={cssNames({ disabled: status === "False" }, type)}
|
||||
disabled={status === "False"}
|
||||
className={type}
|
||||
tooltip={(
|
||||
<>
|
||||
<p>{message}</p>
|
||||
|
||||
@ -21,13 +21,9 @@
|
||||
|
||||
.CrdResourceDetails {
|
||||
.status {
|
||||
.value {
|
||||
.Badge {
|
||||
&.ready {
|
||||
color: white;
|
||||
background-color: $colorOk;
|
||||
}
|
||||
}
|
||||
.ready {
|
||||
color: white;
|
||||
background-color: $colorOk;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,8 @@ export class CrdResourceDetails extends React.Component<Props> {
|
||||
.map(({ kind, message, status }, index) => (
|
||||
<Badge
|
||||
key={kind + index} label={kind}
|
||||
className={cssNames({ disabled: status === "False" }, kind.toLowerCase())}
|
||||
disabled={status === "False"}
|
||||
className={kind.toLowerCase()}
|
||||
tooltip={message}
|
||||
/>
|
||||
));
|
||||
|
||||
@ -30,7 +30,7 @@ $crd-condition-colors: (
|
||||
|
||||
@mixin crd-condition-bgc {
|
||||
@each $status, $color in $crd-condition-colors {
|
||||
&.#{$status} {
|
||||
.#{$status} {
|
||||
background: $color;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@ -24,9 +24,6 @@
|
||||
--status-default-bg: #{$colorError};
|
||||
|
||||
.conditions {
|
||||
.Badge {
|
||||
cursor: default;
|
||||
@include node-status-bgs;
|
||||
}
|
||||
@include node-status-bgs;
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ $node-status-color-list: (
|
||||
|
||||
@mixin node-status-bgs {
|
||||
@each $status, $color in $node-status-color-list {
|
||||
&.#{$status} {
|
||||
.#{$status} {
|
||||
background: $color;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@ -33,8 +33,6 @@
|
||||
}
|
||||
|
||||
.conditions {
|
||||
.Badge {
|
||||
@include job-condition-bgs;
|
||||
}
|
||||
@include job-condition-bgs;
|
||||
}
|
||||
}
|
||||
@ -21,21 +21,19 @@
|
||||
|
||||
.DeploymentDetails {
|
||||
.conditions {
|
||||
.Badge {
|
||||
&.available {
|
||||
color: white;
|
||||
background-color: $deployment-available;
|
||||
}
|
||||
.available {
|
||||
color: white;
|
||||
background-color: $deployment-available;
|
||||
}
|
||||
|
||||
&.progressing {
|
||||
color: white;
|
||||
background-color: $deployment-progressing;
|
||||
}
|
||||
.progressing {
|
||||
color: white;
|
||||
background-color: $deployment-progressing;
|
||||
}
|
||||
|
||||
&.replica-failure {
|
||||
color: white;
|
||||
background-color: $deployment-replicafailure;
|
||||
}
|
||||
.replica-failure {
|
||||
color: white;
|
||||
background-color: $deployment-replicafailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,6 @@ import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import { Badge } from "../badge";
|
||||
import type { Deployment } from "../../api/endpoints";
|
||||
import { cssNames } from "../../utils";
|
||||
import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations";
|
||||
import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
@ -118,7 +117,8 @@ export class DeploymentDetails extends React.Component<Props> {
|
||||
<Badge
|
||||
key={type}
|
||||
label={type}
|
||||
className={cssNames({ disabled: status === "False" }, kebabCase(type))}
|
||||
disabled={status === "False"}
|
||||
className={kebabCase(type)}
|
||||
tooltip={(
|
||||
<>
|
||||
<p>{message}</p>
|
||||
|
||||
@ -22,8 +22,6 @@
|
||||
|
||||
.JobDetails {
|
||||
.conditions {
|
||||
.Badge {
|
||||
@include job-condition-bgs;
|
||||
}
|
||||
@include job-condition-bgs;
|
||||
}
|
||||
}
|
||||
@ -58,8 +58,4 @@
|
||||
|
||||
@include pod-status-colors;
|
||||
}
|
||||
|
||||
.Badge {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
@ -142,7 +142,7 @@ export class PodDetails extends React.Component<Props> {
|
||||
<Badge
|
||||
key={type}
|
||||
label={type}
|
||||
className={cssNames({ disabled: status === "False" })}
|
||||
disabled={status === "False"}
|
||||
tooltip={`Last transition time: ${lastTransitionTime}`}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -127,7 +127,7 @@ export class Pods extends React.Component<Props> {
|
||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||
]}
|
||||
renderTableContents={(pod: Pod) => [
|
||||
<Badge flat key="name" label={pod.getName()} tooltip={pod.getName()} />,
|
||||
<Badge flat key="name" label={pod.getName()} tooltip={pod.getName()} expandable={false} />,
|
||||
<KubeObjectStatusIcon key="icon" object={pod} />,
|
||||
pod.getNs(),
|
||||
this.renderContainersStatus(pod),
|
||||
@ -145,7 +145,7 @@ export class Pods extends React.Component<Props> {
|
||||
);
|
||||
}),
|
||||
pod.getNodeName() ?
|
||||
<Badge flat key="node" className="node" tooltip={pod.getNodeName()}>
|
||||
<Badge flat key="node" className="node" tooltip={pod.getNodeName()} expandable={false}>
|
||||
<Link to={getDetailsUrl(nodesApi.getUrl({ name: pod.getNodeName() }))} onClick={stopPropagation}>
|
||||
{pod.getNodeName()}
|
||||
</Link>
|
||||
|
||||
@ -98,7 +98,7 @@ $cronjob-condition-color-list: (
|
||||
|
||||
@mixin job-condition-bgs {
|
||||
@each $condition, $color in $job-condition-color-list {
|
||||
&.#{$condition} {
|
||||
.#{$condition} {
|
||||
color: white;
|
||||
background: $color;
|
||||
}
|
||||
|
||||
@ -19,25 +19,41 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.Badge {
|
||||
.badge {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.badge + .badge {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.badge.interactive:hover {
|
||||
background-color: var(--mainBackground);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.badge:not(.isExpanded) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not(.flat) {
|
||||
background: $colorVague;
|
||||
color: $textColorSecondary;
|
||||
border-radius: $radius;
|
||||
padding: .2em .4em;
|
||||
}
|
||||
|
||||
&.small {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.badge:not(.flat) {
|
||||
background: var(--colorVague);
|
||||
border-radius: 3px;
|
||||
padding: .2em .4em;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@ -19,29 +19,84 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import "./badge.scss";
|
||||
import styles from "./badge.module.css";
|
||||
|
||||
import React from "react";
|
||||
import { computed, makeObservable, observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { cssNames } from "../../utils/cssNames";
|
||||
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
||||
import { boundMethod } from "../../utils";
|
||||
|
||||
export interface BadgeProps extends React.HTMLAttributes<any>, TooltipDecoratorProps {
|
||||
small?: boolean;
|
||||
flat?: boolean;
|
||||
label?: React.ReactNode;
|
||||
expandable?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
// Common handler for all Badge instances
|
||||
document.addEventListener("selectionchange", () => {
|
||||
Badge.badgeMeta.hasTextSelected ||= window.getSelection().toString().trim().length > 0;
|
||||
});
|
||||
|
||||
@withTooltip
|
||||
@observer
|
||||
export class Badge extends React.Component<BadgeProps> {
|
||||
render() {
|
||||
const { className, label, small, flat, children, ...elemProps } = this.props;
|
||||
const clickable = Boolean(this.props.onClick);
|
||||
static defaultProps: Partial<BadgeProps> = {
|
||||
expandable: true
|
||||
};
|
||||
|
||||
return <>
|
||||
<span className={cssNames("Badge", { small, flat, clickable }, className)} {...elemProps}>
|
||||
static badgeMeta = observable({
|
||||
hasTextSelected: false
|
||||
});
|
||||
|
||||
@observable.ref elem: HTMLElement;
|
||||
@observable isExpanded = false;
|
||||
|
||||
constructor(props: BadgeProps) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed get isExpandable() {
|
||||
if (!this.props.expandable) return false;
|
||||
|
||||
return this.elem?.clientWidth < this.elem?.scrollWidth;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
onMouseUp() {
|
||||
if (!this.isExpandable || Badge.badgeMeta.hasTextSelected) {
|
||||
Badge.badgeMeta.hasTextSelected = false;
|
||||
} else {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
bindRef(elem: HTMLElement) {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, label, disabled, small, children, flat, expandable, ...elemProps } = this.props;
|
||||
const clickable = Boolean(this.props.onClick) || this.isExpandable;
|
||||
const classNames = cssNames(styles.badge, className, {
|
||||
[styles.small]: small,
|
||||
[styles.flat]: flat,
|
||||
[styles.clickable]: clickable,
|
||||
[styles.interactive]: this.isExpandable,
|
||||
[styles.isExpanded]: this.isExpanded,
|
||||
[styles.disabled]: disabled
|
||||
});
|
||||
|
||||
return (
|
||||
<div {...elemProps} className={classNames} onMouseUp={this.onMouseUp} ref={this.bindRef}>
|
||||
{label}
|
||||
{children}
|
||||
</span>
|
||||
</>;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
.legend {
|
||||
.Badge {
|
||||
.LegendBadge {
|
||||
background: transparent;
|
||||
transition: background-color 250ms;
|
||||
white-space: normal;
|
||||
|
||||
@ -169,7 +169,7 @@ export class Chart extends React.Component<ChartProps> {
|
||||
const labelElem = (title: string, color: string, tooltip?: string) => (
|
||||
<Badge
|
||||
key={title}
|
||||
className="flex gaps align-center"
|
||||
className="LegendBadge flex gaps align-center"
|
||||
label={(
|
||||
<div>
|
||||
<StatusBrick style={{ backgroundColor: color }}/>
|
||||
@ -177,6 +177,7 @@ export class Chart extends React.Component<ChartProps> {
|
||||
</div>
|
||||
)}
|
||||
tooltip={tooltip}
|
||||
expandable={false}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
flex-direction: column;
|
||||
|
||||
> * {
|
||||
&.Badge:hover {
|
||||
&.LegendBadge:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
@ -69,13 +69,14 @@ export class ShowMetricsSetting extends React.Component<Props> {
|
||||
const tooltipId = `${name}`;
|
||||
|
||||
return (
|
||||
<Badge key={name} flat>
|
||||
<Badge key={name} flat expandable={false}>
|
||||
<span id={tooltipId}>{name}</span>
|
||||
<Icon
|
||||
smallest
|
||||
material="clear"
|
||||
onClick={() => this.removeMetric(name)}
|
||||
tooltip="Remove"
|
||||
className="mx-3"
|
||||
/>
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@ -30,10 +30,6 @@
|
||||
margin-right: var(--padding);
|
||||
}
|
||||
|
||||
.Badge {
|
||||
background-color: var(--dockBadgeBackground);
|
||||
}
|
||||
|
||||
> .controls {
|
||||
white-space: nowrap;
|
||||
flex: 1 1;
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.Badge {
|
||||
> div {
|
||||
float: left;
|
||||
margin: $spacing;
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.Badge {
|
||||
.SelectorIndex {
|
||||
cursor: pointer;
|
||||
background: var(--secondaryBackground);
|
||||
width: 100%;
|
||||
|
||||
@ -49,6 +49,7 @@ export function HotbarSelector({ hotbar }: Props) {
|
||||
preferredPositions: [TooltipPosition.TOP, TooltipPosition.TOP_LEFT],
|
||||
children: hotbar.name
|
||||
}}
|
||||
className="SelectorIndex"
|
||||
/>
|
||||
</div>
|
||||
<Icon material="play_arrow" className="next box" onClick={() => store.switchToNext()} />
|
||||
|
||||
@ -153,4 +153,4 @@
|
||||
@extend .active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ export class PageFiltersList extends React.Component<Props> {
|
||||
<Badge
|
||||
key={`${type}-${value}`}
|
||||
title={type}
|
||||
className={cssNames("flex gaps filter align-center", type)}
|
||||
className={cssNames("Badge flex gaps filter align-center", type)}
|
||||
label={(
|
||||
<>
|
||||
<FilterIcon type={type}/>
|
||||
|
||||
@ -49,8 +49,4 @@
|
||||
@mixin table-cell-labels-offsets {
|
||||
padding-top: $padding / 2;
|
||||
padding-bottom: 0;
|
||||
|
||||
.Badge + .Badge {
|
||||
margin-left: $padding / 2;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user