Skip to content
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

WIP: replace maxLength by OverflowEllipsis #89

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
17 changes: 9 additions & 8 deletions css/shared/src/main/scala/Styles.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ object Styles extends StyleSheet.Inline {
gridAutoRows := "minmax(50px, 1fr)"
)

val overflowEllipsis = style(
/* BOTH of the following are required for text-overflow */
whiteSpace.nowrap,
overflow.hidden,
textOverflow := "ellipsis",
)

val dragFeedBackKf = keyframes(
(0 %%) -> style(boxShadow := "0px 0px 0px 0px rgba(0,0,0,1)"),
(100 %%) -> style(boxShadow := "0px 0px 0px 20px rgba(0,0,0,0)")
Expand Down Expand Up @@ -262,10 +269,7 @@ object CommonStyles extends StyleSheet.Standalone {
)

".breadcrumb .markdown *" - (
/* BOTH of the following are required for text-overflow */
whiteSpace.nowrap,
overflow.hidden,
textOverflow := "ellipsis",
Styles.overflowEllipsis
)

// first/last breadcrumb should not have any margin.
Expand Down Expand Up @@ -831,10 +835,7 @@ object CommonStyles extends StyleSheet.Standalone {
)

".tag .markdown *" - (
/* BOTH of the following are required for text-overflow */
whiteSpace.nowrap,
overflow.hidden,
textOverflow := "ellipsis",
Styles.overflowEllipsis
)

".tag.colorful a" - (
Expand Down
2 changes: 2 additions & 0 deletions util/shared/src/main/scala/StringOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import scala.util.control.NonFatal

object StringOps {

@deprecated("", "use text-overflow: ellipsis instead")
def trimToMaxLength(str: String, maxLength: Int): String = {
val rawString = str.trim
if(rawString.length > maxLength)
rawString.take(maxLength - 3) + "..."
else rawString
}
@deprecated("", "use text-overflow: ellipsis instead")
def trimToMaxLength(str: String, maxLength: Option[Int]): String = {
maxLength.fold(str)(trimToMaxLength(str, _))
}
Expand Down
4 changes: 2 additions & 2 deletions webApp/src/main/scala/views/ChatView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,10 @@ object ChatView {
case NodeRole.Task => VDomModifier(
Styles.flex,
"Task: ",
renderNodeData(node.data, maxLength = Some(100))
renderNodeData(node.data)
)
case _ => VDomModifier(
renderNodeData(node.data, maxLength = Some(100))
renderNodeData(node.data)
)
}
),
Expand Down
66 changes: 34 additions & 32 deletions webApp/src/main/scala/views/Components.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,21 @@ object Components {

def displayRelativeDate(data: NodeData.RelativeDate) = VDomModifier(span(color := "lightgray", "X + "), span(StringJsOps.durationToString(data.content)))

def renderNodeData(nodeData: NodeData, maxLength: Option[Int] = None): VNode = nodeData match {
case NodeData.Markdown(content) => markdownVNode(trimToMaxLength(content, maxLength))
case NodeData.PlainText(content) => div(trimToMaxLength(content, maxLength))
def renderNodeData(nodeData: NodeData): VNode = nodeData match {
case NodeData.Markdown(content) => markdownVNode(content)
case NodeData.PlainText(content) => div(content)
case user: NodeData.User => div(displayUserName(user))
case data: NodeData.RelativeDate => div(displayRelativeDate(data))
case d => div(trimToMaxLength(d.str, maxLength))
case d => div(d.str)
}

def renderNodeDataWithFile(state: GlobalState, nodeId: NodeId, nodeData: NodeData, maxLength: Option[Int] = None)(implicit ctx: Ctx.Owner): VNode = nodeData match {
case NodeData.Markdown(content) => markdownVNode(trimToMaxLength(content, maxLength))
case NodeData.PlainText(content) => div(trimToMaxLength(content, maxLength))
def renderNodeDataWithFile(state: GlobalState, nodeId: NodeId, nodeData: NodeData)(implicit ctx: Ctx.Owner): VNode = nodeData match {
case NodeData.Markdown(content) => markdownVNode(content)
case NodeData.PlainText(content) => div(content)
case user: NodeData.User => div(displayUserName(user))
case file: NodeData.File => renderUploadedFile(state, nodeId,file)
case data: NodeData.RelativeDate => div(displayRelativeDate(data))
case d => div(trimToMaxLength(d.str, maxLength))
case d => div(d.str)
}

def renderText(str: String): VNode = {
Expand Down Expand Up @@ -312,6 +312,7 @@ object Components {
flexWrap.wrap,
alignItems.flexStart,
b(
Styles.flexStatic,
Styles.flex,
EditableContent.inputInlineOrRender[String](key, editKey, key => span(key + ":")).editValue.collect { case newKey if newKey != key =>
GraphChanges(addEdges = properties.map(p => p.edge.copy(data = p.edge.data.copy(key = newKey)))(breakOut), delEdges = properties.map(_.edge)(breakOut)),
Expand All @@ -330,12 +331,11 @@ object Components {
div(
Styles.flex,
justifyContent.flexEnd,
margin := "3px 0px",

Elements.icon(ItemProperties.iconByNodeData(property.node.data))(marginRight := "5px"),
editablePropertyNode(state, property.node, property.edge, editMode = editValue,
nonPropertyModifier = VDomModifier(writeHoveredNode(state, property.node.id), cursor.pointer, onClick.stopPropagation(Some(FocusPreference(property.node.id))) --> state.rightSidebarNode),
maxLength = Some(100), config = EditableContent.Config.default,
config = EditableContent.Config.default,
),
div(
marginLeft := "5px",
Expand Down Expand Up @@ -377,6 +377,8 @@ object Components {

div(
alignSelf.flexStart,
Styles.flexStatic,

Styles.flex,
alignItems.center,
color.gray,
Expand All @@ -385,10 +387,10 @@ object Components {
),

property.role match {
case NodeRole.Neutral => renderNodeDataWithFile(state, property.id, property.data, maxLength = Some(50))
case NodeRole.Neutral => renderNodeDataWithFile(state, property.id, property.data)
case _ => VDomModifier(
writeHoveredNode(state, property.id),
nodeCardWithFile(state, property, maxLength = Some(50)).apply(
nodeCardWithFile(state, property).apply(
margin := "3px 0",
sidebarNodeFocusMod(state.rightSidebarNode, property.id),
cursor.pointer
Expand Down Expand Up @@ -533,44 +535,44 @@ object Components {
),
)
}
def nodeCard(node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None): VNode = {
def nodeCard(node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty): VNode = {
renderNodeCard(
node,
contentInject = VDomModifier(renderNodeData(node.data, maxLength).apply(nodeInject), contentInject)
contentInject = VDomModifier(renderNodeData(node.data).apply(nodeInject), contentInject)
)
}
def nodeCardWithFile(state: GlobalState, node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None)(implicit ctx: Ctx.Owner): VNode = {
def nodeCardWithFile(state: GlobalState, node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty)(implicit ctx: Ctx.Owner): VNode = {
renderNodeCard(
node,
contentInject = VDomModifier(renderNodeDataWithFile(state, node.id, node.data, maxLength).apply(nodeInject), contentInject)
contentInject = VDomModifier(renderNodeDataWithFile(state, node.id, node.data).apply(nodeInject), contentInject)
)
}
def nodeCardWithoutRender(node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None): VNode = {
def nodeCardWithoutRender(node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty): VNode = {
renderNodeCard(
node,
contentInject = VDomModifier(p(StringOps.trimToMaxLength(node.str, maxLength), nodeInject), contentInject)
contentInject = VDomModifier(p(node.str, nodeInject), contentInject)
)
}
def nodeCardEditable(state: GlobalState, node: Node, editMode: Var[Boolean], contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None, prependInject: VDomModifier = VDomModifier.empty)(implicit ctx: Ctx.Owner): VNode = {
def nodeCardEditable(state: GlobalState, node: Node, editMode: Var[Boolean], contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, prependInject: VDomModifier = VDomModifier.empty)(implicit ctx: Ctx.Owner): VNode = {
renderNodeCard(
node,
contentInject = VDomModifier(
prependInject,
editableNode(state, node, editMode, maxLength).apply(nodeInject),
editableNode(state, node, editMode).apply(nodeInject),
contentInject
),
).apply(
Rx { editMode().ifTrue[VDomModifier](VDomModifier(boxShadow := "0px 0px 0px 2px rgba(65,184,255, 1)")) },
)
}

def nodeCardEditableOnClick(state: GlobalState, node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None, prependInject: VDomModifier = VDomModifier.empty)(implicit ctx: Ctx.Owner): VNode = {
def nodeCardEditableOnClick(state: GlobalState, node: Node, contentInject: VDomModifier = VDomModifier.empty, nodeInject: VDomModifier = VDomModifier.empty, prependInject: VDomModifier = VDomModifier.empty)(implicit ctx: Ctx.Owner): VNode = {
val editMode = Var(false)
renderNodeCard(
node,
contentInject = VDomModifier(
prependInject,
editableNodeOnClick(state, node, maxLength).apply(nodeInject),
editableNodeOnClick(state, node).apply(nodeInject),
contentInject
),
).apply(
Expand Down Expand Up @@ -719,10 +721,10 @@ object Components {
)
}

def editableNodeOnClick(state: GlobalState, node: Node, maxLength: Option[Int] = None, editMode: Var[Boolean] = Var(false), config: EditableContent.Config = EditableContent.Config.cancelOnError)(
def editableNodeOnClick(state: GlobalState, node: Node, editMode: Var[Boolean] = Var(false), config: EditableContent.Config = EditableContent.Config.cancelOnError)(
implicit ctx: Ctx.Owner
): VNode = {
editableNode(state, node, editMode, maxLength, config)(ctx)(
editableNode(state, node, editMode, config)(ctx)(
onClick.stopPropagation foreach {
if(!editMode.now) {
editMode() = true
Expand All @@ -732,10 +734,10 @@ object Components {
)
}

def editablePropertyNodeOnClick(state: GlobalState, node: Node, edge: Edge.LabeledProperty, nonPropertyModifier: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None, editMode: Var[Boolean] = Var(false), config: EditableContent.Config = EditableContent.Config.cancelOnError)(
def editablePropertyNodeOnClick(state: GlobalState, node: Node, edge: Edge.LabeledProperty, nonPropertyModifier: VDomModifier = VDomModifier.empty, editMode: Var[Boolean] = Var(false), config: EditableContent.Config = EditableContent.Config.cancelOnError)(
implicit ctx: Ctx.Owner
): VNode = {
editablePropertyNode(state, node, edge, editMode, nonPropertyModifier, maxLength, config)(ctx)(
editablePropertyNode(state, node, edge, editMode, nonPropertyModifier, config)(ctx)(
onClick.stopPropagation foreach {
if(!editMode.now) {
editMode() = true
Expand All @@ -745,20 +747,20 @@ object Components {
)
}

def editableNode(state: GlobalState, node: Node, editMode: Var[Boolean], maxLength: Option[Int] = None, config: EditableContent.Config = EditableContent.Config.cancelOnError)(implicit ctx: Ctx.Owner): VNode = {
def editableNode(state: GlobalState, node: Node, editMode: Var[Boolean], config: EditableContent.Config = EditableContent.Config.cancelOnError)(implicit ctx: Ctx.Owner): VNode = {
div(
EditableContent.ofNodeOrRender(state, node, editMode, node => renderNodeDataWithFile(state, node.id, node.data, maxLength), config).editValue.map(GraphChanges.addNode) --> state.eventProcessor.changes,
EditableContent.ofNodeOrRender(state, node, editMode, node => renderNodeDataWithFile(state, node.id, node.data), config).editValue.map(GraphChanges.addNode) --> state.eventProcessor.changes,
)
}

def editablePropertyNode(state: GlobalState, node: Node, edge: Edge.LabeledProperty, editMode: Var[Boolean], nonPropertyModifier: VDomModifier = VDomModifier.empty, maxLength: Option[Int] = None, config: EditableContent.Config = EditableContent.Config.cancelOnError)(implicit ctx: Ctx.Owner): VNode = {
def editablePropertyNode(state: GlobalState, node: Node, edge: Edge.LabeledProperty, editMode: Var[Boolean], nonPropertyModifier: VDomModifier = VDomModifier.empty, config: EditableContent.Config = EditableContent.Config.cancelOnError)(implicit ctx: Ctx.Owner): VNode = {
div(
node.role match {
case NodeRole.Neutral => VDomModifier(
EditableContent.ofNodeOrRender(state, node, editMode, node => renderNodeDataWithFile(state, node.id, node.data, maxLength), config).editValue.map(GraphChanges.addNode) --> state.eventProcessor.changes
EditableContent.ofNodeOrRender(state, node, editMode, node => renderNodeDataWithFile(state, node.id, node.data), config).editValue.map(GraphChanges.addNode) --> state.eventProcessor.changes
)
case _ => VDomModifier(
EditableContent.customOrRender[Node](node, editMode, node => nodeCardWithFile(state, node, maxLength = maxLength).apply(Styles.wordWrap, nonPropertyModifier), handler => searchAndSelectNode(state, handler.collectHandler[Option[NodeId]] { case id => EditInteraction.fromOption(id.map(state.rawGraph.now.nodesById(_))) } { case EditInteraction.Input(v) => Some(v.id) }.transformObservable(_.prepend(Some(node.id)))), config).editValue.collect { case newNode if newNode.id != edge.propertyId =>
EditableContent.customOrRender[Node](node, editMode, node => nodeCardWithFile(state, node).apply(Styles.wordWrap, nonPropertyModifier), handler => searchAndSelectNode(state, handler.collectHandler[Option[NodeId]] { case id => EditInteraction.fromOption(id.map(state.rawGraph.now.nodesById(_))) } { case EditInteraction.Input(v) => Some(v.id) }.transformObservable(_.prepend(Some(node.id)))), config).editValue.collect { case newNode if newNode.id != edge.propertyId =>

GraphChanges(delEdges = Set(edge), addEdges = Set(edge.copy(propertyId = PropertyId(newNode.id))))
} --> state.eventProcessor.changes,
Expand All @@ -784,7 +786,7 @@ object Components {
span("Selected:", color.gray, margin := "0px 5px 0px 5px"),
state.graph.map { g =>
val node = g.nodesById(nodeId)
Components.nodeCardWithFile(state, node, maxLength = Some(100)).apply(Styles.wordWrap)
Components.nodeCardWithFile(state, node).apply(Styles.wordWrap)
}
)
case None => VDomModifier.empty
Expand Down
2 changes: 1 addition & 1 deletion webApp/src/main/scala/views/CreateNewPrompt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ object CreateNewPrompt {
Rx {
val nodes = childNodes().flatMap { id =>
state.graph().nodesByIdGet(id).map { node =>
nodeCard(node, contentInject = VDomModifier(Styles.flex, flexDirection.row, justifyContent.spaceBetween, span(freeSolid.faTimes, cursor.pointer, onClick.mapTo(childNodes.now.filterNot(_ == node.id)) --> childNodes)), maxLength = Some(20))
nodeCard(node, contentInject = VDomModifier(Styles.flex, flexDirection.row, justifyContent.spaceBetween, span(freeSolid.faTimes, cursor.pointer, onClick.mapTo(childNodes.now.filterNot(_ == node.id)) --> childNodes)))
}
}

Expand Down
2 changes: 1 addition & 1 deletion webApp/src/main/scala/views/GraphChangesAutomationUI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ object GraphChangesAutomationUI {
)({ templateNode =>
val propertySingle = PropertyData.Single(graph, graph.idToIdxOrThrow(templateNode.id))

Components.nodeCard(templateNode, maxLength = Some(100)).apply(
Components.nodeCard(templateNode).apply(
padding := "3px",
width := "200px",
div(
Expand Down
4 changes: 1 addition & 3 deletions webApp/src/main/scala/views/KanbanView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ object KanbanView {
}
}

private val maxLength = 300 // TODO: use text-overflow:ellipsis instead.
def apply(state: GlobalState, focusState: FocusState)(implicit ctx: Ctx.Owner): VNode = {

val activeAddCardFields = Var(Set.empty[List[NodeId]]) // until we use thunks, we have to track, which text fields are active, so they don't get lost when rerendering the whole kanban board
Expand Down Expand Up @@ -196,7 +195,7 @@ object KanbanView {
)(implicit ctx: Ctx.Owner): VNode = {

val editable = Var(false)
val columnTitle = editableNode(state, node, editable, maxLength = Some(maxLength))(ctx)(cls := "kanbancolumntitle")
val columnTitle = editableNode(state, node, editable)(ctx)(cls := "kanbancolumntitle")

val messageChildrenCount = Rx {
val graph = state.graph()
Expand Down Expand Up @@ -521,7 +520,6 @@ object KanbanView {

nodeCard(
node,
maxLength = Some(maxLength),
contentInject = VDomModifier(
VDomModifier.ifTrue(isDone)(textDecoration.lineThrough),
VDomModifier.ifTrue(inOneLine)(alignItems.flexStart, cardDescription, marginRight := "40px"), // marginRight to not interfere with button bar...
Expand Down
6 changes: 3 additions & 3 deletions webApp/src/main/scala/views/RightSidebar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ object RightSidebar {

VDomModifier(
div(
marginTop := "10px",
margin := "10px",
propertySingle.properties.map { property =>
Components.removablePropertySection(state, property.key, property.values).apply(
marginBottom := "10px",
marginBottom := "5px",
)
},

Expand All @@ -282,7 +282,7 @@ object RightSidebar {
fontSize.small,
span("Backlinks: ", color.gray),
propertySingle.info.reverseProperties.map { node =>
Components.nodeCard(node, maxLength = Some(50)).apply(
Components.nodeCard(node).apply(
margin := "3px",
Components.sidebarNodeFocusMod(state.rightSidebarNode, node.id)
)
Expand Down
4 changes: 1 addition & 3 deletions webApp/src/main/scala/views/SelectedNodes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,7 @@ object SelectedNodes {
selectedNodes.update(_.filterNot(data => data.nodeId == node.id))
}
),
),
maxLength = Some(20)
)(
))(
drag(DragItem.SelectedNode(node.id)),
cls := "draggable"
)
Expand Down
2 changes: 1 addition & 1 deletion webApp/src/main/scala/views/SharedViewElements.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ object SharedViewElements {

def render(node: Node, isDeletedNow: Boolean)(implicit ctx: Ctx.Owner) = {
if(isDeletedNow)
nodeCardWithoutRender(node, maxLength = Some(25)).apply(cls := "node-deleted")
nodeCardWithoutRender(node).apply(cls := "node-deleted")
else {
node.role match {
case NodeRole.Task =>
Expand Down
6 changes: 4 additions & 2 deletions webApp/src/main/scala/views/TableView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@ object TableView {
}.mkString(", "),
value = VDomModifier(
edges.map {
case (Some(edge), node: Node.Content) => Components.editablePropertyNodeOnClick(state, node, edge, maxLength = Some(50), config = EditableContent.Config.default)
case (Some(edge), node: Node.Content) => Components.editablePropertyNodeOnClick(state, node, edge, config = EditableContent.Config.default)
case (_, tag: Node.Content) if tag.role == NodeRole.Tag => Components.removableNodeTag(state, tag, row)
case (_, stage: Node.Content) if stage.role == NodeRole.Stage => Components.removableNodeTag(state, stage, row)
case (_, node: Node.Content) => Components.editableNodeOnClick(state, node, maxLength = Some(50), config = EditableContent.Config.default)
case (_, node: Node.Content) => Components.editableNodeOnClick(state, node, config = EditableContent.Config.default)
case (_, user: Node.User) => Components.removableAssignedUser(state, user, row)
},
maxWidth := "300px",
Styles.wordWrap,
cellModifier
)
)
Expand Down