@vue-lynx-example/7guis 0.1.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 +11 -0
- package/dist/cells.lynx.bundle +0 -0
- package/dist/cells.web.bundle +1 -0
- package/dist/circle-drawer.lynx.bundle +0 -0
- package/dist/circle-drawer.web.bundle +1 -0
- package/dist/counter.lynx.bundle +0 -0
- package/dist/counter.web.bundle +1 -0
- package/dist/crud.lynx.bundle +0 -0
- package/dist/crud.web.bundle +1 -0
- package/dist/flight-booker.lynx.bundle +0 -0
- package/dist/flight-booker.web.bundle +1 -0
- package/dist/temperature-converter.lynx.bundle +0 -0
- package/dist/temperature-converter.web.bundle +1 -0
- package/dist/timer.lynx.bundle +0 -0
- package/dist/timer.web.bundle +1 -0
- package/lynx.config.ts +25 -0
- package/package.json +34 -0
- package/src/cells/App.vue +107 -0
- package/src/cells/index.ts +4 -0
- package/src/circle-drawer/App.vue +153 -0
- package/src/circle-drawer/index.ts +4 -0
- package/src/counter/App.vue +17 -0
- package/src/counter/index.ts +4 -0
- package/src/crud/App.vue +126 -0
- package/src/crud/index.ts +4 -0
- package/src/flight-booker/App.vue +81 -0
- package/src/flight-booker/index.ts +4 -0
- package/src/shims-vue.d.ts +5 -0
- package/src/temperature-converter/App.vue +43 -0
- package/src/temperature-converter/index.ts +4 -0
- package/src/timer/App.vue +81 -0
- package/src/timer/index.ts +4 -0
- package/tsconfig.json +10 -0
package/src/crud/App.vue
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, reactive, computed, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'])
|
|
5
|
+
const selected = ref(-1)
|
|
6
|
+
const prefix = ref('')
|
|
7
|
+
const first = ref('')
|
|
8
|
+
const last = ref('')
|
|
9
|
+
|
|
10
|
+
const filteredNames = computed(() =>
|
|
11
|
+
names
|
|
12
|
+
.map((n, i) => ({ name: n, index: i }))
|
|
13
|
+
.filter(({ name }) => name.toLowerCase().startsWith(prefix.value.toLowerCase()))
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
watch(selected, (idx) => {
|
|
17
|
+
if (idx >= 0 && idx < names.length) {
|
|
18
|
+
const parts = names[idx].split(', ')
|
|
19
|
+
last.value = parts[0] || ''
|
|
20
|
+
first.value = parts[1] || ''
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
function create() {
|
|
25
|
+
if (first.value.trim() && last.value.trim()) {
|
|
26
|
+
const fullName = `${last.value}, ${first.value}`
|
|
27
|
+
if (!names.includes(fullName)) {
|
|
28
|
+
names.push(fullName)
|
|
29
|
+
first.value = ''
|
|
30
|
+
last.value = ''
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function update() {
|
|
36
|
+
if (selected.value >= 0 && first.value.trim() && last.value.trim()) {
|
|
37
|
+
names[selected.value] = `${last.value}, ${first.value}`
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function del() {
|
|
42
|
+
if (selected.value >= 0) {
|
|
43
|
+
names.splice(selected.value, 1)
|
|
44
|
+
selected.value = -1
|
|
45
|
+
first.value = ''
|
|
46
|
+
last.value = ''
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<view :style="{ padding: 20, gap: 12 }">
|
|
53
|
+
<!-- Filter -->
|
|
54
|
+
<view :style="{ gap: 4 }">
|
|
55
|
+
<text :style="{ fontSize: 12, color: '#666' }">Filter prefix:</text>
|
|
56
|
+
<input
|
|
57
|
+
type="text"
|
|
58
|
+
:value="prefix"
|
|
59
|
+
placeholder="Filter prefix"
|
|
60
|
+
:style="{ height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
61
|
+
@input="(e) => prefix = e.detail.value"
|
|
62
|
+
/>
|
|
63
|
+
</view>
|
|
64
|
+
|
|
65
|
+
<!-- Name list -->
|
|
66
|
+
<view :style="{ borderWidth: 1, borderColor: '#ccc', borderRadius: 4, minHeight: 120 }">
|
|
67
|
+
<view
|
|
68
|
+
v-for="item in filteredNames"
|
|
69
|
+
:key="item.index"
|
|
70
|
+
:style="{
|
|
71
|
+
padding: '8px 12px',
|
|
72
|
+
backgroundColor: selected === item.index ? '#0077ff' : 'transparent',
|
|
73
|
+
}"
|
|
74
|
+
@tap="selected = item.index"
|
|
75
|
+
>
|
|
76
|
+
<text :style="{ fontSize: 16, color: selected === item.index ? '#fff' : '#222' }">
|
|
77
|
+
{{ item.name }}
|
|
78
|
+
</text>
|
|
79
|
+
</view>
|
|
80
|
+
</view>
|
|
81
|
+
|
|
82
|
+
<!-- Name / Surname inputs -->
|
|
83
|
+
<view :style="{ gap: 8 }">
|
|
84
|
+
<view :style="{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }">
|
|
85
|
+
<text :style="{ fontSize: 14, width: 80 }">Name:</text>
|
|
86
|
+
<input
|
|
87
|
+
type="text"
|
|
88
|
+
:value="first"
|
|
89
|
+
:style="{ flex: 1, height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
90
|
+
@input="(e) => first = e.detail.value"
|
|
91
|
+
/>
|
|
92
|
+
</view>
|
|
93
|
+
<view :style="{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }">
|
|
94
|
+
<text :style="{ fontSize: 14, width: 80 }">Surname:</text>
|
|
95
|
+
<input
|
|
96
|
+
type="text"
|
|
97
|
+
:value="last"
|
|
98
|
+
:style="{ flex: 1, height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
99
|
+
@input="(e) => last = e.detail.value"
|
|
100
|
+
/>
|
|
101
|
+
</view>
|
|
102
|
+
</view>
|
|
103
|
+
|
|
104
|
+
<!-- Action buttons -->
|
|
105
|
+
<view :style="{ display: 'flex', flexDirection: 'row', gap: 8 }">
|
|
106
|
+
<view
|
|
107
|
+
:style="{ padding: '8px 16px', backgroundColor: '#0077ff', borderRadius: 6 }"
|
|
108
|
+
@tap="create"
|
|
109
|
+
>
|
|
110
|
+
<text :style="{ color: '#fff', fontSize: 14 }">Create</text>
|
|
111
|
+
</view>
|
|
112
|
+
<view
|
|
113
|
+
:style="{ padding: '8px 16px', backgroundColor: selected >= 0 ? '#0077ff' : '#ccc', borderRadius: 6 }"
|
|
114
|
+
@tap="update"
|
|
115
|
+
>
|
|
116
|
+
<text :style="{ color: '#fff', fontSize: 14 }">Update</text>
|
|
117
|
+
</view>
|
|
118
|
+
<view
|
|
119
|
+
:style="{ padding: '8px 16px', backgroundColor: selected >= 0 ? '#ff4444' : '#ccc', borderRadius: 6 }"
|
|
120
|
+
@tap="del"
|
|
121
|
+
>
|
|
122
|
+
<text :style="{ color: '#fff', fontSize: 14 }">Delete</text>
|
|
123
|
+
</view>
|
|
124
|
+
</view>
|
|
125
|
+
</view>
|
|
126
|
+
</template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const isReturn = ref(false)
|
|
5
|
+
const departureDate = ref(dateToString(new Date()))
|
|
6
|
+
const returnDate = ref(departureDate.value)
|
|
7
|
+
|
|
8
|
+
const canBook = computed(() =>
|
|
9
|
+
!isReturn.value || returnDate.value > departureDate.value
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
function toggleFlightType() {
|
|
13
|
+
isReturn.value = !isReturn.value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function book() {
|
|
17
|
+
if (isReturn.value) {
|
|
18
|
+
console.log(`Booked return flight: depart ${departureDate.value}, return ${returnDate.value}`)
|
|
19
|
+
} else {
|
|
20
|
+
console.log(`Booked one-way flight: depart ${departureDate.value}`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dateToString(date) {
|
|
25
|
+
const y = date.getFullYear()
|
|
26
|
+
const m = String(date.getMonth() + 1).padStart(2, '0')
|
|
27
|
+
const d = String(date.getDate()).padStart(2, '0')
|
|
28
|
+
return `${y}-${m}-${d}`
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<view :style="{ padding: 20, gap: 12 }">
|
|
34
|
+
<!-- Flight type toggle (simulated select) -->
|
|
35
|
+
<view
|
|
36
|
+
:style="{ padding: '8px 16px', backgroundColor: '#eee', borderRadius: 6 }"
|
|
37
|
+
@tap="toggleFlightType"
|
|
38
|
+
>
|
|
39
|
+
<text :style="{ fontSize: 16 }">{{ isReturn ? 'Return Flight' : 'One-way Flight' }}</text>
|
|
40
|
+
</view>
|
|
41
|
+
|
|
42
|
+
<!-- Departure date -->
|
|
43
|
+
<view :style="{ gap: 4 }">
|
|
44
|
+
<text :style="{ fontSize: 12, color: '#666' }">Departure (YYYY-MM-DD)</text>
|
|
45
|
+
<input
|
|
46
|
+
type="text"
|
|
47
|
+
:value="departureDate"
|
|
48
|
+
:style="{ height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
49
|
+
@input="(e) => departureDate = e.detail.value"
|
|
50
|
+
/>
|
|
51
|
+
</view>
|
|
52
|
+
|
|
53
|
+
<!-- Return date -->
|
|
54
|
+
<view :style="{ gap: 4, opacity: isReturn ? 1 : 0.4 }">
|
|
55
|
+
<text :style="{ fontSize: 12, color: '#666' }">Return (YYYY-MM-DD)</text>
|
|
56
|
+
<input
|
|
57
|
+
type="text"
|
|
58
|
+
:value="returnDate"
|
|
59
|
+
:style="{ height: 36, borderWidth: 1, borderColor: isReturn ? '#ccc' : '#eee', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
60
|
+
@input="(e) => { if (isReturn) returnDate = e.detail.value }"
|
|
61
|
+
/>
|
|
62
|
+
</view>
|
|
63
|
+
|
|
64
|
+
<!-- Book button -->
|
|
65
|
+
<view
|
|
66
|
+
:style="{
|
|
67
|
+
padding: '10px 20px',
|
|
68
|
+
backgroundColor: canBook ? '#0077ff' : '#ccc',
|
|
69
|
+
borderRadius: 6,
|
|
70
|
+
alignSelf: 'flex-start',
|
|
71
|
+
}"
|
|
72
|
+
@tap="canBook && book()"
|
|
73
|
+
>
|
|
74
|
+
<text :style="{ color: '#fff', fontSize: 16 }">Book</text>
|
|
75
|
+
</view>
|
|
76
|
+
|
|
77
|
+
<text v-if="!canBook" :style="{ color: 'red', fontSize: 14 }">
|
|
78
|
+
Return date must be after departure date.
|
|
79
|
+
</text>
|
|
80
|
+
</view>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const celsius = ref('0')
|
|
5
|
+
const fahrenheit = ref('32')
|
|
6
|
+
|
|
7
|
+
function onCelsiusInput(e) {
|
|
8
|
+
const val = e.detail.value
|
|
9
|
+
celsius.value = val
|
|
10
|
+
const num = Number(val)
|
|
11
|
+
if (Number.isFinite(num)) {
|
|
12
|
+
fahrenheit.value = String(+(num * (9 / 5) + 32).toFixed(2))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function onFahrenheitInput(e) {
|
|
17
|
+
const val = e.detail.value
|
|
18
|
+
fahrenheit.value = val
|
|
19
|
+
const num = Number(val)
|
|
20
|
+
if (Number.isFinite(num)) {
|
|
21
|
+
celsius.value = String(+((num - 32) * (5 / 9)).toFixed(2))
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<view :style="{ display: 'flex', padding: 20, flexDirection: 'row', alignItems: 'center', gap: 8, flexWrap: 'wrap' }">
|
|
28
|
+
<input
|
|
29
|
+
type="text"
|
|
30
|
+
:value="celsius"
|
|
31
|
+
:style="{ width: 100, height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
32
|
+
@input="onCelsiusInput"
|
|
33
|
+
/>
|
|
34
|
+
<text :style="{ fontSize: 16 }">Celsius =</text>
|
|
35
|
+
<input
|
|
36
|
+
type="text"
|
|
37
|
+
:value="fahrenheit"
|
|
38
|
+
:style="{ width: 100, height: 36, borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: '0 8px', fontSize: 16 }"
|
|
39
|
+
@input="onFahrenheitInput"
|
|
40
|
+
/>
|
|
41
|
+
<text :style="{ fontSize: 16 }">Fahrenheit</text>
|
|
42
|
+
</view>
|
|
43
|
+
</template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed, onUnmounted } from 'vue'
|
|
3
|
+
|
|
4
|
+
const duration = ref(15000) // ms
|
|
5
|
+
const elapsed = ref(0)
|
|
6
|
+
|
|
7
|
+
let lastTime = Date.now()
|
|
8
|
+
const interval = setInterval(() => {
|
|
9
|
+
const now = Date.now()
|
|
10
|
+
const dt = now - lastTime
|
|
11
|
+
lastTime = now
|
|
12
|
+
if (elapsed.value < duration.value) {
|
|
13
|
+
elapsed.value = Math.min(elapsed.value + dt, duration.value)
|
|
14
|
+
}
|
|
15
|
+
}, 50)
|
|
16
|
+
|
|
17
|
+
onUnmounted(() => clearInterval(interval))
|
|
18
|
+
|
|
19
|
+
const progressPct = computed(() =>
|
|
20
|
+
Math.min(elapsed.value / duration.value, 1) * 100
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
function reset() {
|
|
24
|
+
elapsed.value = 0
|
|
25
|
+
lastTime = Date.now()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function adjustDuration(delta) {
|
|
29
|
+
duration.value = Math.max(1000, Math.min(30000, duration.value + delta))
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<view :style="{ padding: 20, gap: 14 }">
|
|
35
|
+
<!-- Progress bar -->
|
|
36
|
+
<view :style="{ gap: 4 }">
|
|
37
|
+
<text :style="{ fontSize: 14, color: '#666' }">Elapsed Time:</text>
|
|
38
|
+
<view :style="{ height: 20, backgroundColor: '#eee', borderRadius: 4, overflow: 'hidden' }">
|
|
39
|
+
<view
|
|
40
|
+
:style="{
|
|
41
|
+
height: 20,
|
|
42
|
+
width: progressPct + '%',
|
|
43
|
+
backgroundColor: '#0077ff',
|
|
44
|
+
borderRadius: 4,
|
|
45
|
+
}"
|
|
46
|
+
/>
|
|
47
|
+
</view>
|
|
48
|
+
</view>
|
|
49
|
+
|
|
50
|
+
<text :style="{ fontSize: 20 }">{{ (elapsed / 1000).toFixed(1) }}s</text>
|
|
51
|
+
|
|
52
|
+
<!-- Duration control -->
|
|
53
|
+
<view :style="{ gap: 4 }">
|
|
54
|
+
<text :style="{ fontSize: 14, color: '#666' }">
|
|
55
|
+
Duration: {{ (duration / 1000).toFixed(1) }}s
|
|
56
|
+
</text>
|
|
57
|
+
<view :style="{ display: 'flex', flexDirection: 'row', gap: 8 }">
|
|
58
|
+
<view
|
|
59
|
+
:style="{ padding: '6px 16px', backgroundColor: '#eee', borderRadius: 4 }"
|
|
60
|
+
@tap="adjustDuration(-1000)"
|
|
61
|
+
>
|
|
62
|
+
<text :style="{ fontSize: 16 }">-1s</text>
|
|
63
|
+
</view>
|
|
64
|
+
<view
|
|
65
|
+
:style="{ padding: '6px 16px', backgroundColor: '#eee', borderRadius: 4 }"
|
|
66
|
+
@tap="adjustDuration(1000)"
|
|
67
|
+
>
|
|
68
|
+
<text :style="{ fontSize: 16 }">+1s</text>
|
|
69
|
+
</view>
|
|
70
|
+
</view>
|
|
71
|
+
</view>
|
|
72
|
+
|
|
73
|
+
<!-- Reset -->
|
|
74
|
+
<view
|
|
75
|
+
:style="{ padding: '10px 20px', backgroundColor: '#0077ff', borderRadius: 6, alignSelf: 'flex-start' }"
|
|
76
|
+
@tap="reset"
|
|
77
|
+
>
|
|
78
|
+
<text :style="{ color: '#fff', fontSize: 16 }">Reset</text>
|
|
79
|
+
</view>
|
|
80
|
+
</view>
|
|
81
|
+
</template>
|