Skip to content

Commit 9c71d6e

Browse files
Plus d'éléments sur S3 (#218)
* précisions arrow et snakemake * test tabsets * ajoute connexion s3fs * Automated changes * Automated changes * shortcodes * Automated changes * Automated changes * premiers onglets * s3 * plus d'éléments sur arrow * Automated changes * Automated changes * ajoute snakemake * Automated changes * Automated changes * disable utterances * Automated changes * Automated changes * lineblank * Automated changes * Automated changes * panelset from hugo apero * panelset scss * follow guidelines to load js * Automated changes * Automated changes * change path * change name * box * into box * box * panel to box * first box to panel * panel to box * Automated changes * Automated changes * modif marginale * Automated changes * Automated changes * box * Automated changes * rm utterances * avoid compiling in local * un système minimal pour les tabs * Automated changes * Automated changes * ajoute des exemples * Automated changes * Automated changes * ajoute commandes mc * Automated changes * Automated changes * retour utterances Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 57ac3e8 commit 9c71d6e

39 files changed

+1262
-376
lines changed

.Rprofile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ if (file.exists("~/.Rprofile")) {
55
options(blogdown.new_bundle = TRUE)
66
options(blogdown.hugo.version = "0.83.0")
77
options(blogdown.method = 'markdown')
8+
options(blogdown.knit.on_save = FALSE)
89

910

1011
reminder_jupyter <- function(file = "./content/getting-started/06_rappels_classes.Rmd",

assets/js/panelset.js

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
/* global slideshow */
2+
/* https://github.com/gadenbuie/xaringanExtra/blob/93429efefb268a4df2f43935304a8f781f73ece8/inst/panelset/panelset.js*/
3+
/* hugo apero theme */
4+
(function () {
5+
const ready = function (fn) {
6+
/* MIT License Copyright (c) 2016 Nuclei */
7+
/* https://github.com/nuclei/readyjs */
8+
const completed = () => {
9+
document.removeEventListener('DOMContentLoaded', completed)
10+
window.removeEventListener('load', completed)
11+
fn()
12+
}
13+
if (document.readyState !== 'loading') {
14+
setTimeout(fn)
15+
} else {
16+
document.addEventListener('DOMContentLoaded', completed)
17+
window.addEventListener('load', completed)
18+
}
19+
}
20+
21+
ready(function () {
22+
[...document.querySelectorAll('.panel-name')]
23+
.map(el => el.textContent.trim())
24+
25+
const panelIds = {}
26+
27+
const uniquePanelId = (name) => {
28+
name = encodeURIComponent(name.toLowerCase().replace(/[\s]/g, '-'))
29+
if (Object.keys(panelIds).includes(name)) {
30+
name += ++panelIds[name]
31+
} else {
32+
panelIds[name] = 1
33+
}
34+
return name
35+
}
36+
37+
const identifyPanelName = (item) => {
38+
let name = 'Panel'
39+
40+
// If the item doesn't have a parent element, then we've already processed
41+
// it, probably because we're in an Rmd, and it's been removed from the DOM
42+
if (!item.parentElement) {
43+
return
44+
}
45+
46+
// In R Markdown when header-attrs.js is present, we may have found a
47+
// section header but the class attributes won't be duplicated on the <hX> tag
48+
if (
49+
(item.tagName === 'SECTION' || item.classList.contains('section')) &&
50+
/^H[1-6]/.test(item.children[0].tagName)
51+
) {
52+
name = item.children[0].textContent
53+
item.classList.remove('panel-name')
54+
item.removeChild(item.children[0])
55+
return name
56+
}
57+
58+
const nameDiv = item.querySelector('.panel-name')
59+
if (!nameDiv) return name
60+
61+
// In remarkjs the .panel-name span might be in a paragraph tag
62+
// and if the <p> is empty, we'll remove it
63+
if (
64+
nameDiv.tagName === 'SPAN' &&
65+
nameDiv.parentNode.tagName === 'P' &&
66+
nameDiv.textContent === nameDiv.parentNode.textContent
67+
) {
68+
name = nameDiv.textContent
69+
item.removeChild(nameDiv.parentNode)
70+
return name
71+
}
72+
73+
// If none of the above, remove the nameDiv and return the name
74+
name = nameDiv.textContent
75+
nameDiv.parentNode.removeChild(nameDiv)
76+
return name
77+
}
78+
79+
const processPanelItem = (item) => {
80+
const name = identifyPanelName(item)
81+
if (!name) {
82+
return null
83+
}
84+
return { name, content: item.children, id: uniquePanelId(name) }
85+
}
86+
87+
const getCurrentPanelFromUrl = (panelset) => {
88+
const params = new URLSearchParams(window.location.search)
89+
return params.get(panelset)
90+
}
91+
92+
const reflowPanelSet = (panels, idx) => {
93+
const res = document.createElement('div')
94+
res.className = 'panelset'
95+
res.id = 'panelset' + (idx > 0 ? idx : '')
96+
const panelSelected = getCurrentPanelFromUrl(res.id)
97+
98+
// create header row
99+
const headerRow = document.createElement('ul')
100+
headerRow.className = 'panel-tabs'
101+
headerRow.setAttribute('role', 'tablist')
102+
panels
103+
.map((p, idx) => {
104+
const panelHeaderItem = document.createElement('li')
105+
panelHeaderItem.className = 'panel-tab'
106+
panelHeaderItem.setAttribute('role', 'tab')
107+
const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0
108+
if (thisPanelIsActive) {
109+
panelHeaderItem.classList.add('panel-tab-active')
110+
panelHeaderItem.setAttribute('aria-selected', true)
111+
}
112+
panelHeaderItem.tabIndex = 0
113+
panelHeaderItem.id = res.id + '_' + p.id // #panelsetid_panelid
114+
115+
const panelHeaderLink = document.createElement('a')
116+
panelHeaderLink.href = '?' + res.id + '=' + p.id + '#' + panelHeaderItem.id
117+
panelHeaderLink.setAttribute('onclick', 'return false;')
118+
panelHeaderLink.tabIndex = -1 // list item is tabable, not link
119+
panelHeaderLink.innerHTML = p.name
120+
panelHeaderLink.setAttribute('aria-controls', p.id)
121+
122+
panelHeaderItem.appendChild(panelHeaderLink)
123+
return panelHeaderItem
124+
})
125+
.forEach(el => headerRow.appendChild(el))
126+
127+
res.appendChild(headerRow)
128+
129+
panels
130+
.map((p, idx) => {
131+
const panelContent = document.createElement('section')
132+
panelContent.className = 'panel'
133+
panelContent.setAttribute('role', 'tabpanel')
134+
const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0
135+
panelContent.classList.toggle('panel-active', thisPanelIsActive)
136+
panelContent.id = p.id
137+
panelContent.setAttribute('aria-labelledby', p.id)
138+
Array.from(p.content).forEach(el => panelContent.appendChild(el))
139+
return panelContent
140+
})
141+
.forEach(el => res.appendChild(el))
142+
143+
return res
144+
}
145+
146+
/*
147+
* Update selected panel for panelset or delete panelset from query string
148+
*
149+
* @param panelset Panelset ID to update in the search params
150+
* @param panel Panel ID of selected panel in panelset, or null to delete from search params
151+
* @param params Current params object, or params from window.location.search
152+
*/
153+
function updateSearchParams (panelset, panel, params = new URLSearchParams(window.location.search)) {
154+
if (panel) {
155+
params.set(panelset, panel)
156+
} else {
157+
params.delete(panelset)
158+
}
159+
return params
160+
}
161+
162+
/*
163+
* Update the URL to match params
164+
*/
165+
const updateUrl = (params) => {
166+
if (typeof params === 'undefined') return
167+
params = params.toString() ? ('?' + params.toString()) : ''
168+
const { pathname, hash } = window.location
169+
const uri = pathname + params + hash
170+
window.history.replaceState(uri, '', uri)
171+
}
172+
173+
const togglePanel = (clicked) => {
174+
if (clicked.nodeName.toUpperCase() === 'A') {
175+
clicked = clicked.parentElement
176+
}
177+
if (!clicked.classList.contains('panel-tab')) return
178+
if (clicked.classList.contains('panel-tab-active')) return
179+
180+
const tabs = clicked.parentNode
181+
.querySelectorAll('.panel-tab')
182+
const panels = clicked.parentNode.parentNode
183+
.querySelectorAll('.panel')
184+
const panelTabClicked = clicked.children[0].getAttribute('aria-controls')
185+
const panelClicked = clicked.parentNode.parentNode.id
186+
187+
Array.from(tabs)
188+
.forEach(t => {
189+
t.classList.remove('panel-tab-active')
190+
t.removeAttribute('aria-selected')
191+
})
192+
Array.from(panels)
193+
.forEach(p => {
194+
const active = p.id === panelTabClicked
195+
p.classList.toggle('panel-active', active)
196+
// make inactive panels inaccessible by keyboard navigation
197+
if (active) {
198+
p.removeAttribute('tabIndex')
199+
p.removeAttribute('aria-hidden')
200+
} else {
201+
p.setAttribute('tabIndex', -1)
202+
p.setAttribute('aria-hidden', true)
203+
}
204+
})
205+
206+
clicked.classList.add('panel-tab-active')
207+
clicked.setAttribute('aria-selected', true)
208+
209+
// emit window resize event to trick html widgets into fitting to the panel width
210+
window.dispatchEvent(new Event('resize'))
211+
212+
// update query string
213+
const params = updateSearchParams(panelClicked, panelTabClicked)
214+
updateUrl(params)
215+
}
216+
217+
const initPanelSet = (panelset, idx) => {
218+
let panels = Array.from(panelset.querySelectorAll('.panel'))
219+
if (!panels.length && panelset.matches('.section[class*="level"]')) {
220+
// we're in tabset-alike R Markdown
221+
const panelsetLevel = [...panelset.classList]
222+
.filter(s => s.match(/^level/))[0]
223+
.replace('level', '')
224+
225+
// move children that aren't inside a section up above the panelset
226+
Array.from(panelset.children).forEach(function (el) {
227+
if (el.matches('div.section[class*="level"]')) return
228+
panelset.parentElement.insertBefore(el, panelset)
229+
})
230+
231+
// panels are all .sections with .level<panelsetLevel + 1>
232+
const panelLevel = +panelsetLevel + 1
233+
panels = Array.from(panelset.querySelectorAll(`.section.level${panelLevel}`))
234+
}
235+
236+
if (!panels.length) return
237+
238+
const contents = panels.map(processPanelItem).filter(o => o !== null)
239+
const newPanelSet = reflowPanelSet(contents, idx)
240+
panelset.parentNode.insertBefore(newPanelSet, panelset)
241+
panelset.parentNode.removeChild(panelset)
242+
243+
// click and touch events
244+
const panelTabs = newPanelSet.querySelector('.panel-tabs');
245+
['click', 'touchend'].forEach(eventType => {
246+
panelTabs.addEventListener(eventType, function (ev) {
247+
togglePanel(ev.target)
248+
ev.stopPropagation()
249+
})
250+
})
251+
panelTabs.addEventListener('touchmove', function (ev) {
252+
ev.preventDefault()
253+
})
254+
255+
// key events
256+
newPanelSet
257+
.querySelector('.panel-tabs')
258+
.addEventListener('keydown', (ev) => {
259+
const self = ev.currentTarget.querySelector('.panel-tab-active')
260+
if (ev.code === 'Space' || ev.code === 'Enter') {
261+
togglePanel(ev.target)
262+
ev.stopPropagation()
263+
} else if (ev.code === 'ArrowLeft' && self.previousSibling) {
264+
togglePanel(self.previousSibling)
265+
self.previousSibling.focus()
266+
ev.stopPropagation()
267+
} else if (ev.code === 'ArrowRight' && self.nextSibling) {
268+
togglePanel(self.nextSibling)
269+
self.nextSibling.focus()
270+
ev.stopPropagation()
271+
}
272+
})
273+
274+
return panels
275+
}
276+
277+
// initialize panels
278+
Array.from(document.querySelectorAll('.panelset')).map(initPanelSet)
279+
280+
if (typeof slideshow !== 'undefined') {
281+
const getVisibleActivePanelInfo = () => {
282+
const slidePanels = document.querySelectorAll('.remark-visible .panel-tab-active')
283+
284+
if (!slidePanels.length) return null
285+
286+
return slidePanels.map(panel => {
287+
return {
288+
panel,
289+
panelId: panel.children[0].getAttribute('aria-controls'),
290+
panelSetId: panel.parentNode.parentNode.id
291+
}
292+
})
293+
}
294+
295+
slideshow.on('hideSlide', slide => {
296+
// clear focus if we had a panel-tab selected
297+
document.activeElement.blur()
298+
299+
// clear search query for panelsets in current slide
300+
const params = [...document.querySelectorAll('.remark-visible .panelset')]
301+
.reduce(function (params, panelset) {
302+
return updateSearchParams(panelset.id, null, params)
303+
}, new URLSearchParams(window.location.search))
304+
305+
updateUrl(params)
306+
})
307+
308+
slideshow.on('afterShowSlide', slide => {
309+
const slidePanels = getVisibleActivePanelInfo()
310+
311+
if (slidePanels) {
312+
// only first panel gets focus
313+
slidePanels[0].panel.focus()
314+
// but still update the url to reflect all active panels
315+
const params = slidePanels.reduce(
316+
function (params, { panelId, panelSetId }) {
317+
return updateSearchParams(panelSetId, panelId, params)
318+
},
319+
new URLSearchParams(window.location.search)
320+
)
321+
updateUrl(params)
322+
}
323+
})
324+
}
325+
})
326+
})()

0 commit comments

Comments
 (0)