mirror of
https://github.com/php-flasher/php-flasher.git
synced 2026-03-31 15:07:47 +01:00
chore: update the doc and improve phpstan annotations
This commit is contained in:
@@ -2,93 +2,94 @@ import { Controller } from '@hotwired/stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
connect() {
|
||||
const container = document.querySelector('#anchor-navigation')
|
||||
this.container = document.querySelector('#anchor-navigation')
|
||||
if (!this.container) {
|
||||
return
|
||||
}
|
||||
|
||||
createAnchorNavigation()
|
||||
highlightCurrentAnchor()
|
||||
stickyHeight()
|
||||
this.createAnchorNavigation()
|
||||
this.highlightCurrentAnchor()
|
||||
this.stickyHeight()
|
||||
this.setupEventListeners()
|
||||
}
|
||||
|
||||
createAnchorNavigation() {
|
||||
const ul = this.container.querySelector('ul')
|
||||
const anchors = document.querySelectorAll('#main-article h3, #main-article h2, #main-article a.anchor')
|
||||
|
||||
if (anchors.length === 0) {
|
||||
this.container.remove()
|
||||
return
|
||||
}
|
||||
|
||||
this.container.classList.add('lg:block')
|
||||
|
||||
anchors.forEach((anchor) => {
|
||||
const li = this.createNavItem(anchor)
|
||||
ul.appendChild(li)
|
||||
})
|
||||
}
|
||||
|
||||
createNavItem(anchor) {
|
||||
const li = document.createElement('li')
|
||||
li.className = 'px-6 rounded w-full'
|
||||
if (anchor.tagName === 'A') {
|
||||
li.className = 'px-12 rounded w-full'
|
||||
}
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = anchor.tagName === 'A' ? anchor.hash : `#${anchor.getAttribute('id')}`
|
||||
link.innerHTML = anchor.tagName === 'A' ? anchor.textContent : `<i class="fa-duotone fa-angle-right"></i>${anchor.textContent}`
|
||||
link.className = 'leading-loose text-md inline-block w-full text-indigo-500'
|
||||
|
||||
li.appendChild(link)
|
||||
return li
|
||||
}
|
||||
|
||||
highlightCurrentAnchor(hash = window.location.hash) {
|
||||
if (hash.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const links = document.querySelectorAll('a.anchor, #anchor-navigation ul li a')
|
||||
links.forEach((anchor) => {
|
||||
anchor.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
window.location.hash = anchor.hash
|
||||
highlightCurrentAnchor(anchor.hash)
|
||||
links.forEach((link) => {
|
||||
const parent = link.parentElement
|
||||
if (hash === link.hash) {
|
||||
link.classList.remove('text-gray-900')
|
||||
link.classList.add('text-white')
|
||||
parent.classList.add('bg-indigo-500')
|
||||
} else {
|
||||
link.classList.add('text-gray-900')
|
||||
link.classList.remove('text-white')
|
||||
parent.classList.remove('bg-indigo-500')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
stickyHeight() {
|
||||
const article = document.querySelector('#main-article')
|
||||
const stickies = document.querySelectorAll('.sticky')
|
||||
|
||||
stickies.forEach((sticky) => {
|
||||
if (sticky.offsetHeight > window.innerHeight && article.clientHeight > sticky.offsetHeight) {
|
||||
const div = document.createElement('div')
|
||||
div.className = 'h-screen overflow-y-auto'
|
||||
div.innerHTML = sticky.innerHTML
|
||||
|
||||
sticky.innerHTML = div.outerHTML
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
const links = this.container.querySelectorAll('a')
|
||||
links.forEach((link) => {
|
||||
link.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
window.location.hash = link.hash
|
||||
this.highlightCurrentAnchor(link.hash)
|
||||
})
|
||||
})
|
||||
|
||||
function createAnchorNavigation() {
|
||||
const ul = document.querySelector('#anchor-navigation ul')
|
||||
const anchors = document.querySelectorAll('#main-article h3, #main-article h2, #main-article a.anchor')
|
||||
|
||||
if (anchors.length === 0) {
|
||||
container.remove()
|
||||
return
|
||||
}
|
||||
|
||||
container.classList.add('lg:block')
|
||||
|
||||
anchors.forEach((anchor) => {
|
||||
const parent = anchor.parentElement
|
||||
parent.classList.add('px-6', 'rounded')
|
||||
|
||||
anchor.classList.add('leading-loose')
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = anchor.tagName === 'A' ? anchor.hash : `#${anchor.getAttribute('id')}`
|
||||
link.innerHTML = anchor.tagName === 'A' ? anchor.textContent : `<i class="fa-duotone fa-angle-right"></i>${anchor.textContent}`
|
||||
link.classList.add('leading-loose', 'text-md', 'inline-block', 'w-full', 'text-indigo-500')
|
||||
|
||||
const li = document.createElement('li')
|
||||
li.classList.add('px-6', 'rounded', 'w-full')
|
||||
if (anchor.tagName === 'A') {
|
||||
li.classList.remove('px-6')
|
||||
li.classList.add('px-12')
|
||||
}
|
||||
|
||||
li.appendChild(link)
|
||||
|
||||
ul.appendChild(li)
|
||||
})
|
||||
}
|
||||
|
||||
function highlightCurrentAnchor(hash) {
|
||||
if (typeof hash === 'undefined') {
|
||||
hash = window.location.hash
|
||||
}
|
||||
|
||||
const links = document.querySelectorAll('a.anchor, #anchor-navigation ul li a')
|
||||
links.forEach((link) => {
|
||||
const parent = link.parentElement
|
||||
link.classList.remove('text-gray-900')
|
||||
link.classList.add('text-indigo-500')
|
||||
parent.classList.remove('bg-indigo-500')
|
||||
|
||||
if (hash === link.hash) {
|
||||
link.classList.remove('text-indigo-500')
|
||||
link.classList.add('text-white')
|
||||
|
||||
parent.classList.add('bg-indigo-500')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function stickyHeight() {
|
||||
const article = document.querySelector('#main-article')
|
||||
const elements = document.querySelectorAll('.sticky')
|
||||
|
||||
elements.forEach((element) => {
|
||||
if (element.offsetHeight <= window.innerHeight || article.clientHeight <= element.offsetHeight) {
|
||||
return
|
||||
}
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('h-screen', 'overflow-y-auto')
|
||||
div.innerHTML = element.innerHTML
|
||||
|
||||
element.innerHTML = div.outerHTML
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import Prism from 'prismjs'
|
||||
import flasher from '@flasher/flasher'
|
||||
|
||||
import 'prismjs/components/prism-markup-templating'
|
||||
import 'prismjs/components/prism-php'
|
||||
|
||||
import './playground.pcss'
|
||||
|
||||
export default class extends Controller {
|
||||
static values = {
|
||||
adapter: String,
|
||||
type: String,
|
||||
message: String,
|
||||
title: String,
|
||||
}
|
||||
|
||||
static targets = ['optionsContainer', 'codeSnippet', 'message', 'title', 'adapter', 'type']
|
||||
|
||||
connect() {
|
||||
this.initializeActiveButtons()
|
||||
this.updateAdapterOptions()
|
||||
this.updateCodeSnippet()
|
||||
|
||||
Prism.highlightAll()
|
||||
}
|
||||
|
||||
initializeActiveButtons() {
|
||||
this.updateButtonStyles('#adapter-options', this.adapterValue)
|
||||
this.updateButtonStyles('#type-options', this.typeValue)
|
||||
}
|
||||
|
||||
selectType(event) {
|
||||
const type = event.currentTarget.dataset.value
|
||||
this.typeTarget.value = type
|
||||
this.typeValue = type
|
||||
this.updateButtonStyles('#type-options', type)
|
||||
}
|
||||
|
||||
updateButtonStyles(containerSelector, activeValue) {
|
||||
document.querySelectorAll(`${containerSelector} button`).forEach((button) => {
|
||||
const isActive = button.dataset.value === activeValue
|
||||
|
||||
button.classList.toggle('ring-2', isActive)
|
||||
button.classList.toggle('ring-offset-2', isActive)
|
||||
})
|
||||
}
|
||||
|
||||
updateAdapterOptions() {
|
||||
const options = this.fetchAdapterOptions(this.adapterValue)
|
||||
this.renderOptionsForm(options)
|
||||
}
|
||||
|
||||
fetchAdapterOptions(adapter) {
|
||||
const options = {
|
||||
flasher: {
|
||||
position: {
|
||||
type: 'radio',
|
||||
default: 'top-right',
|
||||
options: ['top-right', 'top-left', 'top-center', 'bottom-right', 'bottom-left', 'bottom-center'],
|
||||
},
|
||||
direction: {
|
||||
type: 'radio',
|
||||
default: 'top',
|
||||
options: ['top', 'bottom'],
|
||||
},
|
||||
timeout: {
|
||||
type: 'radio',
|
||||
default: 5000,
|
||||
options: [0, 3000, 9000],
|
||||
},
|
||||
},
|
||||
toastr: {
|
||||
closeButton: { type: 'checkbox', default: true },
|
||||
},
|
||||
}
|
||||
return options[adapter] || {}
|
||||
}
|
||||
|
||||
renderOptionsForm(options) {
|
||||
let formHTML = '<form id="options-form" class="space-y-4">'
|
||||
Object.entries(options).forEach(([key, option]) => {
|
||||
formHTML += this.optionToFormHTML(key, option)
|
||||
})
|
||||
formHTML += '</form>'
|
||||
this.optionsContainerTarget.innerHTML = formHTML
|
||||
}
|
||||
|
||||
optionToFormHTML(key, option) {
|
||||
let inputHTML = ''
|
||||
if (option.type === 'radio') {
|
||||
inputHTML = option.options.map((opt) => `
|
||||
<div class="flex items-center w-1/4">
|
||||
<input id="${key}-${opt}" type="radio" name="${key}" value="${opt}" class="text-indigo-600 border-gray-300 focus:ring-indigo-500" ${opt === option.default ? 'checked' : ''}>
|
||||
<label for="${key}-${opt}" class="py-1 px-4 text-sm font-medium text-gray-800 whitespace-nowrap">${opt}</label>
|
||||
</div>
|
||||
`).join('')
|
||||
} else if (option.type === 'checkbox') {
|
||||
inputHTML = `
|
||||
<div class="flex items-center w-1/4">
|
||||
<input id="${key}" type="checkbox" name="${key}" class="text-indigo-600 border-gray-300 focus:ring-indigo-500 rounded" ${option.default ? 'checked' : ''}>
|
||||
<label for="${key}" class="py-1 px-4 text-sm font-medium text-gray-800">${key}</label>
|
||||
</div>
|
||||
`
|
||||
} else {
|
||||
inputHTML = `<div class="w-1/4"><input type="${option.type}" name="${key}" value="${option.default}" class="hidden"></div>`
|
||||
}
|
||||
return `
|
||||
<div class="mb-4">
|
||||
<label class="text-sm font-semibold text-gray-700">${key}</label>
|
||||
<div class="mt-2 grid grid-cols-3">
|
||||
${inputHTML}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
showNotification(event) {
|
||||
event.preventDefault()
|
||||
const options = this.collectOptions()
|
||||
const message = this.messageTarget.value
|
||||
const title = this.titleTarget.value
|
||||
const type = this.typeValue
|
||||
|
||||
this.updateCodeSnippet()
|
||||
flasher.use(this.adapterValue).flash(type, message, title, options)
|
||||
}
|
||||
|
||||
collectOptions() {
|
||||
const form = this.optionsContainerTarget.querySelector('#options-form')
|
||||
const formData = new FormData(form)
|
||||
const options = {}
|
||||
formData.forEach((value, key) => {
|
||||
options[key] = value
|
||||
})
|
||||
return options
|
||||
}
|
||||
|
||||
updateCodeSnippet() {
|
||||
const options = this.collectOptions()
|
||||
const message = this.messageTarget.value
|
||||
const title = this.titleTarget.value
|
||||
const type = this.typeValue
|
||||
|
||||
const optionsString = this.optionsToOptionMethods(options)
|
||||
|
||||
const codeFunction = this.adapterValue === 'toastr' ? 'toastr' : 'flash'
|
||||
const formattedOptions = optionsString ? `\n${optionsString}` : ''
|
||||
this.codeSnippetTarget.textContent = `${codeFunction}()${formattedOptions}\n\t->${type}('${message}', '${title}');`
|
||||
Prism.highlightElement(this.codeSnippetTarget)
|
||||
}
|
||||
|
||||
optionsToOptionMethods(options) {
|
||||
return Object.entries(options).map(([key, value]) => {
|
||||
const formattedValue = typeof value === 'string' ? `'${value}'` : value
|
||||
return `\t->option('${key}', ${formattedValue})`
|
||||
}).join('\n')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user