@c2-markdown-ui/vue 0.0.0-stage → 0.4.4

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.
Files changed (61) hide show
  1. package/README.md +112 -2
  2. package/dist/MarkdownUI.vue.d.ts +15 -0
  3. package/dist/favicon.ico +0 -0
  4. package/dist/index.css +1 -0
  5. package/dist/index.d.ts +23 -0
  6. package/dist/index.js +10843 -0
  7. package/dist/index.umd.cjs +53 -0
  8. package/dist/widgets/Button.vue.d.ts +19 -0
  9. package/dist/widgets/ButtonGroup.vue.d.ts +13 -0
  10. package/dist/widgets/Cascader.vue.d.ts +48 -0
  11. package/dist/widgets/Chart.vue.d.ts +24 -0
  12. package/dist/widgets/DatePicker.vue.d.ts +42 -0
  13. package/dist/widgets/Form.vue.d.ts +59 -0
  14. package/dist/widgets/Incomplete.vue.d.ts +2 -0
  15. package/dist/widgets/MultipleChoiceQuestion.vue.d.ts +20 -0
  16. package/dist/widgets/NumberInput.vue.d.ts +37 -0
  17. package/dist/widgets/Quiz.vue.d.ts +39 -0
  18. package/dist/widgets/RadioGroup.vue.d.ts +43 -0
  19. package/dist/widgets/Select.vue.d.ts +27 -0
  20. package/dist/widgets/SelectMulti.vue.d.ts +38 -0
  21. package/dist/widgets/ShortAnswerQuestion.vue.d.ts +22 -0
  22. package/dist/widgets/Slider.vue.d.ts +21 -0
  23. package/dist/widgets/Switch.vue.d.ts +28 -0
  24. package/dist/widgets/Text.vue.d.ts +22 -0
  25. package/dist/widgets/TextInput.vue.d.ts +32 -0
  26. package/dist/widgets/TreeSelect.vue.d.ts +109 -0
  27. package/dist/widgets/Widget.vue.d.ts +8 -0
  28. package/dist/widgets/chartRemoteData.d.ts +17 -0
  29. package/dist/widgets/index.d.ts +20 -0
  30. package/dist/widgets/remoteChoices.d.ts +22 -0
  31. package/dist/widgets/treeSelectTypes.d.ts +43 -0
  32. package/package.json +46 -4
  33. package/src/App.vue +11 -0
  34. package/src/lib/MarkdownUI.vue +105 -0
  35. package/src/lib/index.ts +28 -0
  36. package/src/lib/widgets/Button.vue +47 -0
  37. package/src/lib/widgets/ButtonGroup.vue +43 -0
  38. package/src/lib/widgets/Cascader.vue +475 -0
  39. package/src/lib/widgets/Chart.vue +287 -0
  40. package/src/lib/widgets/DatePicker.vue +659 -0
  41. package/src/lib/widgets/Form.vue +479 -0
  42. package/src/lib/widgets/Incomplete.vue +56 -0
  43. package/src/lib/widgets/MultipleChoiceQuestion.vue +73 -0
  44. package/src/lib/widgets/NumberInput.vue +179 -0
  45. package/src/lib/widgets/Quiz.vue +205 -0
  46. package/src/lib/widgets/RadioGroup.vue +237 -0
  47. package/src/lib/widgets/Select.vue +279 -0
  48. package/src/lib/widgets/SelectMulti.vue +145 -0
  49. package/src/lib/widgets/ShortAnswerQuestion.vue +79 -0
  50. package/src/lib/widgets/Slider.vue +61 -0
  51. package/src/lib/widgets/Switch.vue +68 -0
  52. package/src/lib/widgets/Text.vue +59 -0
  53. package/src/lib/widgets/TextInput.vue +83 -0
  54. package/src/lib/widgets/TreeSelect.vue +872 -0
  55. package/src/lib/widgets/Widget.vue +185 -0
  56. package/src/lib/widgets/chartRemoteData.ts +84 -0
  57. package/src/lib/widgets/index.ts +20 -0
  58. package/src/lib/widgets/remoteChoices.ts +64 -0
  59. package/src/lib/widgets/treeSelectTypes.ts +49 -0
  60. package/src/lib/widgets.css +1754 -0
  61. package/src/main.ts +4 -0
@@ -0,0 +1,8 @@
1
+ interface WidgetProps {
2
+ id: string;
3
+ content: string;
4
+ /** 外部传入的表单初始值(encodeURIComponent 后的 JSON 字符串),由 MarkdownUI 注入 */
5
+ data?: string;
6
+ }
7
+ declare const _default: import("vue").DefineComponent<WidgetProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<WidgetProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ export default _default;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 图表远端数据源解析:
3
+ * - CSV 文本(与 DSL 内嵌 CSV 同格式:首行 header「标签列,系列1,系列2...」,其后数据行)
4
+ * - JSON:支持 { labels, datasets:[{label,data}] } 及常见包装({data|result} 内嵌)
5
+ */
6
+ export interface ChartDataset {
7
+ label: string;
8
+ data: number[];
9
+ }
10
+ export interface RemoteChartData {
11
+ labels: string[];
12
+ datasets: ChartDataset[];
13
+ }
14
+ /** 解析 CSV 文本为图表数据(header 至少 2 列,数据列须为数字) */
15
+ export declare function parseChartCsv(text: string): RemoteChartData | null;
16
+ /** 归一化 JSON 响应为图表数据(labels + datasets) */
17
+ export declare function normalizeRemoteChartData(json: any): RemoteChartData | null;
@@ -0,0 +1,20 @@
1
+ export { default as Button } from './Button.vue';
2
+ export { default as ButtonGroup } from './ButtonGroup.vue';
3
+ export { default as Cascader } from './Cascader.vue';
4
+ export { default as Chart } from './Chart.vue';
5
+ export { default as Form } from './Form.vue';
6
+ export { default as Incomplete } from './Incomplete.vue';
7
+ export { default as MultipleChoiceQuestion } from './MultipleChoiceQuestion.vue';
8
+ export { default as Select } from './Select.vue';
9
+ export { default as SelectMulti } from './SelectMulti.vue';
10
+ export { default as TreeSelect } from './TreeSelect.vue';
11
+ export { default as ShortAnswerQuestion } from './ShortAnswerQuestion.vue';
12
+ export { default as Slider } from './Slider.vue';
13
+ export { default as Switch } from './Switch.vue';
14
+ export { default as TextInput } from './TextInput.vue';
15
+ export { default as NumberInput } from './NumberInput.vue';
16
+ export { default as DatePicker } from './DatePicker.vue';
17
+ export { default as Text } from './Text.vue';
18
+ export { default as Quiz } from './Quiz.vue';
19
+ export { default as RadioGroup } from './RadioGroup.vue';
20
+ export { default as Widget } from './Widget.vue';
@@ -0,0 +1,22 @@
1
+ /** 远端选项:value 是表单实际取值,label 是展示文案 */
2
+ export interface RemoteChoice {
3
+ value: string;
4
+ label: string;
5
+ }
6
+ /**
7
+ * 兼容常见远端响应格式并归一化为 {value,label}:
8
+ * - ["测试"] → value=label="测试"
9
+ * - [{key:"test",label:"测试"}] → value="test"(表单取值),label="测试"(展示)
10
+ * - [{value:"test",label:"测试"}] → value="test",label="测试"
11
+ * - {data:[...]} / {options:[...]} / {choices:[...]} / {items:[...]} → 取内嵌数组
12
+ *
13
+ * 对象条目取值优先级:value > key > id > name;展示优先级:label > name > text > value。
14
+ */
15
+ export declare function normalizeRemoteChoices(json: any): RemoteChoice[];
16
+ /** 解析本地 choices 条目的 key:label 格式:"bj:北京" → {value:"bj",label:"北京"};不符规则时 value=label=原文 */
17
+ export declare function parseChoice(c: string): RemoteChoice;
18
+ /**
19
+ * 把本地 choices / default 值(可能写的是展示文案或 key:label 原文)映射到远端 value 体系:
20
+ * 条目本身已是 value 优先,字符串则用 label 匹配远端条目回查 value。
21
+ */
22
+ export declare function resolveChoiceValue(c: string, remote: RemoteChoice[]): string;
@@ -0,0 +1,43 @@
1
+ /** 远端/静态 data 原始节点(容错:value/key、label/name 等字段映射) */
2
+ export interface TreeNodeRaw {
3
+ value?: string | number;
4
+ key?: string | number;
5
+ label?: string | number;
6
+ children?: TreeNodeRaw[];
7
+ loaded?: boolean;
8
+ [k: string]: any;
9
+ }
10
+ /** 内部归一化树节点 */
11
+ export interface TreeSelectNode {
12
+ value: string;
13
+ label: string;
14
+ children: TreeSelectNode[];
15
+ loaded: boolean;
16
+ depth: number;
17
+ }
18
+ /** TreeSelect 组件 props */
19
+ export interface TreeSelectProps {
20
+ choices: string[];
21
+ data?: TreeNodeRaw[];
22
+ url?: string;
23
+ lazy?: boolean;
24
+ /** 多选模式(antd 默认单选,multiple 开启多选) */
25
+ multiple?: boolean;
26
+ /** 父子节点选中不关联(antd checkStrictly) */
27
+ checkStrictly?: boolean;
28
+ /** 选中值回显策略:all / parent / child(antd SHOW_ALL/SHOW_PARENT/SHOW_CHILD) */
29
+ showCheckedStrategy?: string;
30
+ /** 触发器最多显示的标签数,超出 +N(antd maxTagCount) */
31
+ maxTagCount?: number;
32
+ /** 树连接线(antd treeLine) */
33
+ treeLine?: boolean;
34
+ label?: string;
35
+ default?: string | string[];
36
+ placeholder?: string;
37
+ hint?: string;
38
+ required?: boolean;
39
+ message?: string;
40
+ error?: string;
41
+ disabled?: boolean;
42
+ searchable?: boolean;
43
+ }
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@c2-markdown-ui/vue",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.4.4",
4
+ "description": "Vue renderer for markdown-ui",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./widgets.css": "./src/lib/widgets.css"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "scripts": {
20
+ "build": "vue-tsc -p tsconfig.build.json && vite build",
21
+ "dev": "vite build --watch",
22
+ "demo": "node demo/server.js",
23
+ "prepublishOnly": "npm run build",
24
+ "typecheck": "vue-tsc --noEmit"
25
+ },
26
+ "keywords": [
27
+ "vue",
28
+ "markdown",
29
+ "widgets",
30
+ "ui"
31
+ ],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "peerDependencies": {
35
+ "vue": "^3.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@vitejs/plugin-vue": "^6.0.1",
39
+ "vue-tsc": "^3.0.6"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@tsconfig/node22": "^22.0.2",
46
+ "@vue/tsconfig": "^0.8.1"
47
+ }
48
+ }
package/src/App.vue ADDED
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <h1>You did it!</h1>
5
+ <p>
6
+ Visit <a href="https://vuejs.org/" target="_blank" rel="noopener">vuejs.org</a> to read the
7
+ documentation
8
+ </p>
9
+ </template>
10
+
11
+ <style scoped></style>
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <div ref="container" class="widget-container">
3
+ <div v-html="processedHtml"></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { ref, computed, onMounted, onUnmounted, defineEmits, defineProps } from 'vue'
9
+ import Widget from './widgets/Widget.vue'
10
+
11
+ interface MarkdownUIProps {
12
+ html: string
13
+ /** 表单初始值:{ 字段id: 值 },作用于页面中所有 form widget(仅取各自 fields 声明的 id);优先级高于字段 default */
14
+ data?: Record<string, unknown>
15
+ }
16
+
17
+ interface MarkdownUIEvents {
18
+ widgetEvent: [data: any]
19
+ /** 表单提交事件:form 校验通过并点击提交时触发(独立于 widgetEvent 的 change 流) */
20
+ submit: [data: any]
21
+ /** 表单字段级统计事件:任一表单项被填写时触发,detail.stats 含 visibleCount / filledCount / values */
22
+ formChange: [data: any]
23
+ }
24
+
25
+ const props = defineProps<MarkdownUIProps>()
26
+ const emit = defineEmits<MarkdownUIEvents>()
27
+
28
+ const container = ref<HTMLDivElement>()
29
+
30
+ /** 向 <markdown-ui-widget> 占位标签注入 data-widget-data 属性,把外部初始值带过 custom element 边界 */
31
+ const processedHtml = computed(() => {
32
+ const data = props.data
33
+ if (!data || Object.keys(data).length === 0) return props.html
34
+ const encoded = encodeURIComponent(JSON.stringify(data))
35
+ return props.html.replace(/<markdown-ui-widget\b/g, `<markdown-ui-widget data-widget-data="${encoded}"`)
36
+ })
37
+
38
+ let MarkdownUIWidgetElement: typeof HTMLElement | undefined
39
+
40
+ if (typeof HTMLElement !== 'undefined') {
41
+ MarkdownUIWidgetElement = class extends HTMLElement {
42
+ private vueApp: any = null
43
+ private mountPoint: HTMLElement | null = null
44
+
45
+ connectedCallback() {
46
+ const id = this.getAttribute('id') || ''
47
+ const content = this.getAttribute('content') || ''
48
+ const data = this.getAttribute('data-widget-data') || ''
49
+
50
+ this.innerHTML = ''
51
+ const div = document.createElement('div')
52
+ this.appendChild(div)
53
+ this.mountPoint = div
54
+
55
+ import('vue').then(({ createApp }) => {
56
+ this.vueApp = createApp(Widget, { id, content, data })
57
+ this.vueApp.mount(div)
58
+ })
59
+ }
60
+
61
+ disconnectedCallback() {
62
+ if (this.vueApp && this.mountPoint) {
63
+ this.vueApp.unmount()
64
+ this.vueApp = null
65
+ this.mountPoint = null
66
+ }
67
+ }
68
+ }
69
+
70
+ if (typeof window !== 'undefined' && !customElements.get('markdown-ui-widget')) {
71
+ customElements.define('markdown-ui-widget', MarkdownUIWidgetElement)
72
+ }
73
+ }
74
+
75
+ const handleWidgetEvent = (e: Event) => {
76
+ const customEvent = e as CustomEvent<{id: string, value: unknown}>
77
+ emit('widgetEvent', customEvent)
78
+ }
79
+
80
+ const handleSubmit = (e: Event) => {
81
+ const customEvent = e as CustomEvent<{id: string, value: unknown}>
82
+ emit('submit', customEvent)
83
+ }
84
+
85
+ const handleFormChange = (e: Event) => {
86
+ const customEvent = e as CustomEvent<{id: string, stats: unknown}>
87
+ emit('formChange', customEvent)
88
+ }
89
+
90
+ onMounted(() => {
91
+ if (container.value) {
92
+ container.value.addEventListener('widget-event', handleWidgetEvent)
93
+ container.value.addEventListener('widget-submit', handleSubmit)
94
+ container.value.addEventListener('widget-form-change', handleFormChange)
95
+ }
96
+ })
97
+
98
+ onUnmounted(() => {
99
+ if (container.value) {
100
+ container.value.removeEventListener('widget-event', handleWidgetEvent)
101
+ container.value.removeEventListener('widget-submit', handleSubmit)
102
+ container.value.removeEventListener('widget-form-change', handleFormChange)
103
+ }
104
+ })
105
+ </script>
@@ -0,0 +1,28 @@
1
+ // 全局公共样式(widget-label-row / checkbox-group 等无 scoped 类,需消费者引入)
2
+ import './widgets.css'
3
+
4
+ // SFC scoped 样式:从包入口统一导入,消费方(Vite/Astro)按源码引入组件时会自动编译注入;
5
+ // 库构建(vite lib mode)产物 dist/index.css 与之分离,入口导入保证源码消费路径样式不丢失
6
+ import './widgets/Button.vue'
7
+ import './widgets/ButtonGroup.vue'
8
+ import './widgets/Cascader.vue'
9
+ import './widgets/Chart.vue'
10
+ import './widgets/DatePicker.vue'
11
+ import './widgets/Form.vue'
12
+ import './widgets/Incomplete.vue'
13
+ import './widgets/MultipleChoiceQuestion.vue'
14
+ import './widgets/NumberInput.vue'
15
+ import './widgets/Quiz.vue'
16
+ import './widgets/RadioGroup.vue'
17
+ import './widgets/Select.vue'
18
+ import './widgets/SelectMulti.vue'
19
+ import './widgets/ShortAnswerQuestion.vue'
20
+ import './widgets/Slider.vue'
21
+ import './widgets/Switch.vue'
22
+ import './widgets/Text.vue'
23
+ import './widgets/TextInput.vue'
24
+ import './widgets/TreeSelect.vue'
25
+ import './widgets/Widget.vue'
26
+
27
+ export { default as MarkdownUI } from './MarkdownUI.vue'
28
+ export * from './widgets/index'
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :class="`widget-button size-${size}`"
5
+ :style="buttonStyle"
6
+ @click="handleClick"
7
+ >
8
+ {{ label }}
9
+ </button>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed } from 'vue'
14
+ interface ButtonProps {
15
+ /** 按钮文字 */
16
+ label?: string
17
+ /** 背景色(十六进制或 CSS 颜色名) */
18
+ color?: string
19
+ /** 字体颜色(十六进制或 CSS 颜色名) */
20
+ textColor?: string
21
+ /** 尺寸:small / medium / large,默认 medium */
22
+ size?: 'small' | 'medium' | 'large'
23
+ }
24
+
25
+ interface ButtonEvents {
26
+ change: [value: string]
27
+ }
28
+
29
+ const props = withDefaults(defineProps<ButtonProps>(), {
30
+ label: '按钮',
31
+ size: 'medium'
32
+ })
33
+
34
+ const emit = defineEmits<ButtonEvents>()
35
+
36
+ // 组合内联样式:背景色 + 字体颜色
37
+ const buttonStyle = computed(() => {
38
+ const style: Record<string, string> = {}
39
+ if (props.color) style.backgroundColor = props.color
40
+ if (props.textColor) style.color = props.textColor
41
+ return Object.keys(style).length > 0 ? style : undefined
42
+ })
43
+
44
+ const handleClick = () => {
45
+ emit('change', props.label)
46
+ }
47
+ </script>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="widget-button-group">
3
+ <label v-if="label">{{ label }}</label>
4
+ <div role="group" :aria-label="label">
5
+ <button
6
+ v-for="choice in choices"
7
+ :key="choice"
8
+ type="button"
9
+ :aria-pressed="selected === choice"
10
+ @click="handleClick(choice)"
11
+ >
12
+ {{ choice }}
13
+ </button>
14
+ </div>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { ref, defineProps, defineEmits } from 'vue'
20
+
21
+ interface ButtonGroupProps {
22
+ choices: string[]
23
+ label?: string
24
+ default?: string
25
+ }
26
+
27
+ interface ButtonGroupEvents {
28
+ change: [value: string]
29
+ }
30
+
31
+ const props = withDefaults(defineProps<ButtonGroupProps>(), {
32
+ label: ''
33
+ })
34
+
35
+ const emit = defineEmits<ButtonGroupEvents>()
36
+
37
+ const selected = ref(props.default ?? props.choices[0])
38
+
39
+ const handleClick = (choice: string) => {
40
+ selected.value = choice
41
+ emit('change', choice)
42
+ }
43
+ </script>