mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-01 17:47:07 +00:00
Merge pull request #390 from AtkinsSJ/task-manager-components
refactor: Convert Task Manager to Components
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
|
||||
/**
|
||||
* A table with a sticky header
|
||||
*/
|
||||
export default class Table extends Component {
|
||||
static PROPERTIES = {
|
||||
headings: { value: [] },
|
||||
scale: { value: '2pt' },
|
||||
rows: { value: [] },
|
||||
}
|
||||
|
||||
static CSS = /*css*/`
|
||||
table {
|
||||
box-sizing: border-box;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
thead th {
|
||||
box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
|
||||
backdrop-filter: blur(2px);
|
||||
position: sticky;
|
||||
z-index: 100;
|
||||
padding:
|
||||
calc(10 * var(--scale))
|
||||
calc(2.5 * var(--scale))
|
||||
calc(5 * var(--scale))
|
||||
calc(2.5 * var(--scale));
|
||||
top: 0;
|
||||
background-color: hsla(0, 0%, 100%, 0.8);
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
thead th:not(:last-of-type) {
|
||||
/* we set borders on this span because */
|
||||
/* borders fly away from sticky headers */
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
tbody > * {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 0 calc(2.5 * var(--scale));
|
||||
vertical-align: middle;
|
||||
}
|
||||
`;
|
||||
|
||||
create_template ({ template }) {
|
||||
$(template).html(`
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="headings"></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<slot name="rows"></slot>
|
||||
</tbody>
|
||||
</table>
|
||||
`);
|
||||
}
|
||||
|
||||
on_ready ({ listen }) {
|
||||
listen('headings', headings => {
|
||||
$(this.dom_).find('.headings')
|
||||
.html(headings.map(heading => `<th><span>${heading}</span></th>`).join(''))
|
||||
});
|
||||
|
||||
listen('scale', scale => {
|
||||
$(this.dom_).css('--scale', scale);
|
||||
});
|
||||
|
||||
listen('rows', rows => {
|
||||
const tbody = $(this.dom_).find('tbody')[0];
|
||||
$(tbody).find('[slot=rows]').detach();
|
||||
for (const row of rows) {
|
||||
row.setAttribute('slot', 'rows');
|
||||
row.attach(tbody);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-table', Table);
|
||||
+312
-214
@@ -2,11 +2,311 @@ import { END_HARD, END_SOFT } from "../definitions.js";
|
||||
import UIAlert from "./UIAlert.js";
|
||||
import UIContextMenu from "./UIContextMenu.js";
|
||||
import UIWindow from "./UIWindow.js";
|
||||
import { Component, defineComponent } from '../util/Component.js';
|
||||
import UIComponentWindow from './UIComponentWindow.js';
|
||||
import Table from './Components/Table.js';
|
||||
import Placeholder from '../util/Placeholder.js';
|
||||
import TestView from './Components/TestView.js';
|
||||
|
||||
const end_process = async (uuid, force) => {
|
||||
const svc_process = globalThis.services.get('process');
|
||||
const process = svc_process.get_by_uuid(uuid);
|
||||
if (!process) {
|
||||
console.warn(`Can't end process with uuid='${uuid}': does not exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
let confirmation;
|
||||
if ( process.is_init() ) {
|
||||
if ( ! force ) {
|
||||
confirmation = i18n('close_all_windows_confirm');
|
||||
} else {
|
||||
confirmation = i18n('restart_puter_confirm');
|
||||
}
|
||||
} else if ( force ) {
|
||||
confirmation = i18n('end_process_force_confirm');
|
||||
}
|
||||
|
||||
if ( confirmation ) {
|
||||
const alert_resp = await UIAlert({
|
||||
message: confirmation,
|
||||
buttons:[
|
||||
{
|
||||
label: i18n('yes'),
|
||||
value: true,
|
||||
type: 'primary',
|
||||
},
|
||||
{
|
||||
label: i18n('no'),
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
})
|
||||
if ( ! alert_resp ) return;
|
||||
}
|
||||
|
||||
process.signal(force ? END_HARD : END_SOFT);
|
||||
};
|
||||
|
||||
class TaskManagerTable extends Component {
|
||||
static PROPERTIES = {
|
||||
tasks: { value: [] },
|
||||
};
|
||||
|
||||
static CSS = /*css*/`
|
||||
:host {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: rgba(255,255,255,0.8);
|
||||
border: 2px inset rgba(127, 127, 127, 0.3);
|
||||
overflow: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
#svc_process = globalThis.services.get('process');
|
||||
|
||||
create_template ({ template }) {
|
||||
$(template).html(`
|
||||
<div class="taskmgr-taskarea"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
on_ready ({ listen }) {
|
||||
this.table = new Table({
|
||||
headings: [
|
||||
i18n('taskmgr_header_name'),
|
||||
i18n('taskmgr_header_type'),
|
||||
i18n('taskmgr_header_status'),
|
||||
]
|
||||
});
|
||||
this.table.attach(this.dom_.querySelector('.taskmgr-taskarea'));
|
||||
|
||||
listen('tasks', tasks => {
|
||||
const row_data = this.#iter_tasks(tasks, { indent_level: 0, is_last_item_stack: [] });
|
||||
const new_uuids = row_data.map(it => it.uuid);
|
||||
|
||||
const old_rows = this.table.get('rows');
|
||||
|
||||
const rows = [];
|
||||
for (const data of row_data) {
|
||||
// Try to reuse old row
|
||||
const old_row = old_rows.find(it => data.uuid === it.get('uuid'));
|
||||
if (old_row) {
|
||||
for (const property in data) {
|
||||
old_row.set(property, data[property]);
|
||||
}
|
||||
rows.push(old_row);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a new row
|
||||
rows.push(new TaskManagerRow(data));
|
||||
}
|
||||
|
||||
this.table.set('rows', rows);
|
||||
});
|
||||
}
|
||||
|
||||
#calculate_indent_string (indent_level, is_last_item_stack, is_last_item) {
|
||||
// Returns a string of '| ├└'
|
||||
let result = '';
|
||||
|
||||
for ( let i=0; i < indent_level; i++ ) {
|
||||
const last_cell = i === indent_level - 1;
|
||||
const has_trunk = (last_cell && ( ! is_last_item )) ||
|
||||
(!last_cell && !is_last_item_stack[i+1]);
|
||||
const has_branch = last_cell;
|
||||
|
||||
if (has_trunk && has_branch) {
|
||||
result += '├';
|
||||
} else if (has_trunk) {
|
||||
result += '|';
|
||||
} else if (has_branch) {
|
||||
result += '└';
|
||||
} else {
|
||||
result += ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#iter_tasks (items, { indent_level, is_last_item_stack }) {
|
||||
const rows = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const is_last_item = i === items.length - 1;
|
||||
rows.push({
|
||||
name: item.name,
|
||||
uuid: item.uuid,
|
||||
process_type: item.type,
|
||||
process_status: item.status.i18n_key,
|
||||
indentation: this.#calculate_indent_string(indent_level, is_last_item_stack, is_last_item),
|
||||
});
|
||||
|
||||
const children = this.#svc_process.get_children_of(item.uuid);
|
||||
if (children) {
|
||||
rows.push(...this.#iter_tasks(children, {
|
||||
indent_level: indent_level + 1,
|
||||
is_last_item_stack:
|
||||
[ ...is_last_item_stack, is_last_item ],
|
||||
}));
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
}
|
||||
defineComponent('c-task-manager-table', TaskManagerTable);
|
||||
|
||||
class TaskManagerRow extends Component {
|
||||
static PROPERTIES = {
|
||||
name: {},
|
||||
uuid: {},
|
||||
process_type: {},
|
||||
process_status: {},
|
||||
indentation: { value: '' },
|
||||
};
|
||||
|
||||
static CSS = /*css*/`
|
||||
:host {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
td > span {
|
||||
padding: 0 calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
.task {
|
||||
display: flex;
|
||||
height: calc(10 * var(--scale));
|
||||
line-height: calc(10 * var(--scale));
|
||||
}
|
||||
|
||||
.task-name {
|
||||
flex-grow: 1;
|
||||
padding-left: calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
.task-indentation {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.indentcell {
|
||||
position: relative;
|
||||
align-items: right;
|
||||
width: calc(10 * var(--scale));
|
||||
height: calc(10 * var(--scale));
|
||||
}
|
||||
|
||||
.indentcell-trunk {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(5 * var(--scale));
|
||||
width: calc(5 * var(--scale));
|
||||
height: calc(10 * var(--scale));
|
||||
border-left: 2px solid var(--line-color);
|
||||
}
|
||||
|
||||
.indentcell-branch {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(5 * var(--scale));
|
||||
width: calc(5 * var(--scale));
|
||||
height: calc(5 * var(--scale));
|
||||
border-left: 2px solid var(--line-color);
|
||||
border-bottom: 2px solid var(--line-color);
|
||||
border-radius: 0 0 0 calc(2.5 * var(--scale));
|
||||
}
|
||||
`;
|
||||
|
||||
create_template ({ template }) {
|
||||
template.innerHTML = `
|
||||
<td>
|
||||
<div class="task">
|
||||
<div class="task-indentation"></div>
|
||||
<div class="task-name"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="process-type"></span></td>
|
||||
<td><span class="process-status"></span></td>
|
||||
`;
|
||||
}
|
||||
|
||||
on_ready ({ listen }) {
|
||||
listen('name', name => {
|
||||
$(this.dom_).find('.task-name').text(name);
|
||||
});
|
||||
listen('uuid', uuid => {
|
||||
this.setAttribute('data-uuid', uuid);
|
||||
});
|
||||
listen('process_type', type => {
|
||||
$(this.dom_).find('.process-type').text(i18n('process_type_' + type));
|
||||
});
|
||||
listen('process_status', status => {
|
||||
$(this.dom_).find('.process-status').text(i18n('process_status_' + status));
|
||||
});
|
||||
listen('indentation', indentation => {
|
||||
const el = $(this.dom_).find('.task-indentation');
|
||||
let h = '';
|
||||
for (const c of indentation) {
|
||||
h += `<div class="indentcell">`;
|
||||
switch (c) {
|
||||
case ' ':
|
||||
break;
|
||||
case '|':
|
||||
h += `<div class="indentcell-trunk"></div>`;
|
||||
break;
|
||||
case '└':
|
||||
h += `<div class="indentcell-branch"></div>`;
|
||||
break;
|
||||
case '├':
|
||||
h += `<div class="indentcell-trunk"></div>`;
|
||||
h += `<div class="indentcell-branch"></div>`;
|
||||
break;
|
||||
}
|
||||
h += `</div>`;
|
||||
}
|
||||
el.html(h);
|
||||
});
|
||||
|
||||
$(this).on('contextmenu', () => {
|
||||
const uuid = this.get('uuid');
|
||||
UIContextMenu({
|
||||
items: [
|
||||
{
|
||||
html: i18n('close'),
|
||||
onClick: () => {
|
||||
end_process(uuid);
|
||||
},
|
||||
},
|
||||
{
|
||||
html: i18n('force_quit'),
|
||||
onClick: () => {
|
||||
end_process(uuid, true);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
defineComponent('c-task-manager-row', TaskManagerRow);
|
||||
|
||||
const UIWindowTaskManager = async function UIWindowTaskManager () {
|
||||
const svc_process = globalThis.services.get('process');
|
||||
|
||||
const w = await UIWindow({
|
||||
let task_manager_table = new TaskManagerTable({
|
||||
tasks: [svc_process.get_init()],
|
||||
});
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const processes = [svc_process.get_init()];
|
||||
task_manager_table.set('tasks', processes);
|
||||
}, 500);
|
||||
|
||||
const w = await UIComponentWindow({
|
||||
component: task_manager_table,
|
||||
title: i18n('task_manager'),
|
||||
icon: globalThis.icons['cog.svg'],
|
||||
uid: null,
|
||||
@@ -40,220 +340,18 @@ const UIWindowTaskManager = async function UIWindowTaskManager () {
|
||||
var(--primary-lightness),
|
||||
var(--primary-alpha))`,
|
||||
'backdrop-filter': 'blur(3px)',
|
||||
|
||||
}
|
||||
'box-sizing': 'border-box',
|
||||
// could have been avoided with box-sizing: border-box
|
||||
height: 'calc(100% - 30px)',
|
||||
display: 'flex',
|
||||
'flex-direction': 'column',
|
||||
'--scale': '2pt',
|
||||
'--line-color': '#6e6e6ebd',
|
||||
},
|
||||
on_close: () => {
|
||||
clearInterval(interval);
|
||||
},
|
||||
});
|
||||
const w_body = w.querySelector('.window-body');
|
||||
w_body.classList.add('taskmgr');
|
||||
|
||||
const Indent = ({ has_trunk, has_branch }) => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('taskmgr-indentcell');
|
||||
if ( has_trunk ) {
|
||||
// Add new child element
|
||||
const el_indentcell_child = document.createElement('div');
|
||||
el_indentcell_child.classList.add('taskmgr-indentcell-trunk');
|
||||
el.appendChild(el_indentcell_child);
|
||||
}
|
||||
if ( has_branch ) {
|
||||
const el_indentcell_child = document.createElement('div');
|
||||
el_indentcell_child.classList.add('taskmgr-indentcell-branch');
|
||||
el.appendChild(el_indentcell_child);
|
||||
}
|
||||
|
||||
return {
|
||||
appendTo (parent) {
|
||||
parent.appendChild(el);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const Task = ({ placement, name }) => {
|
||||
const {
|
||||
indent_level, last_item,
|
||||
parent_last_item,
|
||||
} = placement;
|
||||
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('taskmgr-task');
|
||||
|
||||
for ( let i=0; i < indent_level; i++ ) {
|
||||
const last_cell = i === indent_level - 1;
|
||||
Indent({
|
||||
has_trunk: (last_cell && ( ! last_item )) ||
|
||||
(!last_cell && !parent_last_item[i+1]),
|
||||
has_branch: last_cell
|
||||
}).appendTo(el);
|
||||
}
|
||||
|
||||
const el_title = document.createElement('div');
|
||||
el_title.classList.add('taskmgr-task-title');
|
||||
el_title.innerText = name;
|
||||
el.appendChild(el_title);
|
||||
|
||||
return {
|
||||
el () { return el; },
|
||||
appendTo (parent) {
|
||||
parent.appendChild(el);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://codepen.io/fomkin/pen/gOgoBVy
|
||||
const Table = ({ headings }) => {
|
||||
const el_table = $(`
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
${headings.map(heading =>
|
||||
`<th><span>${heading}<span></th>`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
`)[0];
|
||||
|
||||
const el_tbody = el_table.querySelector('tbody');
|
||||
|
||||
return {
|
||||
el () { return el_table; },
|
||||
add (el) {
|
||||
if ( typeof el.el === 'function' ) el = el.el();
|
||||
el_tbody.appendChild(el);
|
||||
return this;
|
||||
},
|
||||
clear () {
|
||||
el_tbody.innerHTML = '';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const Row = () => {
|
||||
const el_tr = document.createElement('tr');
|
||||
return {
|
||||
attach (parent) {
|
||||
parent.appendChild(el_tr);
|
||||
return this;
|
||||
},
|
||||
el () { return el_tr; },
|
||||
add (el) {
|
||||
if ( typeof el.el === 'function' ) el = el.el();
|
||||
const el_td = document.createElement('td');
|
||||
el_td.appendChild(el);
|
||||
el_tr.appendChild(el_td);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const el_taskarea = document.createElement('div');
|
||||
el_taskarea.classList.add('taskmgr-taskarea');
|
||||
|
||||
const tasktable = Table({
|
||||
headings: [
|
||||
i18n('taskmgr_header_name'),
|
||||
i18n('taskmgr_header_type'),
|
||||
i18n('taskmgr_header_status'),
|
||||
]
|
||||
});
|
||||
|
||||
el_taskarea.appendChild(tasktable.el());
|
||||
|
||||
const end_process_ = async (process, force) => {
|
||||
let confirmation;
|
||||
|
||||
if ( process.is_init() ) {
|
||||
if ( ! force ) {
|
||||
confirmation = i18n('close_all_windows_confirm');
|
||||
} else {
|
||||
confirmation = i18n('restart_puter_confirm');
|
||||
}
|
||||
} else if ( force ) {
|
||||
confirmation = i18n('end_process_force_confirm');
|
||||
}
|
||||
|
||||
if ( confirmation ) {
|
||||
const alert_resp = await UIAlert({
|
||||
message: confirmation,
|
||||
buttons:[
|
||||
{
|
||||
label: i18n('yes'),
|
||||
value: true,
|
||||
type: 'primary',
|
||||
},
|
||||
{
|
||||
label: i18n('no'),
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
})
|
||||
if ( ! alert_resp ) return;
|
||||
}
|
||||
|
||||
process.signal(force ? END_HARD : END_SOFT);
|
||||
}
|
||||
|
||||
const iter_tasks = (items, { indent_level, parent_last_item }) => {
|
||||
for ( let i=0 ; i < items.length; i++ ) {
|
||||
const row = Row();
|
||||
const item = items[i];
|
||||
const last_item = i === items.length - 1;
|
||||
row.add(Task({
|
||||
placement: {
|
||||
parent_last_item,
|
||||
indent_level,
|
||||
last_item,
|
||||
},
|
||||
name: item.name
|
||||
}));
|
||||
row.add($(`<span>${i18n('process_type_' + item.type)}</span>`)[0])
|
||||
row.add($(`<span>${i18n('process_status_' + item.status.i18n_key)}</span>`)[0])
|
||||
tasktable.add(row);
|
||||
|
||||
$(row.el()).on('contextmenu', () => {
|
||||
UIContextMenu({
|
||||
parent_element: $(el_taskarea),
|
||||
items: [
|
||||
{
|
||||
html: i18n('close'),
|
||||
onClick: () => {
|
||||
end_process_(item);
|
||||
}
|
||||
},
|
||||
{
|
||||
html: i18n('force_quit'),
|
||||
onClick: () => {
|
||||
end_process_(item, true);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
|
||||
const children = svc_process.get_children_of(item.uuid);
|
||||
if ( children ) {
|
||||
iter_tasks(children, {
|
||||
indent_level: indent_level + 1,
|
||||
parent_last_item:
|
||||
[...parent_last_item, last_item],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(() => {
|
||||
tasktable.clear();
|
||||
const processes = [svc_process.get_init()];
|
||||
iter_tasks(processes, { indent_level: 0, parent_last_item: [] });
|
||||
}, 500)
|
||||
|
||||
w.on_close = () => {
|
||||
clearInterval(interval);
|
||||
}
|
||||
|
||||
w_body.appendChild(el_taskarea);
|
||||
}
|
||||
|
||||
export default UIWindowTaskManager;
|
||||
|
||||
@@ -3830,113 +3830,3 @@ fieldset[name=number-code] {
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.taskmgr {
|
||||
box-sizing: border-box;
|
||||
/* could have been avoided with box-sizing: border-box */
|
||||
height: calc(100% - 30px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
--scale: 2pt;
|
||||
--line-color: #6e6e6ebd;
|
||||
}
|
||||
|
||||
.taskmgr * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.taskmgr table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.taskmgr-taskarea {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: rgba(255,255,255,0.8);
|
||||
border: 2px inset rgba(127, 127, 127, 0.3);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table thead {
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table th {
|
||||
-webkit-box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
|
||||
box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
|
||||
backdrop-filter: blur(2px);
|
||||
position: sticky;
|
||||
z-index: 100;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
background-color: hsla(0, 0%, 100%, 0.8);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table th > span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
/* we set borders on this span because */
|
||||
/* borders fly away from sticky headers */
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
|
||||
/* padding order: top right bottom left */
|
||||
padding:
|
||||
calc(10 * var(--scale))
|
||||
calc(2.5 * var(--scale))
|
||||
calc(5 * var(--scale))
|
||||
calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table th:not(:last-of-type) > span {
|
||||
/* we set borders on this span because */
|
||||
/* borders fly away from sticky headers */
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table td {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.taskmgr-taskarea table td > span {
|
||||
padding: 0 calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
.taskmgr-indentcell {
|
||||
position: relative;
|
||||
align-items: right;
|
||||
width: calc(10 * var(--scale));
|
||||
height: calc(10 * var(--scale));
|
||||
}
|
||||
|
||||
.taskmgr-indentcell-trunk {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(5 * var(--scale));
|
||||
width: calc(5 * var(--scale));
|
||||
height: calc(10 * var(--scale));
|
||||
border-left: 2px solid var(--line-color);
|
||||
}
|
||||
|
||||
.taskmgr-indentcell-branch {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(5 * var(--scale));
|
||||
width: calc(5 * var(--scale));
|
||||
height: calc(5 * var(--scale));
|
||||
border-left: 2px solid var(--line-color);
|
||||
border-bottom: 2px solid var(--line-color);
|
||||
border-radius: 0 0 0 calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
.taskmgr-task {
|
||||
display: flex;
|
||||
height: calc(10 * var(--scale));
|
||||
line-height: calc(10 * var(--scale));
|
||||
}
|
||||
|
||||
.taskmgr-task-title {
|
||||
flex-grow: 1;
|
||||
padding-left: calc(2.5 * var(--scale));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ export class ProcessService extends Service {
|
||||
return this.processes_map.get(NULL_UUID);
|
||||
}
|
||||
|
||||
get_by_uuid (uuid) {
|
||||
return this.processes_map.get(uuid);
|
||||
}
|
||||
|
||||
get_children_of (uuid) {
|
||||
if ( ! uuid ) {
|
||||
uuid = NULL_UUID;
|
||||
|
||||
+12
-3
@@ -1,6 +1,9 @@
|
||||
import ValueHolder from "./ValueHolder.js";
|
||||
|
||||
export class Component extends HTMLElement {
|
||||
#has_created_element = false;
|
||||
#has_called_on_ready = false;
|
||||
|
||||
// Render modes
|
||||
static NO_SHADOW = Symbol('no-shadow');
|
||||
|
||||
@@ -86,12 +89,18 @@ export class Component extends HTMLElement {
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
this.on_ready && this.on_ready(this.get_api_());
|
||||
if (!this.#has_called_on_ready) {
|
||||
this.on_ready && this.on_ready(this.get_api_());
|
||||
this.#has_called_on_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
attach (destination) {
|
||||
const el = this.create_element_();
|
||||
this.dom_.appendChild(el);
|
||||
if (!this.#has_created_element) {
|
||||
const el = this.create_element_();
|
||||
this.dom_.appendChild(el);
|
||||
this.#has_created_element = true;
|
||||
}
|
||||
|
||||
if ( destination instanceof HTMLElement ) {
|
||||
destination.appendChild(this);
|
||||
|
||||
Reference in New Issue
Block a user