-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Log Viewer: Enhances the donut chart to be responsive, link to log search, and show numbers directly #20928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Log Viewer: Enhances the donut chart to be responsive, link to log search, and show numbers directly #20928
Changes from all commits
49219aa
4de030a
a471c72
a2eec64
f27ebb5
f6c66ec
5b46335
52d36ca
72d7715
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ interface Circle { | |
| percent: number; | ||
| kind: string; | ||
| number: number; | ||
| href: string; | ||
| } | ||
|
|
||
| interface CircleWithCommands extends Circle { | ||
|
|
@@ -72,6 +73,20 @@ export class UmbDonutChartElement extends LitElement { | |
| @property({ type: Boolean }) | ||
| hideDetailBox = false; | ||
|
|
||
| /** | ||
| * Shows numbers inside each slice of the donut chart | ||
| * @memberof UmbDonutChartElement | ||
| */ | ||
| @property({ type: Boolean, attribute: 'show-inline-numbers' }) | ||
| showInlineNumbers = false; | ||
|
|
||
| /** | ||
| * Shows the description text below the chart | ||
| * @memberof UmbDonutChartElement | ||
| */ | ||
| @property({ type: Boolean, attribute: 'show-description' }) | ||
| showDescription = false; | ||
|
|
||
| @queryAssignedElements({ selector: 'umb-donut-slice' }) | ||
| private _slices!: UmbDonutSliceElement[]; | ||
|
|
||
|
|
@@ -141,6 +156,7 @@ export class UmbDonutChartElement extends LitElement { | |
| color: slice.color, | ||
| name: slice.name, | ||
| kind: slice.kind, | ||
| href: slice.href, | ||
| }; | ||
| }), | ||
| ); | ||
|
|
@@ -180,11 +196,28 @@ export class UmbDonutChartElement extends LitElement { | |
| return [coordX, coordY].join(' '); | ||
| } | ||
|
|
||
| #getTextPosition(circle: CircleWithCommands): { x: number; y: number } { | ||
| // Calculate the middle angle of the slice | ||
| const startAngle = -circle.offset; | ||
| const sliceDegrees = UmbDonutChartElement.percentToDegrees(circle.percent); | ||
| const middleAngle = startAngle + sliceDegrees / 2; | ||
|
|
||
| // Position the text at the middle of the donut ring | ||
| const textRadius = this.radius - this.borderSize / 2; | ||
| const angleRad = (middleAngle * Math.PI) / 180; | ||
| const x = Math.cos(angleRad) * textRadius + this.svgSize / 2; | ||
| const y = -Math.sin(angleRad) * textRadius + this.svgSize / 2; | ||
|
|
||
| return { x, y }; | ||
| } | ||
|
|
||
| #calculateDetailsBoxPosition = (event: MouseEvent) => { | ||
| // Recalculate bounds on each mouse move to handle window resize | ||
| this.#containerBounds = this._container.getBoundingClientRect(); | ||
| const x = this.#containerBounds ? event.clientX - this.#containerBounds?.left : 0; | ||
| const y = this.#containerBounds ? event.clientY - this.#containerBounds?.top : 0; | ||
| this._posX = x - 10; | ||
| this._posY = y - 70; | ||
| this._posX = x + 10; | ||
| this._posY = y + 10; | ||
| }; | ||
|
Comment on lines
214
to
221
|
||
|
|
||
| #setDetailsBoxData(event: MouseEvent) { | ||
|
|
@@ -231,11 +264,10 @@ export class UmbDonutChartElement extends LitElement { | |
| <feDropShadow stdDeviation="1 1" in="merge1" dx="0" dy="0" flood-color="#000" flood-opacity="0.8" x="0%" y="0%" width="100%" height="100%" result="dropShadow1"/> | ||
| </filter> | ||
| <desc>${this.description}</desc> | ||
| ${this._circles.map( | ||
| (circle, i) => svg` | ||
| ${this._circles.map((circle, i) => { | ||
| const content = svg` | ||
| <path | ||
| class="circle" | ||
|
|
||
| data-index="${i}" | ||
| fill="${circle.color}" | ||
| role="listitem" | ||
|
|
@@ -251,22 +283,41 @@ export class UmbDonutChartElement extends LitElement { | |
| role="listitem" | ||
| d="${circle.commands}" | ||
| transform="rotate(${circle.offset} ${this._viewBox / 2} ${this._viewBox / 2})"> | ||
| </path>`, | ||
| )} | ||
| </path> | ||
| ${ | ||
| this.showInlineNumbers | ||
| ? svg`<text | ||
| class="slice-number" | ||
| x="${this.#getTextPosition(circle).x}" | ||
| y="${this.#getTextPosition(circle).y}" | ||
| text-anchor="middle" | ||
| dominant-baseline="middle" | ||
| fill="white" | ||
| font-weight="bold" | ||
| font-size="${this.borderSize * 0.6}px" | ||
| pointer-events="none">${circle.number}</text>` | ||
| : '' | ||
| }`; | ||
|
|
||
| return circle.href ? svg`<a href="${circle.href}">${content}</a>` : content; | ||
| })} | ||
|
|
||
| `; | ||
| } | ||
|
|
||
| override render() { | ||
| return html` <div id="container" @mousemove=${this.#calculateDetailsBoxPosition}> | ||
| <svg viewBox="0 0 ${this._viewBox} ${this._viewBox}" role="list">${this.#renderCircles()}</svg> | ||
| <svg width="100%" height="100%" viewBox="0 0 ${this._viewBox} ${this._viewBox}" role="list"> | ||
| ${this.#renderCircles()} | ||
| </svg> | ||
| <div | ||
| id="details-box" | ||
| style="--pos-y: ${this._posY}px; --pos-x: ${this._posX}px; --umb-donut-detail-color: ${this._detailColor}"> | ||
| <div id="details-title"><uui-icon name="icon-record"></uui-icon>${this._detailName}</div> | ||
| <span>${this._detailAmount} ${this._detailKind}</span> | ||
| </div> | ||
| </div> | ||
| ${this.showDescription && this.description ? html`<p class="description">${this.description}</p>` : ''} | ||
| <slot @slotchange=${this.#printCircles} @slice-update=${this.#printCircles}></slot>`; | ||
| } | ||
|
|
||
|
|
@@ -292,7 +343,9 @@ export class UmbDonutChartElement extends LitElement { | |
|
|
||
| #container { | ||
| position: relative; | ||
| width: 200px; | ||
| width: 100%; | ||
| max-width: 200px; | ||
| aspect-ratio: 1; | ||
| } | ||
|
|
||
| #details-box { | ||
|
|
@@ -311,6 +364,7 @@ export class UmbDonutChartElement extends LitElement { | |
| transform: translate3d(var(--pos-x), var(--pos-y), 0); | ||
| transition: transform 0.2s cubic-bezier(0.02, 1.23, 0.79, 1.08); | ||
| transition: opacity 150ms linear; | ||
| pointer-events: none; | ||
| } | ||
|
|
||
| #details-box.show { | ||
|
|
@@ -328,6 +382,17 @@ export class UmbDonutChartElement extends LitElement { | |
| display: flex; | ||
| align-items: center; | ||
| } | ||
|
|
||
| .slice-number { | ||
| user-select: none; | ||
| } | ||
|
|
||
| .description { | ||
| text-align: center; | ||
| font-size: var(--uui-type-small-size); | ||
| color: var(--uui-color-text-alt); | ||
| margin: var(--uui-size-space-2) 0 0 0; | ||
| } | ||
| `, | ||
| ]; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
history.pushStateAPI expects a string for the URL parameter, not a URL object. While the URL object will be coerced to a string, it will include the full absolute URL (e.g.,https://example.com/path) rather than just the path portion.This differs from the previous implementation which passed
pathname + search + hash, and may cause issues with the history state.Recommend changing to: