Skip to content

Commit

Permalink
Upstream LM salience chips as <lit-text-chips> and tidy up CSS
Browse files Browse the repository at this point in the history
- <lit-text-chips> as version of <lit-token-chips> for running text.
- Clean up some redundant CSS names and rules in token_chips.css.

PiperOrigin-RevId: 616166852
  • Loading branch information
iftenney authored and LIT team committed Mar 15, 2024
1 parent fe5a705 commit 1ec8626
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 83 deletions.
88 changes: 70 additions & 18 deletions lit_nlp/client/elements/token_chips.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* TODO(b/329716900): consider CSS nesting to make this more readable */

.tokens-group {
position: relative;
}
Expand All @@ -11,6 +13,12 @@
.salient-token {
--token-outline-color: transparent;
/* color and background set individually vis styleMap */
--token-bg-color: transparent;
--token-text-color: black;

background-color: var(--token-bg-color);
color: var(--token-text-color);

font-family: 'Roboto';
border-radius: 2px;
/* TODO: switch to using outline: instead? */
Expand Down Expand Up @@ -44,7 +52,7 @@
--token-outline-color: var(--lit-neutral-900);
}

.tokens-holder-dense .salient-token {
.tokens-holder.dense .salient-token {
padding: 0;
margin: 0;
border: 0;
Expand All @@ -64,60 +72,104 @@
width: 1em;
}

.tokens-holder-dense .word-spacer {
.tokens-holder.dense .word-spacer {
width: 0.5em;
}

/* block mode */
.tokens-holder-display-block {
/**
* Styles for <lit-text-chips>
*/
.text-chips {
display: block;
font-size: 0; /* hack to get zero spacing between elements */
line-height: 22px;
}

.tokens-holder-display-block > * {
.text-chips > * {
/* TODO: set this for all modes? */
font-size: 13px; /* restore standard font size */
}

.tokens-holder-display-block .salient-token {
.text-chips:not(.dense) .salient-token:not(.selected) {
/* outline in non-dense mode */
--token-outline-color: var(--lit-neutral-300);
}

.text-chips .salient-token {
display: inline;
min-height: 1lh;
vertical-align: baseline;

padding: 3px 0; /* this controls the visible highlight area */
margin: 0;
margin-right: 4px;

/* use outline instead of border for more consistent spacing */
outline: 1px solid var(--token-outline-color);
border: 0;
}

.text-chips .salient-token span {
/* this controls the mouseover area, so there are no gaps */
/* or flickering when hovering over multiline tokens */
padding: 6px 0;
}

.text-chips .salient-token.selected {
--token-outline-color: var(--lit-mage-700);
outline: 2px solid var(--token-outline-color);
}

/**
* This is the ugliest matcher I've ever written but it seems to fix the mess
* that is element spacing in inline mode. In particular: with the way we use
* word-spacer, any token followed by one would get extra trailing whitespace,
* word-spacer, any token followed by one would get extra trailing whitespace,
* e.g. it would appear as |word | rather than |word|, making the highlighting
* awkward.
*
*
* It's possible to fix this by scrupulously removing whitespace from the HTML,
* so long as the <span> are direct siblings of the word spacer. But this all
* breaks down when they're nested inside <lit-tooltip> to provide the
* so long as the <span> are direct siblings of the word spacer. But this all
* breaks down when they're nested inside <lit-tooltip> to provide the
* mouseover.
*
* So, instead we need to add a negative margin equal to the width
*
* So, instead we need to add a negative margin equal to the width
* of a single space, only to those .salient-token elements that are followed
* by a .word-spacer. Of course, CSS provides only a next-sibling combinator
* (+), which would work to match the .word-spacer itself - but applying
* margin-left there does not have the desired effect (you just get twice the
* spacing). So, we hack it with the unofficial but well-supported :has()
* margin-left there does not have the desired effect (you just get twice the
* spacing). So, we hack it with the unofficial but well-supported :has()
* pseudo-class to match .salient-token that "has" a next-sibling .word-spacer.
*/
.tokens-holder-display-block.tokens-holder-dense .salient-token:has(+ .word-spacer) span {
*/
.text-chips.dense .salient-token:has(+ .word-spacer) span {
/* hack to remove extra whitespace. ugh. */
margin-right: -0.445ch;
}

.tokens-holder-display-block .word-spacer {
.text-chips.dense .salient-token {
padding: 3px 0; /* this controls the visible highlight area */
margin: 0; /* no extra spacing; determine only from line-height */
}

.text-chips.dense .salient-token.selected {
/* TODO see if we can get away from z-index here */
z-index: 1;
}

.text-chips .word-spacer {
display: inline;
vertical-align: baseline;
white-space: pre-wrap;
}

.tokens-holder-display-block lit-tooltip {
.text-chips lit-tooltip {
--anchor-display-mode: 'inline';
--tooltip-position-left: 0;
}

/* vertical dense mode */
.text-chips.vdense {
line-height: 16px;
}
.text-chips.vdense .salient-token {
padding: 1.5px 0; /* avoid highlight area overlapping across lines */
}
37 changes: 25 additions & 12 deletions lit_nlp/client/elements/token_chips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@ export class TokenChips extends LitElement {
* Dense mode, for less padding and smaller margins around each chip.
*/
@property({type: Boolean}) dense = false;
/**
* Block mode uses display: block and inline elements for chips, instead of
* a flex-row layout. This allows chips to flow across line breaks, behaving
* more like <span> elements and giving a much better experience for larger
* segments like sentences. However, this comes at the cost of more spacing
* artifacts and occasional issues with tooltip positioning.
*/
@property({type: Boolean}) displayBlock = false;
/**
* breakNewlines removes \n at the beginning or end of a segment and inserts
* explicit row break elements instead. Improves readability in many settings,
Expand Down Expand Up @@ -91,8 +83,8 @@ export class TokenChips extends LitElement {
'hover-enabled': !Boolean(tokenInfo.disableHover),
});
const tokenStyle = styleMap({
'background-color': this.cmap.bgCmap(tokenInfo.weight),
'color': this.cmap.textCmap(tokenInfo.weight),
'--token-bg-color': this.cmap.bgCmap(tokenInfo.weight),
'--token-text-color': this.cmap.textCmap(tokenInfo.weight),
});

let tokenText = tokenInfo.token;
Expand Down Expand Up @@ -159,8 +151,7 @@ export class TokenChips extends LitElement {
protected holderClass() {
return {
'tokens-holder': true,
'tokens-holder-dense': this.dense,
'tokens-holder-display-block': this.displayBlock,
'dense': this.dense,
};
}

Expand All @@ -180,8 +171,30 @@ export class TokenChips extends LitElement {
}
}

/**
* As above, but renders closer to running text - most notably by using
* display: block and inline elements instead of flexbox.
*
* <lit-text-chips dense> should give text that is nearly indistinguishable
* from that in a single div, while preserving all the click/hover/highlight
* functionality of <lit-token-chips>.
*/
@customElement('lit-text-chips')
export class TextChips extends TokenChips {
/**
* Vertical dense mode, only affects vertical spacing.
*/
@property({type: Boolean}) vDense = false;

override holderClass() {
return Object.assign(
{}, super.holderClass(), {'text-chips': true, 'vdense': this.vDense});
}
}

declare global {
interface HTMLElementTagNameMap {
'lit-token-chips': TokenChips;
'lit-text-chips': TextChips;
}
}
58 changes: 5 additions & 53 deletions lit_nlp/client/modules/lm_salience_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import '../elements/fused_button_bar';

import {css, html} from 'lit';
// tslint:disable:no-new-decorators
import {customElement, property} from 'lit/decorators.js';
import {customElement} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import {computed, observable} from 'mobx';

import {LitModule} from '../core/lit_module';
import {LegendType} from '../elements/color_legend';
import {NumericInput as LitNumericInput} from '../elements/numeric_input';
import {TokenChips, TokenWithWeight} from '../elements/token_chips';
import {TextChips, TokenChips, TokenWithWeight} from '../elements/token_chips';
import {CONTINUOUS_SIGNED_LAB, CONTINUOUS_UNSIGNED_LAB, SalienceCmap, SignedSalienceCmap, UnsignedSalienceCmap} from '../lib/colors';
import {GENERATION_TYPES, getAllTargetOptions, TargetOption, TargetSource} from '../lib/generated_text_utils';
import {LitType, LitTypeTypesList, Tokens, TokenScores} from '../lib/lit_types';
Expand Down Expand Up @@ -136,62 +136,14 @@ export class SingleExampleSingleModelModule extends LitModule {
* Custom styled version of <lit-token-chips> for rendering LM salience tokens.
*/
@customElement('lm-salience-chips')
class LMSalienceChips extends TokenChips {
class LMSalienceChips extends TextChips {
static override get styles() {
return [
...TokenChips.styles,
css`
.tokens-holder:not(.tokens-holder-dense) .salient-token:not(.selected) {
--token-outline-color: var(--lit-neutral-300); /* outline in non-dense mode */
}
.tokens-holder-display-block .salient-token {
padding: 3px 0; /* this controls the visible highlight area */
margin: 0;
margin-right: 4px;
/* use outline instead of border for more consistent spacing */
outline: 1px solid var(--token-outline-color);
border: 0;
}
.tokens-holder-display-block .salient-token span {
/* this controls the mouseover area, so there are no gaps */
/* or flickering when hovering over multiline tokens */
padding: 6px 0;
}
.salient-token.selected {
--token-outline-color: var(--lit-mage-700);
outline: 2px solid var(--token-outline-color);
}
.tokens-holder-dense .salient-token {
margin: 0; /* no extra spacing; determine only from line-height */
}
.tokens-holder-dense .salient-token.selected {
/* TODO see if we can get away from z-index here */
z-index: 1;
}
/* vertical dense mode */
.tokens-holder-vdense {
line-height: 16px;
}
.tokens-holder-vdense .salient-token {
padding: 1.5px 0; /* avoid highlight area overlapping across lines */
}
`,
];
}

/**
* Vertical dense mode, only affects vertical spacing.
*/
@property({type: Boolean}) vDense = false;

/**
* Custom style for tokens-holder, so we can implement vDense mode without
* adding clutter to token_chips.ts.
*/
override holderClass() {
return Object.assign(
{}, super.holderClass(), {'tokens-holder-vdense': this.vDense});
}
}

interface SalienceResults {
Expand Down Expand Up @@ -910,10 +862,10 @@ export class LMSalienceModule extends SingleExampleSingleModelModule {
// prettier-ignore
return html`
<div class='chip-container'>
<lm-salience-chips
<lm-salience-chips
.tokensWithWeights=${segmentsWithWeights} .cmap=${this.cmap}
?dense=${this.denseView} ?vDense=${this.vDense}
?preSpace=${this.denseView} breakNewlines displayBlock>
?preSpace=${this.denseView} breakNewlines>
</lm-salience-chips>
</div>
`;
Expand Down

0 comments on commit 1ec8626

Please sign in to comment.