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

Merging the new add/remove layout stuff into v19 #11

Merged
merged 22 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d0299c3
adding remove layout function
joeransegnola Jun 10, 2024
4adafe7
adding logic for localstorage saves.
joeransegnola Jun 12, 2024
534aee9
so john can see where i was going with this
joeransegnola Jun 12, 2024
57cc518
adding in DOM element removal since removeLayout override doesn't do it?
joeransegnola Jun 12, 2024
71da15f
updated saveLayout to attempt a re-apply. not working as expected.
joeransegnola Jun 12, 2024
f0c22b4
add function working.
joeransegnola Jun 13, 2024
6ee1c4a
removing unnecessary call to apply after saving state.
joeransegnola Jun 13, 2024
855f66a
linting changes.
joeransegnola Jun 13, 2024
b892c97
okay, swap, remove, add work now.
joeransegnola Jun 18, 2024
3c61c93
Adding some context to the add button
joeransegnola Jun 19, 2024
70664ac
adding destroy to fix layout not being added.
joeransegnola Jul 9, 2024
40d1744
Updated to remove component name so you can add multiple instances of…
johnman Jul 10, 2024
3acb26d
updating pkgs, playing with CSS layouts.
joeransegnola Jul 11, 2024
3d8185e
Merge branch 'dev/joe/add-remove-layouts' of github.com:built-on-open…
joeransegnola Jul 11, 2024
f90805b
changed height, now it works!
joeransegnola Jul 11, 2024
c5e9746
adding disable add btn on add
joeransegnola Jul 12, 2024
c4786ae
Merge pull request #10 from built-on-openfin/dev/joe/add-remove-layouts
joeransegnola Jul 12, 2024
9746c65
updating version number
johnman Jul 12, 2024
13b7d9d
fixing conflict.
joeransegnola Jul 12, 2024
5459913
Merge branch 'web/v19.0.0' into web/vnext
johnman Jul 12, 2024
167008a
Update package lock
johnman Jul 12, 2024
7dbbbf3
Merge branch 'web/vnext' of https://github.com/built-on-openfin/web-s…
johnman Jul 12, 2024
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
242 changes: 210 additions & 32 deletions how-to/web-layout/client/src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable jsdoc/require-param */
import type OpenFin from "@openfin/core";
import { type WebLayoutSnapshot, connect } from "@openfin/core-web";
import { getDefaultLayout, getSettings } from "./platform/settings";
import { getDefaultLayout, getSecondLayout, getSettings } from "./platform/settings";
import type { LayoutManager, LayoutManagerConstructor, LayoutManagerItem } from "./shapes/layout-shapes";
import type { Settings } from "./shapes/setting-shapes";

Expand Down Expand Up @@ -39,12 +40,131 @@ function setupPanels(settings: Settings): void {
* Attach listeners to elements.
*/
async function attachListeners(): Promise<void> {
const swapButton = document.querySelector<HTMLButtonElement>("#swap-layouts");
swapButton?.addEventListener("click", async () => {
await swapLayout();
const addLayoutBtn = document.querySelector<HTMLButtonElement>("#add-layout");
addLayoutBtn?.addEventListener("click", async () => {
await addLayout();
});
}

/**
* Attaches Listeners to Tab Click Events.
* @param tabName the name of the tab to add the event to.
*/
async function attachTabListener(tabName: string): Promise<void> {
const tabBtn = document.querySelector<HTMLDivElement>(`#${tabName}`);
tabBtn?.addEventListener("click", async () => {
await selectTab(tabName);
});
}

/**
* Creates a new tab in the tab row given a specific tab/layout name.
*/
async function createTabBtn(tabName: string): Promise<void> {
const tabRow = document.querySelector<HTMLDivElement>("#tabs");
const newTab = document.createElement("div");
newTab.id = `tab-${tabName}`;
newTab.className = "tab";
newTab.style.display = "block";
newTab.append(document.createTextNode(`${tabName}`));
const closeBtn = document.createElement("span");
closeBtn.className = "close-btn";
closeBtn.innerHTML = "X";
closeBtn.addEventListener("click", async (e) => {
await removeTab(tabName);
e.stopPropagation();
});
newTab.append(closeBtn);
if (tabRow) {
tabRow.append(newTab);
if (document.querySelector<HTMLDivElement>(`#tab-${tabName}`)) {
await attachTabListener(newTab.id);
await selectTab(tabName);
}
}
}

/**
* Makes a layout and tab active.
*/
async function selectTab(tabName: string, removedTabName?: string): Promise<void> {
console.log(`Tab ${tabName} selected`);
let actualName = tabName;
if (tabName.includes("tab")) {
const split = tabName.split("-");
actualName = split[1];
}
const currentOrder = window.localStorage.getItem("order");
if (currentOrder !== "") {
const layoutsArr = currentOrder?.split(",");
if (layoutsArr) {
for (const tab of layoutsArr) {
if (actualName !== removedTabName) {
if (tab === actualName) {
await showTab(tab);
} else {
await hideTab(tab);
}
}
}
}
}
}

/**
* Makes a layout and tab hidden.
*/
async function showTab(tabName: string): Promise<void> {
console.log(`Tab ${tabName} showing...`);
const currentTab = document.querySelector<HTMLDivElement>(`#${tabName}`);
if (currentTab) {
currentTab.style.display = "block";
}
}

/**
* Makes a layout and tab hidden.
*/
async function hideTab(tabName: string): Promise<void> {
console.log(`Tab ${tabName} hiding...`);
const currentTab = document.querySelector<HTMLDivElement>(`#${tabName}`);
if (currentTab) {
currentTab.style.display = "none";
}
}

/**
* Removes a layout & tab from the page.
*/
async function removeTab(tabName: string): Promise<void> {
console.log(`Removing Tab & Layout ${tabName}`);
const lm = window.fin?.Platform.Layout.getCurrentLayoutManagerSync();
await lm?.removeLayout({ layoutName: tabName } as OpenFin.LayoutIdentity);
const tabToRemove = document.querySelector<HTMLDivElement>(`#tab-${tabName}`);
tabToRemove?.remove();

const currentOrder = window.localStorage.getItem("order");
if (currentOrder !== "") {
const layouts = currentOrder?.split(",");
const newOrder = layouts?.filter((e) => e !== tabName);
if (newOrder && newOrder.length > 0) {
window.localStorage.setItem("order", newOrder.toString());
} else {
window.localStorage.setItem("order", "");
}

if (newOrder) {
if (newOrder.length > 0) {
await selectTab(newOrder[0], tabName);
} else {
console.log("There are no layouts loaded.");
// eslint-disable-next-line no-alert
alert("There are no layouts loaded. Please add one.");
}
}
}
}

/**
* A Create function for layouts.
* @param fin the fin object.
Expand All @@ -67,16 +187,14 @@ async function createLayout(

// Normally you can use state here, but just tracking the order of layouts in localStorage.
const currentOrder = window.localStorage.getItem("order");
if (!currentOrder) {
window.localStorage.setItem("order", "");
}
let newOrder = "";
if (order === 0) {
if (!currentOrder || currentOrder === "") {
newOrder = layoutName;
} else {
newOrder = currentOrder ? currentOrder.concat(",", layoutName) : "";
newOrder = currentOrder?.concat(",", layoutName);
}
window.localStorage.setItem("order", newOrder);

// Finally, call the Layout.create() function to apply the snapshot layout to the div we just created.
await fin.Platform.Layout.create({ layoutName, layout, container });
}
Expand All @@ -103,44 +221,104 @@ function makeOverride(fin: OpenFin.Fin<OpenFin.EntityType>, layoutContainerId: s
* @param snapshot The layouts object containing the fixed set of available layouts.
*/
public async applyLayoutSnapshot(snapshot: WebLayoutSnapshot): Promise<void> {
console.log(`Does this exist? ${Boolean(this.layoutContainer)}`);
console.log(`[Apply Layout] Does this exist? ${Boolean(this.layoutContainer)}`);
if (this.layoutContainer !== null && this.layoutContainer !== undefined) {
for (const [key, value] of Object.entries(snapshot.layouts)) {
this.layoutMapArray.push({ layoutName: key, layout: value, container: this.layoutContainer });
}
setTimeout(
() =>
Object.entries(snapshot.layouts).map(async ([layoutName, layout], i) =>
createLayout(fin, layoutName, layout, i)
),
Object.entries(snapshot.layouts).map(async ([layoutName, layout], i) => {
await createLayout(fin, layoutName, layout, i);
await createTabBtn(layoutName);
}),
1000
);
console.log("Layouts loaded");
console.log("[Apply Layout] Layouts loaded");
console.log(`[Apply Layout] Layouts are: ${JSON.stringify(this.layoutMapArray)}`);
window.localStorage.setItem("currentLayout", JSON.stringify(this.layoutMapArray));
}
}

/**
* Remove Layout - You guessed it, it removes a layout from the existing array of layouts.
* @param id The name of the layout you want removed.
*/
public async removeLayout(id: OpenFin.LayoutIdentity): Promise<void> {
const index = this.layoutMapArray.findIndex((x) => x.layoutName === id.layoutName);
console.log(`[LM Override] Removing Layout ${id.layoutName}`);
console.log(`[LM Override] Found layout at index ${index}`);
await removeThisLayout(id.layoutName);
}
};
};
}

/**
* Returns a layout from the settings with a provided name.
* @returns The default layout from the settings.
* Saves the list of layout items to Local Storage.
* @param updatedLayoutContents List of Layouts to save.
*/
export async function swapLayout(): Promise<void> {
// Get that order of created div ids from storage, or state, or wherever you want to save them.
const currentOrder = window.localStorage.getItem("order");
const layouts = currentOrder?.split(",");
// This is a simple swap between two, but you can do this anyway you like.
const firstLayout = document.querySelector<HTMLElement>(`#${layouts ? layouts[0] : null}`);
const secondLayout = document.querySelector<HTMLElement>(`#${layouts ? layouts[1] : null}`);
if (firstLayout && secondLayout) {
if (secondLayout.style.display === "block") {
firstLayout.style.display = "block";
secondLayout.style.display = "none";
} else {
firstLayout.style.display = "none";
secondLayout.style.display = "block";
}
export async function saveLayout(updatedLayoutContents: LayoutManagerItem[]): Promise<void> {
window.localStorage.setItem("currentLayout", JSON.stringify(updatedLayoutContents));
}

/**
* Reads a list of layouts from Local Storage.
* @returns List of Layouts.
*/
export function readLayouts(): LayoutManagerItem[] {
const currentLayouts = window.localStorage.getItem("currentLayout");
if (currentLayouts) {
return JSON.parse(currentLayouts) as LayoutManagerItem[];
}

return [];
}

/**
* Adds another layout.
*/
export async function addLayout(): Promise<void> {
const secondLayoutToAdd = await getSecondLayout();
console.log("[Add Layout] Grabbing Secondary layout file...");
if (secondLayoutToAdd !== undefined) {
const lm = window.fin?.Platform.Layout.getCurrentLayoutManagerSync();
console.log("[Add Layout] Adding layout");
await lm?.applyLayoutSnapshot(secondLayoutToAdd);
} else {
console.log("[Add Layout] Error adding Layout. No Secondary Layout exists.");
}
const addBtn = document.querySelector<HTMLElement>("#add-layout");
if (addBtn) {
addBtn.setAttribute("disabled", "disabled");
}
}

/**
* Click function to remove a layout by name.
* @param layoutName the name of a layout.
*/
export async function removeThisLayout(layoutName: string): Promise<void> {
// remove layout from state.
const layoutsBefore = readLayouts();
let layoutsRemoved: LayoutManagerItem[] = [];
const layoutNameElement = document.querySelector<HTMLElement>(`#${layoutName}`);
if (layoutsBefore.length > 0 && layoutNameElement !== null) {
const idx = layoutsBefore.findIndex((x) => x.layoutName === layoutName);
layoutsRemoved = layoutsBefore.splice(idx, 1);
console.log(`[Remove Layout] Removed this layout: ${JSON.stringify(layoutsRemoved)}`);
await saveLayout(layoutsBefore);
console.log(`[Remove Layout] Layouts After Removal: ${JSON.stringify(layoutsBefore)}`);
layoutNameElement.remove();
await fin.Platform.Layout.destroy({ layoutName, uuid: fin.me.uuid, name: fin.me.name });
if (layoutName === "new") {
const addBtn = document.querySelector<HTMLElement>("#add-layout");
if (addBtn) {
addBtn.removeAttribute("disabled");
}
}
}
}
/**
* Initializes the OpenFin Web Broker connection.
*/
Expand Down Expand Up @@ -182,7 +360,7 @@ async function init(): Promise<void> {
connectionInheritance: "enabled",
platform: { layoutSnapshot }
});

window.fin = fin;
if (fin) {
const layoutManagerOverride = makeOverride(fin, settings.platform.layout.layoutContainerId);
// You may now use the `fin` object to initialize the broker and the layout.
Expand Down
40 changes: 40 additions & 0 deletions how-to/web-layout/public/common/style/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ body {
margin: 0;
background-color: var(--brand-background);
color: var(--brand-text);
width: 100%;
}

body.border {
Expand Down Expand Up @@ -705,6 +706,10 @@ em {
flex: 2;
}

.fill_3 {
flex: 3;
}

.scroll {
overflow: auto;
}
Expand Down Expand Up @@ -943,3 +948,38 @@ td,
.nowrap {
white-space: nowrap;
}

.tabs {
display: inline-flex;
height: 30px;
width: 100%;
}

.tab {
min-width: 120px;
min-height: 30px;
padding: 5px;
border: 2px solid var(--brand-border);
font-weight: 300;
border-top: 2px solid var(--tab-border-top-color);
background-color: var(--tab-background-active-color);
opacity: 100%;
cursor: pointer;
}

.close-btn {
float: right;
font-size: 1em;
max-height: 18px;
content: var(--tab-close-button-url);
color: var(--brand-text-secondary);
cursor: pointer;
}

.main-container {
display: flex;
justify-content: stretch;
align-items: stretch;
height: 100%;
width: 100%;
}
14 changes: 7 additions & 7 deletions how-to/web-layout/public/layouts/secondary.layout.fin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"layouts": {
"default": {
"new": {
"content": [
{
"type": "row",
Expand All @@ -18,10 +18,10 @@
"type": "component",
"componentName": "view",
"componentState": {
"url": "http://localhost:6060/views/fdc3-view.html",
"name": "internal-generated-view-secondary-1"
"url": "http://localhost:6060/views/fdc3-view.html"
},
"title": "FDC3 Same Domain"
"title": "FDC3 Same Domain",
"isClosable": false
}
]
},
Expand All @@ -33,10 +33,10 @@
"type": "component",
"componentName": "view",
"componentState": {
"url": "https://built-on-openfin.github.io/dev-extensions/extensions/v19.0.0/interop/fdc3/context/2-0/fdc3-broadcast-view.html",
"name": "internal-generated-view-secondary-2"
"url": "https://built-on-openfin.github.io/dev-extensions/extensions/v19.0.0/interop/fdc3/context/2-0/fdc3-broadcast-view.html"
},
"title": "FDC3 Different Domain"
"title": "FDC3 Different Domain",
"isClosable": true
}
]
}
Expand Down
Loading
Loading