@lingxia/page-runtime 0.5.0

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 (54) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/react/LxInput.d.ts +47 -0
  4. package/dist/react/LxInput.js +128 -0
  5. package/dist/react/LxNavigator.d.ts +23 -0
  6. package/dist/react/LxNavigator.js +61 -0
  7. package/dist/react/LxPicker.d.ts +27 -0
  8. package/dist/react/LxPicker.js +113 -0
  9. package/dist/react/LxTextarea.d.ts +47 -0
  10. package/dist/react/LxTextarea.js +118 -0
  11. package/dist/react/LxVideo.d.ts +5 -0
  12. package/dist/react/LxVideo.js +113 -0
  13. package/dist/react/hook.d.ts +39 -0
  14. package/dist/react/hook.js +212 -0
  15. package/dist/react/index.d.ts +6 -0
  16. package/dist/react/index.js +6 -0
  17. package/dist/react/text_component_shared.d.ts +5 -0
  18. package/dist/react/text_component_shared.js +33 -0
  19. package/dist/shared/runtime.d.ts +21 -0
  20. package/dist/shared/runtime.js +142 -0
  21. package/dist/vue/LxInput.vue +92 -0
  22. package/dist/vue/LxNavigator.vue +79 -0
  23. package/dist/vue/LxPicker.vue +144 -0
  24. package/dist/vue/LxTextarea.vue +90 -0
  25. package/dist/vue/LxVideo.vue +111 -0
  26. package/dist/vue/hook.d.ts +42 -0
  27. package/dist/vue/hook.js +194 -0
  28. package/dist/vue/index.d.ts +7 -0
  29. package/dist/vue/index.js +6 -0
  30. package/dist/vue/text_component_shared.d.ts +3 -0
  31. package/dist/vue/text_component_shared.js +26 -0
  32. package/dist/vue/types.d.ts +101 -0
  33. package/dist/vue/types.js +1 -0
  34. package/package.json +67 -0
  35. package/src/index.ts +21 -0
  36. package/src/react/LxInput.tsx +224 -0
  37. package/src/react/LxNavigator.tsx +132 -0
  38. package/src/react/LxPicker.tsx +205 -0
  39. package/src/react/LxTextarea.tsx +216 -0
  40. package/src/react/LxVideo.tsx +166 -0
  41. package/src/react/hook.ts +317 -0
  42. package/src/react/index.ts +14 -0
  43. package/src/react/text_component_shared.ts +43 -0
  44. package/src/shared/runtime.ts +186 -0
  45. package/src/vue/LxInput.vue +92 -0
  46. package/src/vue/LxNavigator.vue +79 -0
  47. package/src/vue/LxPicker.vue +144 -0
  48. package/src/vue/LxTextarea.vue +90 -0
  49. package/src/vue/LxVideo.vue +111 -0
  50. package/src/vue/hook.ts +313 -0
  51. package/src/vue/index.ts +22 -0
  52. package/src/vue/shims-vue.d.ts +5 -0
  53. package/src/vue/text_component_shared.ts +33 -0
  54. package/src/vue/types.ts +107 -0
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { ref, h, onMounted, onBeforeUnmount, useAttrs } from 'vue';
3
+ import type { LxNavigatorEvent } from '@lingxia/elements';
4
+ import { buildNavigatorNativeAttrs } from '@lingxia/elements';
5
+ import type { LxNavigatorProps } from './types.js';
6
+ import '@lingxia/elements';
7
+
8
+ const props = withDefaults(defineProps<LxNavigatorProps>(), {
9
+ openType: 'navigate',
10
+ delta: 1,
11
+ hoverClass: 'navigator-hover',
12
+ hoverStopPropagation: false,
13
+ hoverStartTime: 20,
14
+ hoverStayTime: 70,
15
+ });
16
+
17
+ const slots = defineSlots();
18
+ const attrs = useAttrs();
19
+
20
+ const emit = defineEmits<{
21
+ success: [e: LxNavigatorEvent];
22
+ fail: [e: LxNavigatorEvent];
23
+ complete: [e: LxNavigatorEvent];
24
+ }>();
25
+
26
+ const elementRef = ref<HTMLElement | null>(null);
27
+
28
+ const handleSuccess = (e: Event) => emit('success', e as LxNavigatorEvent);
29
+ const handleFail = (e: Event) => emit('fail', e as LxNavigatorEvent);
30
+ const handleComplete = (e: Event) => emit('complete', e as LxNavigatorEvent);
31
+ const successListener: EventListenerObject = { handleEvent: handleSuccess };
32
+ const failListener: EventListenerObject = { handleEvent: handleFail };
33
+ const completeListener: EventListenerObject = { handleEvent: handleComplete };
34
+
35
+ onMounted(() => {
36
+ const el = elementRef.value;
37
+ if (!el) return;
38
+ el.addEventListener('success', successListener);
39
+ el.addEventListener('fail', failListener);
40
+ el.addEventListener('complete', completeListener);
41
+ });
42
+
43
+ onBeforeUnmount(() => {
44
+ const el = elementRef.value;
45
+ if (!el) return;
46
+ el.removeEventListener('success', successListener);
47
+ el.removeEventListener('fail', failListener);
48
+ el.removeEventListener('complete', completeListener);
49
+ });
50
+
51
+ defineExpose({ el: elementRef });
52
+
53
+ const render = () => h(
54
+ 'lx-navigator',
55
+ {
56
+ ref: elementRef,
57
+ ...buildNavigatorNativeAttrs({
58
+ url: props.url,
59
+ openType: props.openType,
60
+ target: props.target,
61
+ delta: props.delta,
62
+ appId: props.appId,
63
+ path: props.path,
64
+ phoneNumber: props.phoneNumber,
65
+ hoverClass: props.hoverClass,
66
+ hoverStopPropagation: props.hoverStopPropagation,
67
+ hoverStartTime: props.hoverStartTime,
68
+ hoverStayTime: props.hoverStayTime,
69
+ }, attrs as Record<string, unknown>),
70
+ class: props.class ?? attrs.class,
71
+ style: props.style ?? attrs.style,
72
+ },
73
+ slots.default?.()
74
+ );
75
+ </script>
76
+
77
+ <template>
78
+ <render />
79
+ </template>
@@ -0,0 +1,144 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, h, onBeforeUnmount, useAttrs, useId, watch } from 'vue';
3
+ import { registerPickerComponent } from '@lingxia/elements';
4
+ import {
5
+ buildPickerNativeAttrs,
6
+ getPickerDisplayText,
7
+ getPickerValueFromIndex,
8
+ } from '@lingxia/elements';
9
+ import { bindElementEvents, getCustomEventDetail, unbindElementEvents } from './text_component_shared.js';
10
+ import type { LxPickerProps } from './types.js';
11
+
12
+ const props = withDefaults(defineProps<LxPickerProps>(), {
13
+ placeholder: 'Please select',
14
+ disabled: false,
15
+ });
16
+ const attrs = useAttrs();
17
+
18
+ const slots = defineSlots();
19
+
20
+ const emit = defineEmits<{
21
+ 'update:modelValue': [value: string | string[]];
22
+ confirm: [value: string | string[]];
23
+ cancel: [];
24
+ scroll: [value: string | string[]];
25
+ }>();
26
+
27
+ if (typeof window !== 'undefined') {
28
+ registerPickerComponent();
29
+ }
30
+
31
+ const visible = ref(false);
32
+ const vueId = useId();
33
+ const pickerId = computed(() => `lx-picker-${vueId.replace(/[:]/g, '')}`);
34
+ const pickerRef = ref<HTMLElement | null>(null);
35
+ let boundElement: HTMLElement | null = null;
36
+ const pickerEventListeners: Record<string, EventListenerObject> = {
37
+ change: {
38
+ handleEvent: (event: Event) => handleChange(event),
39
+ },
40
+ scroll: {
41
+ handleEvent: (event: Event) => handleScroll(event),
42
+ },
43
+ };
44
+
45
+ const displayText = computed(() => getPickerDisplayText(props.modelValue, props.fields));
46
+
47
+ function handleChange(e: Event) {
48
+ props.onChange?.(e);
49
+ const detail = getCustomEventDetail<{
50
+ confirmed?: boolean;
51
+ cancelled?: boolean;
52
+ value?: string | string[];
53
+ index?: number | number[];
54
+ }>(e);
55
+ if (detail.confirmed) {
56
+ if (props.mode === 'date' || props.mode === 'time') {
57
+ const nextValue = detail.value ?? '';
58
+ emit('update:modelValue', nextValue);
59
+ emit('confirm', nextValue);
60
+ } else if (detail.index !== undefined) {
61
+ const nextValue = getPickerValueFromIndex(props.columns, detail.index);
62
+ emit('update:modelValue', nextValue);
63
+ emit('confirm', nextValue);
64
+ }
65
+ visible.value = false;
66
+ } else if (detail.cancelled) {
67
+ emit('cancel');
68
+ visible.value = false;
69
+ }
70
+ }
71
+
72
+ function handleScroll(e: Event) {
73
+ props.onNativeScroll?.(e);
74
+ const detail = getCustomEventDetail<{
75
+ value?: string | string[];
76
+ index?: number | number[];
77
+ }>(e);
78
+ if (detail.value !== undefined) {
79
+ emit('scroll', detail.value);
80
+ } else if (detail.index !== undefined) {
81
+ emit('scroll', getPickerValueFromIndex(props.columns, detail.index));
82
+ }
83
+ }
84
+
85
+ function handleClick() {
86
+ if (!props.disabled) visible.value = true;
87
+ }
88
+
89
+ watch(pickerRef, (element) => {
90
+ boundElement = bindElementEvents(boundElement, element, pickerEventListeners);
91
+ });
92
+
93
+ onBeforeUnmount(() => {
94
+ unbindElementEvents(boundElement, pickerEventListeners);
95
+ });
96
+
97
+ const pickerProps = computed(() => {
98
+ return buildPickerNativeAttrs({
99
+ id: pickerId.value,
100
+ columns: props.columns,
101
+ mode: props.mode,
102
+ start: props.start,
103
+ end: props.end,
104
+ fields: props.fields,
105
+ modelValue: props.modelValue,
106
+ cancelText: props.cancelText,
107
+ cancelTextColor: props.cancelTextColor,
108
+ cancelButtonColor: props.cancelButtonColor,
109
+ confirmText: props.confirmText,
110
+ confirmTextColor: props.confirmTextColor,
111
+ confirmButtonColor: props.confirmButtonColor,
112
+ }, attrs as Record<string, unknown>);
113
+ });
114
+
115
+ const triggerStyle = computed(() => ({
116
+ display: 'flex',
117
+ alignItems: 'center',
118
+ justifyContent: 'space-between',
119
+ padding: '12px 14px',
120
+ backgroundColor: '#fff',
121
+ border: '1px solid #e5e7eb',
122
+ borderRadius: '8px',
123
+ cursor: props.disabled ? 'not-allowed' : 'pointer',
124
+ opacity: props.disabled ? 0.5 : 1,
125
+ width: '100%',
126
+ boxSizing: 'border-box',
127
+ }));
128
+
129
+ defineExpose({ el: pickerRef });
130
+
131
+ const renderPicker = () => visible.value ? h('lx-picker', { ref: pickerRef, ...pickerProps.value }) : null;
132
+ </script>
133
+
134
+ <template>
135
+ <slot :open="handleClick" :disabled="props.disabled">
136
+ <div :class="props.class ?? attrs.class" :style="[triggerStyle, props.style ?? attrs.style]" @click="handleClick">
137
+ <span :style="{ color: modelValue ? '#111' : '#9ca3af' }">{{ displayText || placeholder }}</span>
138
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2">
139
+ <path d="M6 9l6 6 6-6" />
140
+ </svg>
141
+ </div>
142
+ </slot>
143
+ <renderPicker />
144
+ </template>
@@ -0,0 +1,90 @@
1
+ <script setup lang="ts">
2
+ import { computed, h, onBeforeUnmount, ref, useAttrs, watch } from 'vue';
3
+ import { registerTextareaComponent } from '@lingxia/elements';
4
+ import { buildTextareaNativeAttrs } from '@lingxia/elements';
5
+ import type { LxTextareaProps } from './types.js';
6
+ import {
7
+ bindElementEvents,
8
+ getCustomEventDetail,
9
+ unbindElementEvents,
10
+ } from './text_component_shared.js';
11
+
12
+ const props = withDefaults(defineProps<LxTextareaProps>(), {
13
+ disabled: false,
14
+ });
15
+ const attrs = useAttrs();
16
+
17
+ const emit = defineEmits<{
18
+ 'update:modelValue': [value: string];
19
+ input: [detail: Record<string, unknown>];
20
+ change: [detail: Record<string, unknown>];
21
+ focus: [detail: Record<string, unknown>];
22
+ blur: [detail: Record<string, unknown>];
23
+ confirm: [detail: Record<string, unknown>];
24
+ lineChange: [detail: Record<string, unknown>];
25
+ keyboardHeightChange: [detail: Record<string, unknown>];
26
+ }>();
27
+
28
+ if (typeof window !== 'undefined') {
29
+ registerTextareaComponent();
30
+ }
31
+
32
+ const elementRef = ref<HTMLElement | null>(null);
33
+ let boundElement: HTMLElement | null = null;
34
+
35
+ const textareaEventListeners: Record<string, EventListenerObject> = {
36
+ input: {
37
+ handleEvent: (event: Event) => {
38
+ const detail = getCustomEventDetail<Record<string, unknown>>(event);
39
+ if (typeof detail.value === 'string') {
40
+ emit('update:modelValue', detail.value);
41
+ }
42
+ emit('input', detail);
43
+ },
44
+ },
45
+ change: {
46
+ handleEvent: (event: Event) => emit('change', getCustomEventDetail<Record<string, unknown>>(event)),
47
+ },
48
+ focus: {
49
+ handleEvent: (event: Event) => emit('focus', getCustomEventDetail<Record<string, unknown>>(event)),
50
+ },
51
+ blur: {
52
+ handleEvent: (event: Event) => emit('blur', getCustomEventDetail<Record<string, unknown>>(event)),
53
+ },
54
+ confirm: {
55
+ handleEvent: (event: Event) => emit('confirm', getCustomEventDetail<Record<string, unknown>>(event)),
56
+ },
57
+ linechange: {
58
+ handleEvent: (event: Event) => emit('lineChange', getCustomEventDetail<Record<string, unknown>>(event)),
59
+ },
60
+ keyboardheightchange: {
61
+ handleEvent: (event: Event) => emit('keyboardHeightChange', getCustomEventDetail<Record<string, unknown>>(event)),
62
+ },
63
+ };
64
+
65
+ watch(elementRef, (element) => {
66
+ boundElement = bindElementEvents(boundElement, element, textareaEventListeners);
67
+ });
68
+
69
+ onBeforeUnmount(() => {
70
+ unbindElementEvents(boundElement, textareaEventListeners);
71
+ });
72
+
73
+ const textareaProps = computed<Record<string, unknown>>(() => {
74
+ const result: Record<string, unknown> = buildTextareaNativeAttrs({
75
+ ...props,
76
+ id: props.id ?? (typeof attrs.id === 'string' ? attrs.id : undefined),
77
+ }, attrs as Record<string, unknown>, elementRef.value !== null);
78
+ result.class = props.class ?? attrs.class;
79
+ result.style = props.style ?? attrs.style;
80
+ return result;
81
+ });
82
+
83
+ defineExpose({ el: elementRef });
84
+
85
+ const renderTextarea = () => h('lx-textarea', { ref: elementRef, ...textareaProps.value });
86
+ </script>
87
+
88
+ <template>
89
+ <renderTextarea />
90
+ </template>
@@ -0,0 +1,111 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, h, onBeforeUnmount, useAttrs, useId, watch } from 'vue';
3
+ import { registerVideoComponent } from '@lingxia/elements';
4
+ import {
5
+ buildVideoNativeAttrs,
6
+ VIDEO_DOM_EVENT_MAP,
7
+ } from '@lingxia/elements';
8
+ import { bindElementEvents, unbindElementEvents } from './text_component_shared.js';
9
+ import type { LxVideoProps } from './types.js';
10
+
11
+ const props = withDefaults(defineProps<LxVideoProps>(), {
12
+ autoplay: false,
13
+ loop: false,
14
+ muted: false,
15
+ controls: true,
16
+ progressBar: true,
17
+ live: false,
18
+ });
19
+ const attrs = useAttrs();
20
+
21
+ const emit = defineEmits<{
22
+ playRequest: [e: Event];
23
+ play: [e: Event];
24
+ playing: [e: Event];
25
+ pause: [e: Event];
26
+ stop: [e: Event];
27
+ ended: [e: Event];
28
+ timeUpdate: [e: Event];
29
+ error: [e: Event];
30
+ loadedMetadata: [e: Event];
31
+ fullscreenChange: [e: Event];
32
+ waiting: [e: Event];
33
+ qualityChange: [e: Event];
34
+ rateChange: [e: Event];
35
+ }>();
36
+
37
+ if (typeof window !== 'undefined') {
38
+ registerVideoComponent();
39
+ }
40
+
41
+ const elementRef = ref<HTMLElement | null>(null);
42
+ const vueId = useId();
43
+ let boundElement: HTMLElement | null = null;
44
+
45
+ const resolvedId = computed(() => props.id || `lx-video-${vueId.replace(/[:]/g, '')}`);
46
+
47
+ const videoEventListeners: Record<string, EventListenerObject> = {
48
+ [VIDEO_DOM_EVENT_MAP.onPlayRequest]: { handleEvent: (event: Event) => emit('playRequest', event) },
49
+ [VIDEO_DOM_EVENT_MAP.onPlay]: { handleEvent: (event: Event) => emit('play', event) },
50
+ [VIDEO_DOM_EVENT_MAP.onPlaying]: { handleEvent: (event: Event) => emit('playing', event) },
51
+ [VIDEO_DOM_EVENT_MAP.onPause]: { handleEvent: (event: Event) => emit('pause', event) },
52
+ [VIDEO_DOM_EVENT_MAP.onStop]: { handleEvent: (event: Event) => emit('stop', event) },
53
+ [VIDEO_DOM_EVENT_MAP.onEnded]: { handleEvent: (event: Event) => emit('ended', event) },
54
+ [VIDEO_DOM_EVENT_MAP.onTimeUpdate]: { handleEvent: (event: Event) => emit('timeUpdate', event) },
55
+ [VIDEO_DOM_EVENT_MAP.onError]: { handleEvent: (event: Event) => emit('error', event) },
56
+ [VIDEO_DOM_EVENT_MAP.onLoadedMetadata]: { handleEvent: (event: Event) => emit('loadedMetadata', event) },
57
+ [VIDEO_DOM_EVENT_MAP.onFullscreenChange]: { handleEvent: (event: Event) => emit('fullscreenChange', event) },
58
+ [VIDEO_DOM_EVENT_MAP.onWaiting]: { handleEvent: (event: Event) => emit('waiting', event) },
59
+ [VIDEO_DOM_EVENT_MAP.onQualityChange]: { handleEvent: (event: Event) => emit('qualityChange', event) },
60
+ [VIDEO_DOM_EVENT_MAP.onRateChange]: { handleEvent: (event: Event) => emit('rateChange', event) },
61
+ };
62
+
63
+ watch(elementRef, (element) => {
64
+ boundElement = bindElementEvents(boundElement, element, videoEventListeners);
65
+ });
66
+
67
+ onBeforeUnmount(() => {
68
+ unbindElementEvents(boundElement, videoEventListeners);
69
+ });
70
+
71
+ watch(
72
+ [elementRef, () => props.rotate, () => props.objectFit],
73
+ () => {
74
+ const el = elementRef.value;
75
+ if (!el) return;
76
+ const rotate = props.rotate;
77
+ if (rotate === undefined || rotate === null || rotate === '') {
78
+ el.removeAttribute('rotate');
79
+ } else {
80
+ el.setAttribute('rotate', String(rotate).trim());
81
+ }
82
+ const objectFit = props.objectFit;
83
+ if (objectFit === undefined || objectFit === null || objectFit === '') {
84
+ el.removeAttribute('object-fit');
85
+ } else {
86
+ el.setAttribute('object-fit', String(objectFit).trim().toLowerCase());
87
+ }
88
+ },
89
+ { immediate: true }
90
+ );
91
+
92
+ const domProps = computed(() => {
93
+ const result = buildVideoNativeAttrs({
94
+ ...props,
95
+ id: resolvedId.value,
96
+ }, attrs as Record<string, unknown>);
97
+ return {
98
+ ...result,
99
+ class: props.class ?? attrs.class,
100
+ style: props.style ?? attrs.style,
101
+ };
102
+ });
103
+
104
+ defineExpose({ el: elementRef });
105
+
106
+ const render = () => h('lx-video', { ref: elementRef, ...domProps.value });
107
+ </script>
108
+
109
+ <template>
110
+ <render />
111
+ </template>