Skip to content

Commit

Permalink
Merge pull request #3 from izo0x90/hristo/web-ui-templates
Browse files Browse the repository at this point in the history
Add boilerplate templates
  • Loading branch information
izo0x90 authored Nov 14, 2024
2 parents c3a980e + 76dfb3c commit 632dbce
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 2 deletions.
3 changes: 3 additions & 0 deletions boilerplate_templates/chrome_extension/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const COMMANDS = {
TEST: 'test'
}
11 changes: 11 additions & 0 deletions boilerplate_templates/chrome_extension/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<body>

<div>
Chrome extension content.
</div>

<script src="path_to_library/kiss/kiss_chrome_extension/popup_script_utils.js"></script>
<script src="popup_script.js" type="module"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions boilerplate_templates/chrome_extension/main.js
Original file line number Diff line number Diff line change
@@ -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.')
24 changes: 24 additions & 0 deletions boilerplate_templates/chrome_extension/manifest.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
]
}
14 changes: 14 additions & 0 deletions boilerplate_templates/chrome_extension/popup_script.js
Original file line number Diff line number Diff line change
@@ -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)
}
20 changes: 20 additions & 0 deletions boilerplate_templates/web_ui/app_config.js
Original file line number Diff line number Diff line change
@@ -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 = [
'<link rel="stylesheet" href="example_style_sheet.css">'
]

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
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ 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

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
}

Expand All @@ -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 = `
<div id="example-id">
Text that will be set to exampleProp.
<div id="main-example-id">
<div id="example-id">
</div>
</div>
`
9 changes: 9 additions & 0 deletions boilerplate_templates/web_ui/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
<link rel="stylesheet" href="example_style_sheet.css">
</head>

<body>
<ns-ui-app id="example-extension-app-id"></ns-ui-app>
</body>
</html>
25 changes: 25 additions & 0 deletions boilerplate_templates/web_ui/main.js
Original file line number Diff line number Diff line change
@@ -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
)
}
}
)
73 changes: 73 additions & 0 deletions boilerplate_templates/web_ui/ui_app_template.js
Original file line number Diff line number Diff line change
@@ -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 = `
<div id="main-element" some-prop="true">
<button id="refresh-button">Click me!</button>
<div id="components-container">
</div>
</div>
`

0 comments on commit 632dbce

Please sign in to comment.