Skip to content

Commit

Permalink
Merge branch 'feature/stencila-authorship-component-2206'
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed May 10, 2024
2 parents f8bc7d5 + d8f5a8d commit c9fbcb1
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 55 deletions.
27 changes: 26 additions & 1 deletion web/src/nodes/entity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { provide } from '@lit/context'
import { css } from '@twind/core'
import { html, LitElement } from 'lit'
import { property } from 'lit/decorators'
import { property, state } from 'lit/decorators'

import { DirectoryAction, directoryActionEvent } from '../clients/directory'
import { nodePatchEvent, NodePatch } from '../clients/nodes'
import { DocumentAccess, DocumentView, NodeId } from '../types'
import { EntityContext, entityContext } from '../ui/nodes/context'

/**
* Abstract base class for web components representing Stencila Schema `Entity` node types
Expand All @@ -26,6 +28,29 @@ export abstract class Entity extends LitElement {
@property({ type: String, attribute: 'active-child' })
activeChild: NodeId

@provide({ context: entityContext })
@state()
protected context: EntityContext = {
nodeId: this.id,
cardOpen: false,
}

override connectedCallback(): void {
super.connectedCallback()
this.shadowRoot.addEventListener(
`toggle-${this.id}`,
(e: Event & { detail: EntityContext }) => {
// only update the context for the relevant node
if (e.detail.nodeId === this.id) {
this.context = {
...this.context,
cardOpen: e.detail.cardOpen,
}
}
}
)
}

/**
* Select the closest element matching a selector
*
Expand Down
1 change: 1 addition & 0 deletions web/src/nodes/heading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { customElement, property } from 'lit/decorators'

import '../ui/nodes/card'
import '../ui/nodes/properties/authors'
import '../ui/nodes/properties/authorship'
import '../ui/nodes/properties/provenance/provenance'

import { Entity } from './entity'
Expand Down
1 change: 1 addition & 0 deletions web/src/nodes/instruction-inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class InstructionInline extends Instruction {
return html` <stencila-ui-inline-on-demand
type="InstructionInline"
view="dynamic"
node-id=${this.id}
>
<div slot="body">
<stencila-ui-node-execution-details
Expand Down
6 changes: 5 additions & 1 deletion web/src/nodes/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export abstract class Instruction extends Executable {
* with execution actions and details and code read-only and collapsed.
*/
override renderDynamicView() {
return html`<stencila-ui-block-on-demand type=${this.type} view="dynamic">
return html`<stencila-ui-block-on-demand
type=${this.type}
view="dynamic"
node-id=${this.id}
>
<span slot="header-right">
<stencila-ui-node-execution-commands
node-id=${this.id}
Expand Down
6 changes: 5 additions & 1 deletion web/src/nodes/paragraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ export class Paragraph extends Entity {
// TODO: Add summary stats to card

return html`
<stencila-ui-block-on-demand type="Paragraph" view="dynamic">
<stencila-ui-block-on-demand
type="Paragraph"
view="dynamic"
node-id=${this.id}
>
<div slot="body">
<stencila-ui-node-authors type="Paragraph">
<slot name="authors"></slot>
Expand Down
26 changes: 26 additions & 0 deletions web/src/ui/nodes/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createContext } from '@lit/context'

/**
* Context object to be used for the state of any Entity
*/
export interface EntityContext {
readonly nodeId?: string

/**
* Whether the entity's respective node card is open
*/
cardOpen?: boolean
}

/**
* Context object for state of Entity
*/
export const entityContext = createContext<EntityContext>('entity-node-card')

export interface InstructionContext extends EntityContext {
childEntitys: string[]
}

export const instructionContext = createContext<InstructionContext>(
'instruction-node-card'
)
14 changes: 7 additions & 7 deletions web/src/ui/nodes/icons-and-colours.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,16 @@ export const executionMessageUI = (

export const provenanceHighlights = {
0: 'transparent',
1: '#f5f7ff',
2: '#e4f0ff',
3: '#d3e3ff',
4: '#c2d6ff',
5: '#b1c9fa',
1: '#f1f5fe', // blue-50
2: '#cfdefc', // blue-100
3: '#b1c9fa', // blue-200
4: '#92b3f7', // blue-300
5: '#77a0f5', // blue-400
}

export type ProvenanceHighlightLvl = keyof typeof provenanceHighlights
export type ProvenanceHighlightLevel = keyof typeof provenanceHighlights

export const getProvHighlight = (miLvl: ProvenanceHighlightLvl) => {
export const getProvenanceHighlight = (miLvl: ProvenanceHighlightLevel) => {
return provenanceHighlights[miLvl]
}

Expand Down
13 changes: 11 additions & 2 deletions web/src/ui/nodes/mixins/toggle-chip.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { apply } from '@twind/core'
import { html, LitElement } from 'lit'
import { html } from 'lit'
import { state } from 'lit/decorators'

import { nodeUi } from '../icons-and-colours'

import { UIBaseClass } from './ui-base-class'

export declare class ChipToggleInterface {
protected renderChip: (icons: [string, string], colours: NodeColours) => void
protected toggle: boolean
Expand All @@ -19,7 +21,7 @@ type NodeColours = Pick<ReturnType<typeof nodeUi>, 'borderColour' | 'colour'>
* A Mixin that provides a "chip" to allow for a card to have its visibility
* toggled on and off.
*/
export const ToggleChipMixin = <T extends Constructor<LitElement>>(
export const ToggleChipMixin = <T extends Constructor<UIBaseClass>>(
superClass: T
) => {
class ToggleMixin extends superClass {
Expand All @@ -34,6 +36,13 @@ export const ToggleChipMixin = <T extends Constructor<LitElement>>(

protected toggleChip() {
this.toggle = !this.toggle
this.shadowRoot.dispatchEvent(
new CustomEvent(`toggle-${this.nodeId}`, {
bubbles: true,
composed: true,
detail: { cardOpen: this.toggle, nodeId: this.nodeId },
})
)
}

protected renderChip(icons: [string, string], colours: NodeColours) {
Expand Down
6 changes: 6 additions & 0 deletions web/src/ui/nodes/mixins/ui-base-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export class UIBaseClass extends LitElement {
@property()
type: NodeType

/**
* The `id` of the cards parent node element
*/
@property({ attribute: 'node-id' })
nodeId: string

/**
* The view that this card is within
*
Expand Down
23 changes: 18 additions & 5 deletions web/src/ui/nodes/node-card/on-demand/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,28 @@ export class UIBlockOnDemand extends ToggleChipMixin(UIBaseCard) {
this.toggle && 'p-3',
])

return html` <div class=${`${contentStyles}`}>
${this.renderChip(this.getIcon(), this.ui)}
<div class="content-block">
<slot name="content" class="w-full"></slot>
return html`
<div class=${`${contentStyles}`}>
${this.renderChip(this.getIcon(), this.ui)}
<div class="content-block">
<slot name="content" class="w-full"></slot>
</div>
</div>
</div>`
`
}

protected override toggleCardDisplay() {
this.toggle = !this.toggle

this.shadowRoot.dispatchEvent(
new CustomEvent(`toggle-${this.nodeId}`, {
bubbles: true,
composed: true,
detail: {
cardOpen: this.toggle,
nodeId: this.nodeId,
},
})
)
}
}
8 changes: 8 additions & 0 deletions web/src/ui/nodes/node-card/on-demand/in-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,13 @@ export class UIInlineOnDemand extends ToggleChipMixin(UIBaseCard) {

protected override toggleCardDisplay() {
this.toggle = !this.toggle

this.shadowRoot.dispatchEvent(
new CustomEvent(`toggle-${this.id}`, {
bubbles: true,
composed: true,
detail: { cardOpen: this.toggle, nodeId: this.nodeId },
})
)
}
}
90 changes: 67 additions & 23 deletions web/src/ui/nodes/properties/authorship.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,92 @@
import { apply } from '@twind/core'
import { consume } from '@lit/context'
import { ProvenanceCategory } from '@stencila/types'
import { apply, css } from '@twind/core'
import { LitElement, html } from 'lit'
import { property, customElement } from 'lit/decorators'
import { property, customElement, state } from 'lit/decorators'

import { withTwind } from '../../../twind'
import { ProvenanceHighlightLvl, getProvHighlight } from '../icons-and-colours'
import { entityContext, EntityContext } from '../context'
import {
ProvenanceHighlightLevel,
getProvenanceHighlight,
} from '../icons-and-colours'

/**
* Renders the author provenance highlighting of document text.
*/
@customElement('stencila-authorship')
@withTwind()
export class StencilaAuthorship extends LitElement {
@consume({ context: entityContext, subscribe: true })
@state()
context: EntityContext

/**
* Number of authors who have edited this content.
*/
@property()
count: number

/**
* A stringified array, containing the numeric value for each author.
*/
@property()
authors: string

/**
* provenance string
* one, or a combination of: 'Hw', 'He', 'Hv', 'Mw', 'Me', 'Mv'.
*/
@property()
provenance: string
provenance: ProvenanceCategory

/**
* 'Machine influence' rank,
* number from 0 (human only) to 5 (machine only).
*/
@property()
mi: number

override render() {
const bgColour = getProvHighlight(this.mi as ProvenanceHighlightLvl)

return html`
<sl-tooltip style="--show-delay: 1000ms;">
<div slot="content">
${this.renderTooltipContent('Author count', this.count)}
${this.renderTooltipContent('Provenance', this.provenance)}
</div>
<span style="background-color: ${bgColour}">
<slot></slot>
</span>
</sl-tooltip>
const bgColour = getProvenanceHighlight(this.mi as ProvenanceHighlightLevel)

const toolTipStyles = css`
&::part(body) {
color: #000000;
background-color: ${getProvenanceHighlight(2)};
}
&::part(base__arrow) {
background-color: ${getProvenanceHighlight(2)};
}
`

if (this.context.cardOpen) {
return html`
<sl-tooltip
style="--show-delay: 1000ms; background-color: white;"
placement="bottom-start"
class=${toolTipStyles}
>
<!-- tooltip content -->
<div slot="content">
${this.renderTooltipContent(
`${this.count} Author${this.count > 1 ? 's' : ''}`
)}
${this.renderTooltipContent(this.provenance)}
</div>
<!-- -->
<span style="background-color: ${bgColour};">
<slot></slot>
</span>
</sl-tooltip>
`
} else {
return html`<slot></slot>`
}
}

renderTooltipContent(property: string, value: string | number) {
renderTooltipContent(value: string | number) {
const styles = apply(['flex justify-between', 'text-sm'])
return html`
<div class=${styles}>
<span class="font-bold mr-2">${property}: </span>
<span>${value}</span>
</div>
`
return html` <div class=${styles}>${value}</div> `
}
}
19 changes: 12 additions & 7 deletions web/src/ui/nodes/properties/code/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { ExecutionMessage } from '../execution-message'
import { CodeAuthorElement, ProvenanceMarker } from './types'
import {
executionMessageLinter,
messagesTheme,
createProvenanceDecorations,
provenanceTooltip,
stencilaTheme,
} from './utils'

/**
Expand Down Expand Up @@ -161,17 +162,19 @@ export class UINodeCode extends LitElement {
EditorView.editable.of(!this.readOnly),
EditorState.readOnly.of(this.readOnly),
provenanceMarkers
? EditorView.decorations.of(
createProvenanceDecorations(provenanceMarkers)
)
? [
EditorView.decorations.of(
createProvenanceDecorations(provenanceMarkers)
),
provenanceTooltip(provenanceMarkers),
]
: [],
...languageExtension,
lineNumbers(),
foldGutter(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
executionMessages
? [executionMessageLinter(executionMessages), messagesTheme]
: [],
executionMessages ? executionMessageLinter(executionMessages) : [],
stencilaTheme,
]
}

Expand Down Expand Up @@ -254,6 +257,8 @@ export class UINodeCode extends LitElement {
from: tuple[0],
to: tuple[1],
mi: tuple[5],
count: tuple[2],
provenance: tuple[4],
})
})
return provenanceMarkers
Expand Down
Loading

0 comments on commit c9fbcb1

Please sign in to comment.