picocrank 1.22.0 → 1.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/router.js +10 -0
- package/vue/components/QuickSearch.vue +27 -3
- package/vue/views/App.vue +1 -0
- package/vue/views/DialogExample.vue +123 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picocrank",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.2",
|
|
4
4
|
"description": "A set of zero dependency Vue components built on the Femtocrank global CSS theme.",
|
|
5
5
|
"main": "vite.config.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"@hugeicons/core-free-icons": "^4.2.3",
|
|
22
22
|
"@hugeicons/vue": "^1.0.7",
|
|
23
23
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
24
|
-
"femtocrank": "^2.5.
|
|
24
|
+
"femtocrank": "^2.5.2",
|
|
25
25
|
"unplugin-vue-components": "^32.1.0",
|
|
26
|
-
"vite": "^8.2.
|
|
27
|
-
"vue": "^3.5.
|
|
26
|
+
"vite": "^8.2.1",
|
|
27
|
+
"vue": "^3.5.41",
|
|
28
28
|
"vue-router": "^5.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"semantic-release": "^25.0.
|
|
31
|
+
"semantic-release": "^25.0.9"
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/jamesread/picocrank#readme"
|
|
34
34
|
}
|
package/router.js
CHANGED
|
@@ -14,6 +14,7 @@ import { LayoutGridIcon } from '@hugeicons/core-free-icons';
|
|
|
14
14
|
import { StatusIcon } from '@hugeicons/core-free-icons';
|
|
15
15
|
import { PaintBoardIcon } from '@hugeicons/core-free-icons';
|
|
16
16
|
import { UserIcon } from '@hugeicons/core-free-icons';
|
|
17
|
+
import { InformationCircleIcon } from '@hugeicons/core-free-icons';
|
|
17
18
|
|
|
18
19
|
const routes = [
|
|
19
20
|
{
|
|
@@ -136,6 +137,15 @@ const routes = [
|
|
|
136
137
|
icon: StatusIcon,
|
|
137
138
|
}
|
|
138
139
|
},
|
|
140
|
+
{
|
|
141
|
+
name: 'DialogExample',
|
|
142
|
+
path: '/dialog-example',
|
|
143
|
+
component: () => import('./vue/views/DialogExample.vue'),
|
|
144
|
+
meta: {
|
|
145
|
+
title: 'Dialog',
|
|
146
|
+
icon: InformationCircleIcon,
|
|
147
|
+
}
|
|
148
|
+
},
|
|
139
149
|
{
|
|
140
150
|
name: 'TabsExample',
|
|
141
151
|
path: '/tabs-example',
|
|
@@ -362,11 +362,35 @@ function selectItem(item) {
|
|
|
362
362
|
close()
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
function escapeHtml(value) {
|
|
366
|
+
return String(value)
|
|
367
|
+
.replace(/&/g, '&')
|
|
368
|
+
.replace(/</g, '<')
|
|
369
|
+
.replace(/>/g, '>')
|
|
370
|
+
.replace(/"/g, '"')
|
|
371
|
+
.replace(/'/g, ''')
|
|
372
|
+
}
|
|
373
|
+
|
|
365
374
|
function highlightText(text, query) {
|
|
366
|
-
if (
|
|
375
|
+
if (text == null || text === '') return ''
|
|
376
|
+
|
|
377
|
+
const source = String(text)
|
|
378
|
+
if (!query) return escapeHtml(source)
|
|
379
|
+
|
|
380
|
+
const regex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi')
|
|
381
|
+
let result = ''
|
|
382
|
+
let lastIndex = 0
|
|
383
|
+
|
|
384
|
+
for (const match of source.matchAll(regex)) {
|
|
385
|
+
const start = match.index
|
|
386
|
+
const matched = match[0]
|
|
387
|
+
result += escapeHtml(source.slice(lastIndex, start))
|
|
388
|
+
result += `<mark>${escapeHtml(matched)}</mark>`
|
|
389
|
+
lastIndex = start + matched.length
|
|
390
|
+
}
|
|
367
391
|
|
|
368
|
-
|
|
369
|
-
return
|
|
392
|
+
result += escapeHtml(source.slice(lastIndex))
|
|
393
|
+
return result
|
|
370
394
|
}
|
|
371
395
|
|
|
372
396
|
function addItem(item) {
|
package/vue/views/App.vue
CHANGED
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
|
|
199
199
|
navigation.value.addSection('Feedback & status', { name: 'nav-feedback' })
|
|
200
200
|
navigation.value.addRouterLink('StatusExample', 'Status & notifications')
|
|
201
|
+
navigation.value.addRouterLink('DialogExample', 'Dialog')
|
|
201
202
|
|
|
202
203
|
navigation.value.addSection('Patterns', { name: 'nav-patterns' })
|
|
203
204
|
navigation.value.addCallback('Callback example', helloWorld, { icon: Pin02Icon })
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Section
|
|
3
|
+
title="Dialog"
|
|
4
|
+
subtitle="Native HTML dialog element with showModal()"
|
|
5
|
+
>
|
|
6
|
+
<p>
|
|
7
|
+
The <code><dialog></code> element provides accessible modal and
|
|
8
|
+
non-modal overlays without a third-party library. Femtocrank styles the
|
|
9
|
+
element globally; use <code>showModal()</code> for a modal with backdrop,
|
|
10
|
+
or <code>show()</code> for a non-modal panel.
|
|
11
|
+
</p>
|
|
12
|
+
</Section>
|
|
13
|
+
|
|
14
|
+
<Section title="Modal dialog" subtitle="Opens with showModal() and closes on backdrop click">
|
|
15
|
+
<div role="toolbar" class="dialog-actions">
|
|
16
|
+
<button type="button" @click="openModal">Open modal</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<dialog
|
|
20
|
+
ref="modalRef"
|
|
21
|
+
class="example-dialog"
|
|
22
|
+
@close="onModalClose"
|
|
23
|
+
@click="onBackdropClick($event, modalRef)"
|
|
24
|
+
>
|
|
25
|
+
<div class="dialog-panel" @click.stop>
|
|
26
|
+
<h3>Confirm action</h3>
|
|
27
|
+
<p>
|
|
28
|
+
This modal uses the native dialog API. Press Escape or click outside
|
|
29
|
+
to dismiss, or choose an action below.
|
|
30
|
+
</p>
|
|
31
|
+
<form method="dialog" class="dialog-form">
|
|
32
|
+
<div role="toolbar" class="dialog-actions">
|
|
33
|
+
<button type="submit" value="cancel" class="neutral">Cancel</button>
|
|
34
|
+
<button type="submit" value="confirm" class="good">Confirm</button>
|
|
35
|
+
</div>
|
|
36
|
+
</form>
|
|
37
|
+
</div>
|
|
38
|
+
</dialog>
|
|
39
|
+
|
|
40
|
+
<p v-if="modalResult" class="subtle">{{ modalResult }}</p>
|
|
41
|
+
</Section>
|
|
42
|
+
|
|
43
|
+
<Section title="Non-modal dialog" subtitle="Stays open while the page remains interactive">
|
|
44
|
+
<div role="toolbar" class="dialog-actions">
|
|
45
|
+
<button type="button" class="neutral" @click="openPanel">Open panel</button>
|
|
46
|
+
<button type="button" class="bad" :disabled="!panelRef?.open" @click="closePanel">
|
|
47
|
+
Close panel
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<dialog ref="panelRef" class="example-dialog example-dialog-panel">
|
|
52
|
+
<div class="dialog-panel">
|
|
53
|
+
<h3>Side note</h3>
|
|
54
|
+
<p>
|
|
55
|
+
Non-modal dialogs use <code>show()</code> instead of
|
|
56
|
+
<code>showModal()</code>. They do not trap focus or dim the page.
|
|
57
|
+
</p>
|
|
58
|
+
<button type="button" class="neutral" @click="closePanel">Dismiss</button>
|
|
59
|
+
</div>
|
|
60
|
+
</dialog>
|
|
61
|
+
</Section>
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<script setup>
|
|
65
|
+
import { ref } from 'vue'
|
|
66
|
+
import Section from '../components/Section.vue'
|
|
67
|
+
|
|
68
|
+
const modalRef = ref(null)
|
|
69
|
+
const panelRef = ref(null)
|
|
70
|
+
const modalResult = ref('')
|
|
71
|
+
|
|
72
|
+
function openModal() {
|
|
73
|
+
modalRef.value?.showModal()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function onModalClose(event) {
|
|
77
|
+
const value = event.target?.returnValue
|
|
78
|
+
if (value === 'confirm') {
|
|
79
|
+
modalResult.value = 'Action confirmed.'
|
|
80
|
+
} else {
|
|
81
|
+
modalResult.value = 'Dialog closed without confirming.'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function onBackdropClick(event, dialog) {
|
|
86
|
+
if (event.target === dialog) {
|
|
87
|
+
dialog.close('cancel')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function openPanel() {
|
|
92
|
+
panelRef.value?.show()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function closePanel() {
|
|
96
|
+
panelRef.value?.close()
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<style scoped>
|
|
101
|
+
.dialog-actions {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: 0.5rem;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.dialog-panel h3 {
|
|
108
|
+
margin: 0 0 0.5rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.dialog-panel p {
|
|
112
|
+
margin: 0 0 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.dialog-form {
|
|
116
|
+
margin: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.example-dialog-panel {
|
|
120
|
+
margin-block-start: 1rem;
|
|
121
|
+
max-width: 24rem;
|
|
122
|
+
}
|
|
123
|
+
</style>
|