nuxt-ui-basekit 0.1.1 → 0.2.1
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/README.md +93 -79
- package/app/assets/css/basekit.css +23 -24
- package/app/components/BaseKitBackLink.vue +9 -9
- package/app/components/BaseKitChoiceCard.vue +19 -19
- package/app/components/BaseKitConfirmModal.vue +7 -7
- package/app/components/BaseKitDataTable.vue +30 -30
- package/app/components/BaseKitEmptyState.vue +7 -7
- package/app/components/BaseKitFileUpload.vue +5 -5
- package/app/components/BaseKitIconPicker.vue +8 -8
- package/app/components/BaseKitMarkdownEditor.vue +11 -9
- package/app/components/BaseKitPending.vue +15 -16
- package/app/components/BaseKitRecordPicker.vue +14 -14
- package/app/components/BaseKitSettingRow.vue +15 -14
- package/app/components/BaseKitStatTile.vue +8 -8
- package/app/components/BaseKitTabs.vue +33 -34
- package/app/components/BaseKitViewLink.vue +14 -14
- package/app/components/charts/BaseKitChartBars.vue +8 -9
- package/app/components/charts/BaseKitChartColumns.vue +30 -31
- package/app/components/charts/BaseKitChartDonut.vue +18 -18
- package/app/components/charts/BaseKitChartFigure.vue +10 -10
- package/app/components/charts/BaseKitChartMeter.vue +4 -4
- package/app/composables/useBaseKit.ts +68 -68
- package/app/composables/useChartPalette.ts +14 -14
- package/app/composables/useChartWidth.ts +10 -10
- package/app/composables/useConfirm.ts +3 -3
- package/app/stores/confirm.ts +16 -16
- package/app/utils/html-to-markdown.ts +14 -16
- package/app/utils/markdown.ts +8 -7
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Measures a container so an SVG can draw in real pixels instead of going
|
|
5
|
+
* through a scaled `viewBox`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The difference is the type: a `viewBox` scaled up to container width drags
|
|
8
|
+
* axis labels and values up with it — a wide card then shows 15px text where
|
|
9
|
+
* 11 was meant. With a measured width, text stays text and only the geometry
|
|
10
|
+
* grows.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Before mount (SSR, first frame) `fallback` applies. The chart is therefore
|
|
13
|
+
* there straight away and settles once, rather than flickering.
|
|
14
14
|
*/
|
|
15
15
|
export function useChartWidth(fallback = 640): {
|
|
16
16
|
el: Ref<HTMLElement | null>
|
|
@@ -25,8 +25,8 @@ export function useChartWidth(fallback = 640): {
|
|
|
25
25
|
|
|
26
26
|
observer = new ResizeObserver((entries) => {
|
|
27
27
|
const measured = entries[0]?.contentRect.width ?? 0
|
|
28
|
-
//
|
|
29
|
-
//
|
|
28
|
+
// Below 240px the geometry becomes useless; draw narrow and let the
|
|
29
|
+
// container scroll instead.
|
|
30
30
|
if (measured > 0) width.value = Math.max(240, Math.round(measured))
|
|
31
31
|
})
|
|
32
32
|
observer.observe(el.value)
|
|
@@ -2,10 +2,10 @@ import { useConfirmStore } from '../stores/confirm'
|
|
|
2
2
|
import type { BaseKitConfirmOptions } from '../stores/confirm'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A confirmation prompt in place of the native `window.confirm`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Opens the globally mounted `BaseKitConfirmModal` and returns a promise that
|
|
8
|
+
* resolves to `true` or `false`.
|
|
9
9
|
*
|
|
10
10
|
* const confirm = useConfirm()
|
|
11
11
|
* if (!(await confirm({ description: t('…'), color: 'error', confirmLabel: t('common.delete') }))) return
|
package/app/stores/confirm.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** Options for one confirmation prompt. */
|
|
5
5
|
export interface BaseKitConfirmOptions {
|
|
6
|
-
/**
|
|
6
|
+
/** Dialog title. Falls back to the `confirmTitle` label. */
|
|
7
7
|
title?: string
|
|
8
|
-
/**
|
|
8
|
+
/** Explanation or consequence — spell it out for anything destructive. */
|
|
9
9
|
description?: string
|
|
10
|
-
/**
|
|
10
|
+
/** Label of the confirm button. Falls back to the `confirm` label. */
|
|
11
11
|
confirmLabel?: string
|
|
12
|
-
/**
|
|
12
|
+
/** Label of the cancel button. Falls back to the `cancel` label. */
|
|
13
13
|
cancelLabel?: string
|
|
14
|
-
/**
|
|
14
|
+
/** Colour of the confirm button — `error` for destructive actions. */
|
|
15
15
|
color?: 'error' | 'primary'
|
|
16
|
-
/**
|
|
16
|
+
/** Optional icon on the confirm button. */
|
|
17
17
|
icon?: string
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* The confirmation store — replaces native `window.confirm` dialogs with a
|
|
22
|
+
* themed, dark-mode-capable prompt.
|
|
23
23
|
*
|
|
24
|
-
* `ask()`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* `ask()` opens the globally mounted `BaseKitConfirmModal` and returns a
|
|
25
|
+
* promise resolving to `true` (confirmed) or `false` (cancelled or closed).
|
|
26
|
+
* The resolver lives outside reactivity so exactly one answer goes back per
|
|
27
|
+
* prompt.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
29
|
+
* Used through the `useConfirm()` composable:
|
|
30
30
|
* const confirm = useConfirm()
|
|
31
31
|
* if (!(await confirm({ description: t('…'), color: 'error' }))) return
|
|
32
32
|
*/
|
|
@@ -36,7 +36,7 @@ export const useConfirmStore = defineStore('basekit:confirm', () => {
|
|
|
36
36
|
let resolver: ((value: boolean) => void) | null = null
|
|
37
37
|
|
|
38
38
|
function ask(opts: BaseKitConfirmOptions = {}): Promise<boolean> {
|
|
39
|
-
//
|
|
39
|
+
// A prompt still running is resolved as cancelled.
|
|
40
40
|
resolver?.(false)
|
|
41
41
|
options.value = opts
|
|
42
42
|
open.value = true
|
|
@@ -45,7 +45,7 @@ export const useConfirmStore = defineStore('basekit:confirm', () => {
|
|
|
45
45
|
})
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
48
|
+
/** Answer the prompt and close the dialog. */
|
|
49
49
|
function settle(value: boolean): void {
|
|
50
50
|
open.value = false
|
|
51
51
|
resolver?.(value)
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* - Tiptap speichert HTML → hier nach Markdown serialisieren.
|
|
6
|
-
* - Legacy-Text-Blöcke mit rohem `config.html` einmalig nach Markdown wandeln.
|
|
2
|
+
* HTML to Markdown, through turndown. Kept apart from `markdown.ts` on
|
|
3
|
+
* purpose: that one may end up in a public bundle and should not drag turndown
|
|
4
|
+
* along, while this one is only ever needed where something is edited.
|
|
7
5
|
*
|
|
8
|
-
* **turndown
|
|
9
|
-
* CommonJS;
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* **turndown is loaded lazily, and that is deliberate.** The package is
|
|
7
|
+
* CommonJS; an import at module level lands in the server bundle and throws
|
|
8
|
+
* "require is not defined in ES module scope" while rendering. The editor runs
|
|
9
|
+
* in the browser anyway — on the server this function is never called.
|
|
12
10
|
*/
|
|
13
11
|
type Turndown = { turndown: (html: string) => string }
|
|
14
12
|
|
|
@@ -18,14 +16,14 @@ function service(): Turndown | null {
|
|
|
18
16
|
if (instance) return instance
|
|
19
17
|
if (import.meta.server) return null
|
|
20
18
|
|
|
21
|
-
//
|
|
22
|
-
// `prepare()`
|
|
19
|
+
// Synchronous access to an already loaded module: the editor calls
|
|
20
|
+
// `prepare()` on mount, well before anything can be saved.
|
|
23
21
|
return instance
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
25
|
+
* Preloads turndown in the browser. The editor components call this on mount
|
|
26
|
+
* so {@link htmlToMarkdown} can stay synchronous afterwards.
|
|
29
27
|
*/
|
|
30
28
|
export async function prepareHtmlToMarkdown(): Promise<void> {
|
|
31
29
|
if (instance || import.meta.server) return
|
|
@@ -40,8 +38,8 @@ export async function prepareHtmlToMarkdown(): Promise<void> {
|
|
|
40
38
|
export function htmlToMarkdown(html?: string | null): string {
|
|
41
39
|
if (!html) return ''
|
|
42
40
|
const td = service()
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
41
|
+
// Without turndown loaded — on the server, or when prepare() was forgotten
|
|
42
|
+
// — return the original rather than throwing: the text would otherwise be
|
|
43
|
+
// lost on save.
|
|
46
44
|
return td ? td.turndown(String(html)).trim() : String(html)
|
|
47
45
|
}
|
package/app/utils/markdown.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import MarkdownIt from 'markdown-it'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Markdown
|
|
4
|
+
* Markdown to HTML.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `html: false` on purpose: raw HTML inside the Markdown is NOT passed
|
|
7
|
+
* through. That closes the biggest XSS hole without a separate sanitizer —
|
|
8
|
+
* markdown-it also blocks `javascript:` links through its default
|
|
9
|
+
* `validateLink`. `linkify` makes bare URLs clickable, `typographer` smooths
|
|
10
|
+
* quotes and dashes.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Shared between display and editor preview, so that editing and output look
|
|
13
|
+
* the same.
|
|
13
14
|
*/
|
|
14
15
|
const md: MarkdownIt = new MarkdownIt({
|
|
15
16
|
html: false,
|
package/package.json
CHANGED