chore: update the doc and improve phpstan annotations

This commit is contained in:
Younes ENNAJI
2024-05-05 17:52:48 +01:00
parent eafa471fde
commit d2f019fb62
135 changed files with 3329 additions and 1423 deletions
+83 -82
View File
@@ -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')
}
}