feat: add animation on auto typing
This commit is contained in:
parent
39eb07798f
commit
465f97c53b
9
force_delete.css
Normal file
9
force_delete.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.__force_deleted {
|
||||||
|
animation: __force_deleted 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes __force_deleted {
|
||||||
|
0% { scale: inherit }
|
||||||
|
50% { scale: 1.1 }
|
||||||
|
100% { scale: inherit }
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
class ForceDeleteExtension {
|
class ForceDeleteExtension {
|
||||||
private readonly LOOP_INTERVAL_TIME = 500
|
private readonly LOOP_INTERVAL_TIME = 100
|
||||||
|
|
||||||
private readonly EXPLICIT_TARGET_SELECTORS = [
|
private readonly EXPLICIT_TARGET_SELECTORS = [
|
||||||
|
|
||||||
@ -11,7 +11,11 @@ class ForceDeleteExtension {
|
|||||||
|
|
||||||
// IAM ---
|
// IAM ---
|
||||||
'div[data-testid="roles-delete-modal-input"]>input',
|
'div[data-testid="roles-delete-modal-input"]>input',
|
||||||
'div[data-testid="policies-delete-modal-input"]>input'
|
'div[data-testid="policies-delete-modal-input"]>input',
|
||||||
|
'div[data-testid="policies-from-role-delete-modal-input"]>input',
|
||||||
|
|
||||||
|
// Firehose ---
|
||||||
|
'div[data-hook="DELETE_CONFIRMATION_INPUT"]>input'
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -73,6 +77,8 @@ class ForceDeleteExtension {
|
|||||||
elements.forEach(this.applyDeleteMessage.bind(this))
|
elements.forEach(this.applyDeleteMessage.bind(this))
|
||||||
|
|
||||||
private readonly applyDeleteMessage = (element: HTMLInputElement): void => {
|
private readonly applyDeleteMessage = (element: HTMLInputElement): void => {
|
||||||
|
element.classList.add('__force_deleted')
|
||||||
|
|
||||||
element.value = element.placeholder
|
element.value = element.placeholder
|
||||||
element.dispatchEvent(new Event('input', {
|
element.dispatchEvent(new Event('input', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
import { CommunicateMessage, FormSavedDataContent } from './types'
|
|
||||||
|
|
||||||
class FormSaverExtension {
|
|
||||||
private readonly api = browser ?? chrome
|
|
||||||
|
|
||||||
public startListen = (): void =>
|
|
||||||
this.api.runtime.onMessage.addListener(this.messageHandler.bind(this))
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
private readonly messageHandler = (message: unknown, _: any, sendResponse: (message: CommunicateMessage) => void): undefined => {
|
|
||||||
const receivedMessage = message as CommunicateMessage
|
|
||||||
|
|
||||||
if (receivedMessage.type === 'SAVE_FORM') {
|
|
||||||
return sendResponse(this.saveForm()) as undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivedMessage.type === 'LOAD_FORM') {
|
|
||||||
return this.loadForm(receivedMessage.data?.contents ?? []) as undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly saveForm = (): CommunicateMessage => {
|
|
||||||
const inputElements = this.findInputElements()
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'FORM_SAVED',
|
|
||||||
data: {
|
|
||||||
url: window.location.href,
|
|
||||||
contents: [
|
|
||||||
...this.retrieveTextInputValues(inputElements)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly loadForm = (formSaved: FormSavedDataContent[]): void => {
|
|
||||||
this.loadTextInputValues(formSaved)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
private readonly findInputElements = (): HTMLInputElement[] =>
|
|
||||||
[...document.querySelectorAll('input')]
|
|
||||||
|
|
||||||
private readonly retrieveTextInputValues = (elements: HTMLInputElement[]): FormSavedDataContent[] =>
|
|
||||||
elements
|
|
||||||
.filter((v) => v.type === 'text')
|
|
||||||
.map((v) => ({
|
|
||||||
selector: `div#${v.parentElement?.id ?? ''}>input`,
|
|
||||||
type: 'TEXT' as const,
|
|
||||||
value: v.value
|
|
||||||
}))
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
private readonly loadTextInputValues = (formSaved: FormSavedDataContent[]): void =>
|
|
||||||
formSaved
|
|
||||||
.filter((v) => v.type === 'TEXT')
|
|
||||||
.forEach(this.loadTextInputValue.bind(this))
|
|
||||||
|
|
||||||
private readonly loadTextInputValue = (formSaved: FormSavedDataContent): void => {
|
|
||||||
const element = document.querySelector(formSaved.selector)
|
|
||||||
if (element === null || !(element instanceof HTMLInputElement) || element.type !== 'text') { return }
|
|
||||||
|
|
||||||
element.value = formSaved.value
|
|
||||||
element.dispatchEvent(new Event('input', {
|
|
||||||
bubbles: true
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new FormSaverExtension()
|
|
||||||
.startListen()
|
|
@ -9,23 +9,14 @@
|
|||||||
"48": "icons/koishi.webp"
|
"48": "icons/koishi.webp"
|
||||||
},
|
},
|
||||||
|
|
||||||
"action": {
|
|
||||||
"default_icon": "icons/koishi.webp",
|
|
||||||
"default_title": "Koishi",
|
|
||||||
"default_popup": "popup/index.html"
|
|
||||||
},
|
|
||||||
|
|
||||||
"host_permissions": [
|
|
||||||
"*://*.console.aws.amazon.com/*"
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["*://*.console.aws.amazon.com/*"],
|
"matches": ["*://*.console.aws.amazon.com/*"],
|
||||||
"js": [
|
"js": [
|
||||||
"force_delete.js",
|
"force_delete.js"
|
||||||
"form_saver.js"
|
],
|
||||||
|
"css": [
|
||||||
|
"force_delete.css"
|
||||||
],
|
],
|
||||||
"all_frames": true,
|
"all_frames": true,
|
||||||
"match_about_blank": true,
|
"match_about_blank": true,
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" href="/popup/main.css">
|
|
||||||
<title>Koishi Form Saver</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h2>Form Saver</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>url</th>
|
|
||||||
<th>content</th>
|
|
||||||
<th>action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="render_point">
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<button id="save">Save!!</button>
|
|
||||||
<script src="/popup/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,34 +0,0 @@
|
|||||||
:root {
|
|
||||||
background-color: #212121;
|
|
||||||
color: #fafafa;
|
|
||||||
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #626262;
|
|
||||||
padding: 2px 5px;
|
|
||||||
color: #fafafa;
|
|
||||||
border: none;
|
|
||||||
outline: 1px solid #af7d85;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #838383;
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
import { CommunicateMessage, FormSavedData } from '../types'
|
|
||||||
|
|
||||||
void (async () => {
|
|
||||||
const api = browser ?? chrome
|
|
||||||
|
|
||||||
const [tab] = await api.tabs.query({
|
|
||||||
active: true,
|
|
||||||
currentWindow: true
|
|
||||||
})
|
|
||||||
|
|
||||||
if (tab.url === undefined) {
|
|
||||||
document.body.innerText = 'Extension disabled on this page'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localStorage.getItem('form_saved') === null) { localStorage.setItem('form_saved', '[]') }
|
|
||||||
|
|
||||||
document.getElementById('save')?.addEventListener('click', () => {
|
|
||||||
void (async () => {
|
|
||||||
const formSaved: CommunicateMessage = await api.tabs.sendMessage(tab.id ?? 0, {
|
|
||||||
type: 'SAVE_FORM'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (formSaved.type !== 'FORM_SAVED') { return }
|
|
||||||
|
|
||||||
const prevSaved = JSON.parse(localStorage.getItem('form_saved') ?? '') as Array<FormSavedData & { id: string }>
|
|
||||||
const newSaved = JSON.stringify([
|
|
||||||
{
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
...formSaved.data
|
|
||||||
},
|
|
||||||
...prevSaved.slice(0, 10)
|
|
||||||
])
|
|
||||||
|
|
||||||
localStorage.setItem('form_saved', newSaved)
|
|
||||||
|
|
||||||
renderTable()
|
|
||||||
})()
|
|
||||||
})
|
|
||||||
|
|
||||||
renderTable()
|
|
||||||
function renderTable (): void {
|
|
||||||
const formSaved = JSON.parse(localStorage.getItem('form_saved') ?? '') as Array<FormSavedData & { id: string }>
|
|
||||||
const renderPoint = document.getElementById('render_point')
|
|
||||||
|
|
||||||
if (renderPoint === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPoint.innerHTML = ''
|
|
||||||
|
|
||||||
for (const form of formSaved) {
|
|
||||||
renderPoint.innerHTML += `
|
|
||||||
<tr>
|
|
||||||
<td>${form.id.substring(0, 8)}</td>
|
|
||||||
<td>${new URL(form.url).pathname}${new URL(form.url).hash}</td>
|
|
||||||
<td>${form.contents.filter((v) => v.type === 'TEXT').length} Texts</td>
|
|
||||||
<td>
|
|
||||||
<button id="load-${form.id}">Load</button>
|
|
||||||
<button id="delete-${form.id}">x</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const form of formSaved) {
|
|
||||||
document.getElementById(`load-${form.id}`)?.addEventListener('click', () => {
|
|
||||||
void api.tabs.sendMessage<CommunicateMessage>(tab.id ?? 0, {
|
|
||||||
type: 'LOAD_FORM',
|
|
||||||
data: form
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById(`delete-${form.id}`)?.addEventListener('click', () => {
|
|
||||||
const prevSaved = JSON.parse(localStorage.getItem('form_saved') ?? '') as Array<FormSavedData & { id: string }>
|
|
||||||
const newSaved = JSON.stringify(prevSaved.filter((v) => v.id !== form.id))
|
|
||||||
|
|
||||||
localStorage.setItem('form_saved', newSaved)
|
|
||||||
renderTable()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
Loading…
Reference in New Issue
Block a user