@operato/input 8.0.0-alpha.10 → 8.0.0-alpha.19

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 CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/input",
3
3
  "description": "Webcomponents for input following open-wc recommendations",
4
4
  "author": "heartyoh@hatiolab.com",
5
- "version": "8.0.0-alpha.10",
5
+ "version": "8.0.0-alpha.19",
6
6
  "main": "dist/src/index.js",
7
7
  "module": "dist/src/index.js",
8
8
  "license": "MIT",
@@ -58,6 +58,7 @@
58
58
  "./ox-input-textarea.js": "./dist/src/ox-input-textarea.js",
59
59
  "./ox-input-direction.js": "./dist/src/ox-input-direction.js",
60
60
  "./ox-input-signature.js": "./dist/src/ox-input-signature.js",
61
+ "./ox-select-floor.js": "./dist/src/ox-select-floor.js",
61
62
  "./ox-input-table-column-config.js": "./dist/src/ox-input-table-column-config.js"
62
63
  },
63
64
  "typesVersions": {
@@ -179,6 +180,9 @@
179
180
  "./ox-input-signature.js": [
180
181
  "./dist/src/ox-input-signature.d.ts"
181
182
  ],
183
+ "./ox-select-floor.js": [
184
+ "./dist/src/ox-select-floor.d.ts"
185
+ ],
182
186
  "./ox-input-table-column-config.js": [
183
187
  "./dist/src/ox-input-table-column-config.d.ts"
184
188
  ]
@@ -258,5 +262,5 @@
258
262
  "prettier --write"
259
263
  ]
260
264
  },
261
- "gitHead": "f937b76c9831c46e26acde8e94cc71514068a961"
265
+ "gitHead": "9656b17ec8476c2eeace042a4e561161539e81e7"
262
266
  }
@@ -0,0 +1,257 @@
1
+ import { css, html, PropertyValues } from 'lit'
2
+ import { customElement, property, query, state } from 'lit/decorators.js'
3
+ import { OxFormField } from './ox-form-field'
4
+
5
+ type Card = {
6
+ name: string
7
+ image: string
8
+ }
9
+
10
+ @customElement('ox-select-floor')
11
+ export class OxSelectFloor extends OxFormField {
12
+ static styles = [
13
+ css`
14
+ :host {
15
+ display: block;
16
+ position: relative;
17
+ overflow: hidden;
18
+ height: 100%;
19
+
20
+ --ox-select-floor-rotate-x: 60deg;
21
+ --ox-select-floor-rotate-x-active: 40deg;
22
+ --ox-select-floor-perspective: 1200px;
23
+ }
24
+
25
+ .carousel-container {
26
+ position: relative;
27
+ height: 100%;
28
+ width: 100%;
29
+ overflow: hidden;
30
+ user-select: none;
31
+ }
32
+
33
+ .card {
34
+ position: absolute;
35
+ bottom: 0;
36
+ width: 100%;
37
+ background-color: white;
38
+ transition:
39
+ transform 0.3s ease,
40
+ opacity 0.3s ease,
41
+ box-shadow 0.3s ease,
42
+ border 0.3s ease;
43
+ transform-origin: bottom;
44
+ transform: perspective(var(--ox-select-floor-perspective)) rotateX(var(--ox-select-floor-rotate-x));
45
+ opacity: 0.5;
46
+ border: 2px solid transparent;
47
+ border-radius: 12px;
48
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
49
+ }
50
+
51
+ .card img {
52
+ width: 100%;
53
+ height: auto;
54
+ display: block;
55
+ pointer-events: none;
56
+ border-radius: 12px;
57
+ }
58
+
59
+ .selected {
60
+ z-index: 10;
61
+ opacity: 0.8;
62
+ border: 4px solid #3b82f6;
63
+ box-shadow: 0 8px 16px rgba(59, 130, 246, 0.4);
64
+ }
65
+
66
+ .selected.active {
67
+ opacity: 1;
68
+ transform: perspective(var(--ox-select-floor-perspective)) rotateX(var(--ox-select-floor-rotate-x-active));
69
+ box-shadow: 0 12px 24px rgba(59, 130, 246, 0.4);
70
+ }
71
+
72
+ [template-container] {
73
+ position: absolute;
74
+ right: 10px;
75
+ z-index: 20;
76
+ }
77
+ `
78
+ ]
79
+
80
+ @property({ type: Array }) cards: Card[] = []
81
+ @property({ type: String }) value?: string
82
+ @property({ type: Number }) bottomLimit = 70
83
+
84
+ @state() private selectedIndex = 0
85
+ @state() private activeIndex: number | null = null
86
+ @query('.carousel-container') private carouselContainer!: HTMLDivElement
87
+
88
+ private isDragging = false
89
+ private lastTouchY = 0
90
+ private lastMouseY = 0
91
+
92
+ render() {
93
+ const length = this.cards.length
94
+
95
+ return html`
96
+ <div
97
+ class="carousel-container"
98
+ @wheel=${this.handleWheel}
99
+ @mousedown=${this.handleMouseDown}
100
+ @mousemove=${this.handleMouseMove}
101
+ @mouseup=${this.handleMouseUp}
102
+ @mouseleave=${this.handleMouseLeave}
103
+ @touchstart=${this.handleTouchStart}
104
+ @touchmove=${this.handleTouchMove}
105
+ @touchend=${this.handleTouchEnd}
106
+ >
107
+ ${this.cards.map(({ image, name }, index) => {
108
+ const reverse = length - index - 1
109
+ return html`
110
+ <div
111
+ class="card ${this.getClassForCard(reverse)} ${this.isActive(reverse) ? 'active' : ''}"
112
+ style="bottom: ${(this.bottomLimit * reverse) / length}%;"
113
+ @click=${() => this.selectCard(reverse)}
114
+ @tap=${() => this.toggleActiveCard(reverse)}
115
+ >
116
+ <img src="${image}" @load=${(e: Event) => this.adjustCardHeight(e, reverse)} />
117
+ </div>
118
+ `
119
+ })}
120
+ ${this.cards.map(({ image, name }, index) => {
121
+ const reverse = length - index - 1
122
+ return html`
123
+ <div
124
+ style="bottom: ${(this.bottomLimit * reverse) / length}%;"
125
+ @click=${() => this.selectCard(reverse)}
126
+ template-container
127
+ >
128
+ <slot name="template-${reverse}"></slot>
129
+ </div>
130
+ `
131
+ })}
132
+ </div>
133
+ `
134
+ }
135
+
136
+ updated(changes: PropertyValues<this>) {
137
+ if (changes.has('value')) {
138
+ if (this.value) {
139
+ this.selectedIndex = this.cards.findIndex(card => card.name == this.value)
140
+ } else {
141
+ this.selectedIndex = -1
142
+ }
143
+ }
144
+ }
145
+
146
+ firstUpdated() {
147
+ this.scrollToSelectedCard()
148
+ }
149
+
150
+ getClassForCard(index: number) {
151
+ return index === this.selectedIndex ? 'selected' : 'compressed'
152
+ }
153
+
154
+ isActive(index: number): boolean {
155
+ return this.activeIndex === index
156
+ }
157
+
158
+ handleWheel(event: WheelEvent) {
159
+ event.preventDefault()
160
+ const delta = Math.sign(event.deltaY)
161
+ this.updateSelectedIndex(this.selectedIndex + delta)
162
+ }
163
+
164
+ handleTouchStart(event: TouchEvent) {
165
+ this.lastTouchY = event.touches[0].clientY
166
+ }
167
+
168
+ handleTouchMove(event: TouchEvent) {
169
+ const touch = event.touches[0]
170
+ const deltaY = touch.clientY - this.lastTouchY
171
+ this.lastTouchY = touch.clientY
172
+
173
+ if (Math.abs(deltaY) > 30) {
174
+ const direction = deltaY < 0 ? -1 : 1
175
+ this.updateSelectedIndex(this.selectedIndex + direction)
176
+ }
177
+ }
178
+
179
+ handleTouchEnd() {
180
+ this.isDragging = false
181
+ }
182
+
183
+ handleMouseDown(event: MouseEvent) {
184
+ this.isDragging = true
185
+ this.lastMouseY = event.clientY
186
+ }
187
+
188
+ handleMouseMove(event: MouseEvent) {
189
+ if (!this.isDragging) {
190
+ return
191
+ }
192
+
193
+ const deltaY = event.clientY - this.lastMouseY
194
+
195
+ if (!this.lastMouseY) {
196
+ this.lastMouseY = event.clientY
197
+ }
198
+
199
+ if (Math.abs(deltaY) > 30) {
200
+ this.lastMouseY = event.clientY
201
+ const direction = deltaY > 0 ? -1 : 1
202
+ this.updateSelectedIndex(this.selectedIndex + direction)
203
+ }
204
+ }
205
+
206
+ handleMouseUp() {
207
+ this.isDragging = false
208
+ }
209
+
210
+ handleMouseLeave() {
211
+ this.isDragging = false
212
+ }
213
+
214
+ private toggleActiveCard(index: number) {
215
+ if (this.activeIndex === index) {
216
+ this.activeIndex = null
217
+ } else {
218
+ this.activeIndex = index
219
+ }
220
+ }
221
+
222
+ private updateSelectedIndex(newIndex: number) {
223
+ this.activeIndex = null
224
+
225
+ this.selectedIndex = Math.max(0, Math.min(newIndex, this.cards.length - 1))
226
+ this.scrollToSelectedCard()
227
+ }
228
+
229
+ private scrollToSelectedCard() {
230
+ const cardHeight = 320
231
+ const targetScrollTop = this.selectedIndex * cardHeight - (window.innerHeight / 2 - cardHeight / 2)
232
+ this.carouselContainer.scrollTo({ top: targetScrollTop, behavior: 'smooth' })
233
+ }
234
+
235
+ private selectCard(index: number) {
236
+ this.selectedIndex = index
237
+ this._notifySelection()
238
+ this.scrollToSelectedCard()
239
+ }
240
+
241
+ private _notifySelection() {
242
+ this.value = this.selectedIndex !== -1 ? this.cards[this.selectedIndex]?.name : undefined
243
+
244
+ this.dispatchEvent(
245
+ new CustomEvent('change', {
246
+ detail: this.value
247
+ })
248
+ )
249
+ }
250
+
251
+ private adjustCardHeight(e: Event, index: number) {
252
+ const imgElement = e.target as HTMLImageElement
253
+ const aspectRatio = imgElement.naturalWidth / imgElement.naturalHeight
254
+ const newHeight = imgElement.offsetWidth / aspectRatio
255
+ imgElement.style.height = `${newHeight}px`
256
+ }
257
+ }