Styling Perspective Tree branches?

I want to be able to style the text on individual branches of a tree, for example I want to add a search/filter and highlight the text in the branches that contain the search term(s). I've tried adding a style key value into the branches and setting the colour, but no dice :roll_eyes:

1 Like

not to hard hard to select tree-items with css, but for dynamic css changes you'll need the markown component
*= is contains

.tree-item[data-label*="Item 2"] {
    background-color:red;
}

another way without markdown is to define all the paths... if the tree view isnt to dynamic (or you have time to define a lot of posibilites)

then you would just have to change the class on the tree

.psc-select0 .parent-node[data-item-path="0"] > .tree-row{
    background-color:red;
}

.psc-select1 .parent-node[data-item-path="1"] > .tree-row{
    background-color:red;
}
.psc-select1-0 .parent-node[data-item-path="1/0"] > .tree-row{
    background-color:red;
}

2 Likes

Hey Victor, I'm finally getting back to this and trying to use the Markdown component to add this CSS dynamically, but I'm not getting anywhere...

I'm practicing just trying to add a Label at this stage via a script, but not really sure how to fire it on load.

I've got this in my Markdown with HTML un-escaped:

<script>
// Create the label element
var label = document.createElement('label');
label.textContent = 'Hello';  // Set the text content

// Get a reference to an existing element where you want to insert the label
var targetElement = document.getElementById('hello');

// Append the label to the target element
targetElement.appendChild(label);
</script>

This script works if I run it in the devtools console, so I presume it's just not firing in the Markdown.

(If it's not obvious, this is really my first real foray into this stuff!!)

The code I got to work in the console to add the CSS is this:

const sheet = new CSSStyleSheet();
sheet.replaceSync('.tree-item[data-label*="job_dld"] {     background-color:green; }');
document.adoptedStyleSheets = [sheet]

But again, I don't know how to force it to trigger

Non javascript option for consideration, I was able to get something working by having a function that modifies the tree item's label to include a set number of leading spaces when their text contains the search string.

The problem this approach is that it requires you to 'rebuild' the tree's items every time. I don't know how well it will scale on large tree structures.

def recursiveSearchMarkExpand(items, key):
	"""
	Recursively searches through a tree structure and expands to nodes that contain the search key
	Injects a set number of spaces in front of labels for items with the search key so it can be selected using CSS selectors
	"""
	expandParent = False
	for item in items:
		if bool(re.search(key, item['label'].lower(), flags=re.IGNORECASE)):
			item['label'] = "   " + item['label'].strip()
			expandParent = True

		# Remove leading spaces left over from last search pass-
		else:
			item['label'] = item['label'].strip()

		if len(item['items']):
			item['items'], childHasKey = recursiveSearchHighlightExpand(item['items'], key)
			item['expanded'] = childHasKey
			# If any child has key, mark that the parent of this item needs to be expanded
			if childHasKey:
				expandParent = True

	return items, expandParent

I have the search box text bound to a view custom property with a change script that takes the current tree items and runs it through the above function and returns the modified items to place back into the tree.

I combine it with the following definition in the style sheet:

/* Highlights items in tree that match search text */ 
.psc-CUSTOM\/Tree\/Item *[data-label^="  "]{
	background-color: #FFE8CC;
}

and the end result is:

Tree Search Example

2 Likes

yeah scripts dont run on load like this, thats why i wrap my scripts in the onload of an img object

	code =  """<img style='display:none' src='/favicon.ico' onload=\"
		console.log('hey');				
	\"></img>""".replace("\n", "").replace("\t", "")
	return code

Do note that the added a css sheet will not disspear, when closing the view, and if you open a new view a second css sheet will be made... (i think)
Css sheets also can get cached, so this might turn out ugly after a while.

Its better to just add in a <style> object instead of a script (or the onload img) for css related injections. and use python to generate your style "dynamicaly"

styles=".iaDropdownCommon_options [data-label='YourLabel']:before { content: url('/system/images/Builtin/icons/16/check2.png');	}"
	code =  """<style>
		"""+styles+"""
	\"></style>""".replace("\n", "").replace("\t", "")
	return code
2 Likes

Hmm, I can get it working for the dropdown as you've got it, but can't get it working for the tree-item class:

def transform(self, value, quality, timestamp):
	styles="""
	.tree-item [data-label*='%s'] {
		background-color: #ff0000;
	}
	""" % value
	
	code = """<style>
		"""+styles+"""
	\"></style>""".replace("\n", "").replace("\t", "")
	return code

where value = 'UPDATE' coming from a text input component. I successfully used this in the script transform for the dropdown as well.

I'll keep plugging away...

Ok, rookie mistake and result of a lack of CSS syntax knowledge... I added a space between .tree-item and [data-label...]

Removing the space made it work. My bad.

def transform(self, value, quality, timestamp):
	styles="""
	.tree-item[data-label*='%s'] {
		background-color: #c1f0c9;
	}""" % value
	
	code = """<style>
		"""+styles+"""
	\"></style>""".replace("\n", "").replace("\t", "")
	return code

Thanks!! This is very cool

1 Like

Does this also expand the tree branch nodes/children? Or does it just apply the styling?

It only applies styling. Expanding the nodes would mean traversing the props.items and toggling the expanded prop via script. For my use-case, I have all nodes expanded though so no need, it'd be super useful though in other circumstances

Ok, CSS question now along the same vein. Maybe this should be a separate post... oh well.

I want to set the background colour of the whole parent-node class div that has a tree-row child which has a tree-item child where its data-label prop value is 'UPDATE', but I can't work out what the selector should be..

I've tried below but it seems to select a lot of the .parent-nodes despite them not containing the correct children and data-label.

.parent-node:has(.tree-row .tree-item[data-label="UPDATE"]){
    background-color: #ff0000;
}

E.g. below it should have only selected the items with the blue bar on the left.

E.g. I want to select the orange if it contains a tree-view class div with a child tree-item class div where the data-label equals "UPDATE".


Or, for red conditionals, select blue parent-node div:
image

HTML below, look for:
<------ WANT TO TARGET THIS ------<<<
and
<------ IF THIS = "UPDATE" ------<<<

HTML
<div class="parent-node"
     draggable="false"
     data-item-path="1/1">
	<div class="tree-alignment vert-alignment ia_treeComponent__alignmentGuide"
	     style="left: 32px; top: 30px; height: calc(100% - 30px);"/>
	<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--expanded">
		<div class="tree-item"
		     data-label="2024-03-26 15:31:01.837 - project.JOB_STS"
		     style="padding-left: 20px; height: 30px;">
			<svg viewBox="0 0 24 24"
			     class="expand-icon ia_treeComponent__expandIcon ia_treeComponent__expandIcon--expanded"
			     data-icon="material/arrow_drop_down">
				<g class="icon"
				   id="arrow_drop_down">
					<path d="M7 10l5 5 5-5z"/>
				</g>
			</svg>
			<div class="tree-item-label">
				<div class="label-wrapper label-wrapper-icon">
					<svg viewBox="0 0 24 24"
					     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--expanded"
					     data-icon="material/functions"
					     style="fill: rgb(43, 43, 43);">
						<g class="icon"
						   id="functions">
							<path d="M18 4H6v2l6.5 6L6 18v2h12v-3h-7l5-5-5-5h7z"/>
						</g>
					</svg>
				</div>
				<div class="label-wrapper label-wrapper-text">
					<div class="text-scroll">2024-03-26 15:31:01.837 - project.JOB_STS</div>
				</div>
			</div>
		</div>
	</div>
	<div class="parent-node" <------ WANT TO TARGET THIS ------<<<
	     draggable="false"
	     data-item-path="1/1/0">
		<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--expanded">
			<div class="tree-item"
			     data-label="UPDATE" <------ IF THIS = "UPDATE" ------<<<
			     style="padding-left: 40px; height: 30px;">
				<svg viewBox="0 0 24 24"
				     class="expand-icon ia_treeComponent__expandIcon ia_treeComponent__expandIcon--expanded"
				     data-icon="material/arrow_drop_down">
					<g class="icon"
					   id="arrow_drop_down">
						<path d="M7 10l5 5 5-5z"/>
					</g>
				</svg>
				<div class="tree-item-label">
					<div class="label-wrapper label-wrapper-icon">
						<svg viewBox="0 0 24 24"
						     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--expanded"
						     data-icon="material/folder_open">
							<g class="icon"
							   id="folder_open">
								<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/>
							</g>
						</svg>
					</div>
					<div class="label-wrapper label-wrapper-text">
						<div class="text-scroll">UPDATE</div>
					</div>
				</div>
			</div>
		</div>
		<div class="terminal-node"
		     data-item-path="1/1/0/0">
			<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--collapsed">
				<div class="tree-alignment terminal-alignment ia_treeComponent__alignmentGuide"
				     style="left: 72px;">
					<div class="cross-alignment ia_treeComponent__alignmentGuide"/>
				</div>
				<div class="tree-item"
				     data-label="goodResponse = True"
				     style="padding-left: 60px; height: 30px;">
					<svg viewBox="0 0 24 24"
					     class="terminal-expand-icon ia_treeComponent__terminalExpandIcon"
					     data-icon=""/>
					<div class="tree-item-label">
						<div class="label-wrapper label-wrapper-icon">
							<svg viewBox="0 0 24 24"
							     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--collapsed"
							     data-icon="material/stop">
								<g class="icon"
								   id="stop">
									<path d="M6 6h12v12H6z"/>
								</g>
							</svg>
						</div>
						<div class="label-wrapper label-wrapper-text">
							<div class="text-scroll">goodResponse = True</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		<div class="parent-node"
		     draggable="false"
		     data-item-path="1/1/0/1">
			<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--expanded">
				<div class="tree-item"
				     data-label="SYSTEM Response"
				     style="padding-left: 60px; height: 30px;">
					<svg viewBox="0 0 24 24"
					     class="expand-icon ia_treeComponent__expandIcon ia_treeComponent__expandIcon--expanded"
					     data-icon="material/arrow_drop_down">
						<g class="icon"
						   id="arrow_drop_down">
							<path d="M7 10l5 5 5-5z"/>
						</g>
					</svg>
					<div class="tree-item-label">
						<div class="label-wrapper label-wrapper-icon">
							<svg viewBox="0 0 24 24"
							     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--expanded"
							     data-icon="material/folder_open">
								<g class="icon"
								   id="folder_open">
									<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/>
								</g>
							</svg>
						</div>
						<div class="label-wrapper label-wrapper-text">
							<div class="text-scroll">SYSTEM Response</div>
						</div>
					</div>
				</div>
			</div>
			<div class="terminal-node"
			     data-item-path="1/1/0/1/0">
				<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--collapsed">
					<div class="tree-alignment terminal-alignment ia_treeComponent__alignmentGuide"
					     style="left: 92px;">
						<div class="cross-alignment ia_treeComponent__alignmentGuide"/>
					</div>
					<div class="tree-item"
					     data-label="statusMsg = None"
					     style="padding-left: 80px; height: 30px;">
						<svg viewBox="0 0 24 24"
						     class="terminal-expand-icon ia_treeComponent__terminalExpandIcon"
						     data-icon=""/>
						<div class="tree-item-label">
							<div class="label-wrapper label-wrapper-icon">
								<svg viewBox="0 0 24 24"
								     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--collapsed"
								     data-icon="material/stop">
									<g class="icon"
									   id="stop">
										<path d="M6 6h12v12H6z"/>
									</g>
								</svg>
							</div>
							<div class="label-wrapper label-wrapper-text">
								<div class="text-scroll">statusMsg = None</div>
							</div>
						</div>
					</div>
				</div>
			</div>
			<div class="terminal-node"
			     data-item-path="1/1/0/1/1">
				<div class="tree-row ia_treeComponent__node  ia_treeComponent__node--collapsed">
					<div class="tree-alignment terminal-alignment ia_treeComponent__alignmentGuide last-child"
					     style="left: 92px;">
						<div class="cross-alignment ia_treeComponent__alignmentGuide"/>
					</div>
					<div class="tree-item"
					     data-label="statusCode = 204"
					     style="padding-left: 80px; height: 30px;">
						<svg viewBox="0 0 24 24"
						     class="terminal-expand-icon ia_treeComponent__terminalExpandIcon"
						     data-icon=""/>
						<div class="tree-item-label">
							<div class="label-wrapper label-wrapper-icon">
								<svg viewBox="0 0 24 24"
								     class="node-icon ia_treeComponent__node__icon ia_treeComponent__node__icon--collapsed"
								     data-icon="material/stop">
									<g class="icon"
									   id="stop">
										<path d="M6 6h12v12H6z"/>
									</g>
								</svg>
							</div>
							<div class="label-wrapper label-wrapper-text">
								<div class="text-scroll">statusCode = 204</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</div>

Update:
This works! Not entirely sure why it needs the double .parent-node thouhg...

.parent-node .parent-node:has(.tree-row):has(.tree-item[data-label="UPDATE"]){
    background-color: #ff0000;
}

Although it selects too much... it selects the asdsad item as well
image

Prepending yet another .parent-node fixes it :face_exhaling:

.parent-node .parent-node .parent-node:has(.tree-row):has(.tree-item[data-label="UPDATE"]){
    background-color: #ff0000;
}

image

Update2:
Gemini is telling me that CSS isn't able to select the parent of a child with certain characteristics :frowning:

Dont use multiple parents, makes your css content dependend.
You can use > which means "directly followed by". To prevent higher lvl parents to be selected aswell

.parent-node:has(> .tree-row .tree-item[data-label="UPDATE"]){
    background-color: #ff0000;
}

:has is quite a new feature, the ai's probably dont know about.

3 Likes

Nice, that's what I was looking for but I didn't know it could be used inside of :has :slight_smile: I feel like i've only scratched the surface of CSS...
That works perfectly, Cheers!

2 Likes

(Somewhat) interestingly, ChatGPT4 very authoritatively says it's impossible, until reminded about :has, at which point it reluctantly spits out exactly what you have there, Victor. :man_shrugging:

1 Like

I found the same thing in Gemini, it said "nope, can't do it" (paraphrasing its essay) and then after Victor's post, I said "what about using > inside has?" and it sheepishly backpeddled :slight_smile:

Now, how did they get it to be sheepish ?!?

Maybe there is a kernel of general intelligence there. :grimacing:

4 Likes

Ok, so perhaps it wasn't exactly sheepish for a human, but for an AI I think it's as close as it will get! :smile:

1 Like

Yeah maybe it has a couple of sources of the new feature, but 90% of the data probbaly says its impossible. So it tends to just answer that.

Oh gpt knows when its last updated?:o

However, as of my last update in April 2023,

That's one of the hidden instructions it's given at the start of the chat, boilerplate stuff like
"You are ChatGPT. Your last update was in April 2023. You know just enough programming to be dangerous" etc.

4 Likes

Did you try asking it to sheepishly apologize ?

1 Like