Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/tedi/components/form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from "./date-picker/date-picker.component";
export * from "./feedback-text/feedback-text.component";
export * from "./label-row";
export * from "./number-field/number-field.component";
export * from "./rating";
export * from "./search";
export * from "./select";
export * from "./slider/slider.component";
Expand Down
1 change: 1 addition & 0 deletions src/tedi/components/form/rating/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./rating.component";
114 changes: 114 additions & 0 deletions src/tedi/components/form/rating/rating.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
@if (readOnly()) {
<div class="tedi-rating__summary" aria-hidden="true">
@if (type() === "star" && readOnlyVariant() === "scale") {
<span class="tedi-rating__stars">
@for (fill of partialFills(); track $index) {
<span class="tedi-rating__star-partial">
<tedi-icon
class="tedi-rating__star"
[name]="starGlyph()"
color="inherit"
[size]="starIconSize"
/>
@if (fill > 0) {
<span
class="tedi-rating__star-partial-fill"
[style.--tedi-rating-star-fill]="fill + '%'"
>
<tedi-icon
class="tedi-rating__star"
[name]="starGlyph()"
variant="filled"
color="inherit"
[size]="starIconSize"
/>
</span>
}
</span>
}
</span>
} @else if (type() === "star") {
<tedi-icon
class="tedi-rating__star"
[name]="starGlyph()"
variant="filled"
color="inherit"
[size]="starIconSize"
/>
} @else if (type() === "icon") {
<span class="tedi-rating__circle tedi-rating__circle--filled">
<tedi-icon
[name]="readOnlyIcon()"
color="inherit"
[size]="circleIconSize"
/>
</span>
}
<span class="tedi-rating__summary-text">{{ summary() }}</span>
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} @else {
<div
class="tedi-rating__items"
[class.tedi-rating__items--captioned]="hasCaptions()"
(mouseleave)="setHover(null)"
>
@for (item of items(); track item.position) {
<label class="tedi-rating__item" (mouseenter)="setHover(item.position)">
<input
type="radio"
class="tedi-rating__input"
[name]="groupName()"
[value]="item.position"
[checked]="value() === item.position"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the component is used with [value] inside a native form, selecting 5 and resetting the form clears the checked radio, while five items remain visually selected. The form then submits no rating despite displaying a selection. Reset through Angular reactive forms works correctly.

[disabled]="isDisabled()"
[attr.aria-label]="item.accessibleName"
(change)="select(item.position)"
(blur)="handleBlur()"
/>
<span
class="tedi-rating__visual"
[class.tedi-rating__visual--selected]="item.selected"
[class.tedi-rating__visual--hover]="item.previewed"
aria-hidden="true"
>
@if (type() === "star") {
<!-- Filled copy behind the glyph; tinted only on hover so the outlined star gets a light fill. -->
<tedi-icon
class="tedi-rating__star-backdrop"
[name]="starGlyph()"
variant="filled"
color="inherit"
[size]="starIconSize"
/>
<tedi-icon
class="tedi-rating__star"
[name]="starGlyph()"
[variant]="item.selected ? 'filled' : 'outlined'"
color="inherit"
[size]="starIconSize"
/>
} @else {
<span class="tedi-rating__circle">
@if (type() === "number") {
{{ item.position }}
} @else {
<tedi-icon
[name]="item.icon"
color="inherit"
[size]="circleIconSize"
/>
}
</span>
}
</span>
@if (item.caption) {
<span class="tedi-rating__caption">{{ item.caption }}</span>
}
</label>
}
</div>

@if (starCaption() !== undefined) {
<div class="tedi-rating__star-caption">{{ starCaption() }}</div>
}
}
234 changes: 234 additions & 0 deletions src/tedi/components/form/rating/rating.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
@use "@tedi-design-system/core/bootstrap-utility/breakpoints";

.tedi-rating {
display: flex;
flex-direction: column;
gap: var(--layout-grid-gutters-04);
align-items: flex-start;
width: fit-content;
}

.tedi-rating__items {
display: flex;
gap: var(--feedback-star-inner-spacing);
align-items: flex-start;
}

.tedi-rating--number .tedi-rating__items,
.tedi-rating--icon .tedi-rating__items {
flex-wrap: wrap;
gap: var(--layout-grid-gutters-08);
}

.tedi-rating__item {
position: relative;
display: inline-flex;
flex-direction: column;
gap: var(--layout-grid-gutters-04);
align-items: center;
cursor: pointer;
}

.tedi-rating--disabled .tedi-rating__item {
cursor: not-allowed;
}

.tedi-rating--vertical .tedi-rating__items {
flex-direction: column;
align-items: stretch;
}

.tedi-rating--vertical .tedi-rating__item {
flex-direction: row;
gap: var(--layout-grid-gutters-08);
justify-content: flex-start;
}

.tedi-rating--vertical .tedi-rating__caption {
text-align: left;
}

.tedi-rating__input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
cursor: inherit;
opacity: 0;
}

.tedi-rating__visual {
position: relative;
display: inline-flex;
}

.tedi-rating .tedi-rating__star {
position: relative; // paint above the absolutely positioned backdrop
line-height: 1;
color: var(--general-text-brand);
text-align: center;
}

.tedi-rating--star .tedi-rating__visual {
padding: var(--feedback-star-inner-spacing);
}

// Filled glyph stacked under the outlined one; transparent unless the star is hovered while unselected.
.tedi-rating .tedi-rating__star-backdrop {
position: absolute;
inset: var(--feedback-star-inner-spacing);
color: transparent;
}

.tedi-rating__circle {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--button-sm-height);
height: var(--button-sm-height);
font-size: var(--body-small-regular-size);
color: var(--general-text-brand);
background-color: var(--general-surface-primary);
border: var(--tedi-borders-01) solid var(--general-border-secondary);
border-radius: 50%;
}

.tedi-rating__visual--selected .tedi-rating__circle,
.tedi-rating__circle--filled {
color: var(--general-text-white);
background-color: var(--general-surface-brand-primary);
border-color: var(--general-surface-brand-primary);
}

.tedi-rating:not(.tedi-rating--disabled) {

.tedi-rating__visual--hover,
.tedi-rating__item:hover>.tedi-rating__visual {
.tedi-rating__star {
color: var(--general-icon-brand-dark);
}

.tedi-rating__star-backdrop {
color: var(--general-surface-brand-quaternary);
}

.tedi-rating__circle {
color: var(--general-icon-brand-dark);
background-color: var(--general-surface-brand-quaternary);
border-color: var(--general-icon-brand-dark);
}

&.tedi-rating__visual--selected .tedi-rating__circle {
color: var(--general-text-white);
background-color: var(--general-icon-brand-dark);
border-color: var(--general-icon-brand-dark);
}
}
}

.tedi-rating__input:focus-visible+.tedi-rating__visual::after {
position: absolute;
inset: -3px;
pointer-events: none;
content: "";
border-radius: 50%;
box-shadow:
inset 0 0 0 2px var(--general-effect-colors-focus-outset-outer-shadow),
inset 0 0 0 3px var(--general-effect-colors-focus-outset-inner-shadow);
}

.tedi-rating__caption {
font-size: var(--body-extra-small-size);
line-height: var(--body-extra-small-line-height);
color: var(--general-text-secondary);
text-align: center;
}

.tedi-rating__star-caption {
align-self: stretch;
min-height: var(--body-extra-small-line-height);
font-size: var(--body-extra-small-size);
line-height: var(--body-extra-small-line-height);
color: var(--general-text-secondary);
text-align: center;
}

.tedi-rating--number:not(.tedi-rating--vertical) {
.tedi-rating__items--captioned {
row-gap: var(--layout-grid-gutters-24);
padding-bottom: var(--layout-grid-gutters-24);
}

.tedi-rating__caption {
position: absolute;
top: 100%;
left: 50%;
margin-top: var(--layout-grid-gutters-04);
white-space: nowrap;
transform: translateX(-50%);
}
}

.tedi-rating--disabled {
.tedi-rating__star {
color: var(--form-general-border-disabled);
}

.tedi-rating__visual--selected .tedi-rating__star {
color: var(--form-general-background-disabled);
}

.tedi-rating__circle {
color: var(--general-text-disabled);
background-color: transparent;
border-color: var(--form-general-border-disabled);
}

.tedi-rating__visual--selected .tedi-rating__circle {
background-color: var(--form-general-background-disabled);
}
}

.tedi-rating--readonly {
flex-direction: row;
align-items: center;
}

.tedi-rating__summary {
display: inline-flex;
gap: var(--layout-grid-gutters-08);
align-items: center;
}

.tedi-rating__stars {
display: inline-flex;
gap: var(--feedback-star-inner-spacing);
}

.tedi-rating__star-partial {
position: relative;
display: inline-flex;
}

.tedi-rating__star-partial-fill {
position: absolute;
inset-block: 0;
inset-inline-start: 0;
display: inline-flex;
width: var(--tedi-rating-star-fill);
overflow: hidden;
}

.tedi-rating__summary-text {
font-size: var(--heading-h6-size);
font-weight: var(--heading-h6-weight);
line-height: var(--heading-h6-line-height);
color: var(--general-text-secondary);
}

@include breakpoints.media-breakpoint-down(md) {
.tedi-rating--number:not(.tedi-rating--readonly, .tedi-rating--vertical) .tedi-rating__items {
column-gap: var(--layout-grid-gutters-04);
}
}
Loading
Loading