From 76dfb3c34e28d69aacbfbb2e2cb12d667b6ba901 Mon Sep 17 00:00:00 2001 From: "Hristo (Izo) Gueorguiev" <53634432+izo0x90@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:59:38 -0500 Subject: [PATCH] Add boilerplate templates --- .../chrome_extension/commands.js | 3 + .../chrome_extension/main.html | 11 +++ .../chrome_extension/main.js | 25 +++++++ .../chrome_extension/manifest.json | 24 ++++++ .../chrome_extension/popup_script.js | 14 ++++ boilerplate_templates/web_ui/app_config.js | 20 +++++ .../{ => web_ui}/component_template.js | 13 +++- boilerplate_templates/web_ui/main.html | 9 +++ boilerplate_templates/web_ui/main.js | 25 +++++++ .../web_ui/ui_app_template.js | 73 +++++++++++++++++++ 10 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 boilerplate_templates/chrome_extension/commands.js create mode 100644 boilerplate_templates/chrome_extension/main.html create mode 100644 boilerplate_templates/chrome_extension/main.js create mode 100644 boilerplate_templates/chrome_extension/manifest.json create mode 100644 boilerplate_templates/chrome_extension/popup_script.js create mode 100644 boilerplate_templates/web_ui/app_config.js rename boilerplate_templates/{ => web_ui}/component_template.js (65%) create mode 100644 boilerplate_templates/web_ui/main.html create mode 100644 boilerplate_templates/web_ui/main.js create mode 100644 boilerplate_templates/web_ui/ui_app_template.js diff --git a/boilerplate_templates/chrome_extension/commands.js b/boilerplate_templates/chrome_extension/commands.js new file mode 100644 index 0000000..65a7653 --- /dev/null +++ b/boilerplate_templates/chrome_extension/commands.js @@ -0,0 +1,3 @@ +export const COMMANDS = { + TEST: 'test' +} diff --git a/boilerplate_templates/chrome_extension/main.html b/boilerplate_templates/chrome_extension/main.html new file mode 100644 index 0000000..337fa69 --- /dev/null +++ b/boilerplate_templates/chrome_extension/main.html @@ -0,0 +1,11 @@ + + + +
+ Chrome extension content. +
+ + + + + diff --git a/boilerplate_templates/chrome_extension/main.js b/boilerplate_templates/chrome_extension/main.js new file mode 100644 index 0000000..a3ac706 --- /dev/null +++ b/boilerplate_templates/chrome_extension/main.js @@ -0,0 +1,25 @@ +// This is the main content script module that will get injected into the page with access to it's DOM etc. +import { CommandChannel } from 'path_to_library/kiss/kiss_chrome_extension/message_utils.js' + +import { COMMANDS } from './commands.js' + +// Commands +async function testCommand (inputData) { + // This code will execute in the webpages sandbox, allowing to access it's contents, issue fetch requests etc. + console.log(`TEST COMMAND REQUEST RECEIVED, Got ${inputData}`) + const results = document.getElementsByTagName('h3')[0]?.textContent + return new Promise(resolve => resolve(results)) +} + +// The chrome extension script can trigger commands to execute in the webpages sandbox by sending a +// message along the commandMessageChannel, as demonstrated in `popup_script.js`. +// Results of a command will be sent back to the caller along the channel. +// Event listeners and messages +const commandsMap = { + [COMMANDS.TEST]: testCommand +} +const commandMessageChannel = new CommandChannel(commandsMap) +commandMessageChannel.listenForCommands() + +// Init finished +console.log('Content script injected into web page.') diff --git a/boilerplate_templates/chrome_extension/manifest.json b/boilerplate_templates/chrome_extension/manifest.json new file mode 100644 index 0000000..086c647 --- /dev/null +++ b/boilerplate_templates/chrome_extension/manifest.json @@ -0,0 +1,24 @@ +{ + "manifest_version": 3, + "name": "Example Plug-In", + "description": "Example plug-in using kiss-chrome-extension", + "version": "1.0", + "action": { + "default_popup": "main.html" + }, + "content_scripts": [{ + "matches": ["*://*.desired.domain.here/*"], + "js": [ + "path_to_library/kiss/kiss_chrome_extension/content_script_utils.js" + ] + }], + "web_accessible_resources": [ + { + "matches": ["*://*.desired.domain.here/*"], + "resources": [ + "main.js", + "path_to_library/kiss/kiss_chrome_extension/message_utils.js" + ] + } + ] + } diff --git a/boilerplate_templates/chrome_extension/popup_script.js b/boilerplate_templates/chrome_extension/popup_script.js new file mode 100644 index 0000000..c78fb52 --- /dev/null +++ b/boilerplate_templates/chrome_extension/popup_script.js @@ -0,0 +1,14 @@ +import { CommandChannel } from 'path_to_library/kiss/kiss_chrome_extension/message_utils.js' +import { COMMANDS } from './commands.js' + +// Chrome extension specific +const commandMessageChannel = new CommandChannel() + +// State management +function executeTestCommandInContentScript () { + const payloadToSendTestCommand = {someData: 'Test data'} + commandMessageChannel.sendCommandToActiveTab(COMMANDS.TEST, (results) => { + // Here we can execute any callback once the Test command has executed and its results are returned + console.log('Returning fresh results!') + }, payloadToSendTestCommand) +} diff --git a/boilerplate_templates/web_ui/app_config.js b/boilerplate_templates/web_ui/app_config.js new file mode 100644 index 0000000..69e0a75 --- /dev/null +++ b/boilerplate_templates/web_ui/app_config.js @@ -0,0 +1,20 @@ +import { ExampleComponent } from './component_template.js' +import { UiApp, ExamplePublicEvent } from 'ui_app_template.js' + +export const APPNAMEPREFIX = 'ap' // This will be appended to the front of the HTML tag ex. `ap-ui-app` + +export const GLOBALSTYLESHEETS = [ + '' +] + +export const PUBLICEVENTS = [ + ExamplePublicEvent +] + +// Order matters here. All child components used by parent components need to be registered before the +// parent component can be registered. Aka order should be leafs to root, it should be already clear but +// let us restate that cyclical dependency will break the component registration functionality +export const APPCOMPONENTS = [ + ExampleComponent, + UiApp +] diff --git a/boilerplate_templates/component_template.js b/boilerplate_templates/web_ui/component_template.js similarity index 65% rename from boilerplate_templates/component_template.js rename to boilerplate_templates/web_ui/component_template.js index fb3e0e8..b2f802d 100644 --- a/boilerplate_templates/component_template.js +++ b/boilerplate_templates/web_ui/component_template.js @@ -2,6 +2,7 @@ import { BaseElement } from 'path_to_library/kiss/kiss_web_ui/base.js' export class ExampleComponent extends BaseElement { template = TEMPLATESTRING + #RESULTSID = 'data' exampleProp = null exampleAttrib = null @@ -9,6 +10,9 @@ export class ExampleComponent extends BaseElement { buildComponent () { const templateInstance = this.defaultTemplate templateInstance.utils.updateTextById('example-id', this.exampleProp) + const span = document.createElement('span') + span.id = this.#RESULTSID + templateInstance.utils.appendById('main-example-id', span) return templateInstance } @@ -17,10 +21,15 @@ export class ExampleComponent extends BaseElement { // component will be on real DOM and redrawn return ['example-attrib'] } + + replaceResults (resultsData) { + this.domUtils.updateTextbyId(this.#RESULTSID, resultsData) + } } const TEMPLATESTRING = ` -
- Text that will be set to exampleProp. +
+
+
` diff --git a/boilerplate_templates/web_ui/main.html b/boilerplate_templates/web_ui/main.html new file mode 100644 index 0000000..1bc94a3 --- /dev/null +++ b/boilerplate_templates/web_ui/main.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate_templates/web_ui/main.js b/boilerplate_templates/web_ui/main.js new file mode 100644 index 0000000..143b74d --- /dev/null +++ b/boilerplate_templates/web_ui/main.js @@ -0,0 +1,25 @@ +import { initSimpleWebCApp } from 'path_to_library/kiss/kiss_web_ui/utils.js' + +// Initialize +const { appUi, publicUiEvents } = await initSimpleWebCApp('/path_to_app_components_code', 'example-extension-app-id') + +async function refreshResults (eventDetails, callback) { + console.log('Do some required processing, I/O etc. and call the appropriate UI app command') + // Pass the callback to some async function/s that is doing the processing + // Pretend async block + const pretendData = 'DATA' + callback(pretendData) +} + +// Register UI event handlers +document.addEventListener( + publicUiEvents.ExamplePublicEvent.EVENTNAME, (event) => { + console.log('Example event, details:', event.detail) + if (event.detail.someEventProp != null) { + refreshResults( + event.detail, + appUi.commands.updateResults + ) + } + } +) diff --git a/boilerplate_templates/web_ui/ui_app_template.js b/boilerplate_templates/web_ui/ui_app_template.js new file mode 100644 index 0000000..a6ff247 --- /dev/null +++ b/boilerplate_templates/web_ui/ui_app_template.js @@ -0,0 +1,73 @@ +import { BaseElement, PublicUiEvent } from 'path_to_library/kiss/kiss_web_ui/base.js' +import { ExampleComponent } from './component_template.js' + +export class ExamplePublicEvent extends PublicUiEvent { + static EVENTNAME = 'exampleEvent' + static DataClass = class { + constructor (eventDetailsPropInput) { + this.someEventProp = eventDetailsPropInput + } + } + + constructor (eventDetails) { + const eventData = { detail: eventDetails } + super(ExamplePublicEvent.EVENTNAME, eventData) + } +} + +export class UiApp extends BaseElement { + template = TEMPLATESTRING + #PROPNAME = 'some-prop' + #EXAMPLECOMPONENTID = 'example-component' + + buildComponent () { + const templateInstance = this.defaultTemplate + const exampleChildComponent = new ExampleComponent() + exampleChildComponent.id = this.#EXAMPLECOMPONENTID + templateInstance.utils.appendById('components-container', exampleChildComponent) + + return templateInstance + } + + static get observedAttributes () { + return [] + } + + onMount () { + console.log('APP Mounted!') + + // Register event handlers here + this.domUtils.addListenerById('refresh-button', 'click', this.eventHandlers.refreshClick) + } + + // Organize event handler methods under .eventHandlers + eventHandlers = { + refreshClick: (_) => { + const someEventProp = this.domUtils.byId( + 'main-element' + ).getAttribute(this.#PROPNAME) + + this.commands.requestResults(someEventProp) + } + } + + // Publicly available commands are under .commands only on the root app component + // the root component is responsible for managing the rest of the child components + commands = { + requestResults: (someEventProp) => { + this.dispatchEvent(new ExamplePublicEvent(new ExamplePublicEvent.DataClass(someEventProp))) + }, + + updateResults: (resultsData) => { + this.domUtils.byId(this.#EXAMPLECOMPONENTID).replaceResults(resultsData) + } + } +} + +const TEMPLATESTRING = ` +
+ +
+
+
+`