@operato/input 8.0.0-alpha.2 → 8.0.0-alpha.21

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 (31) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/src/ox-input-file.js +3 -2
  3. package/dist/src/ox-input-file.js.map +1 -1
  4. package/dist/src/ox-input-signature.d.ts +4 -5
  5. package/dist/src/ox-input-signature.js +31 -20
  6. package/dist/src/ox-input-signature.js.map +1 -1
  7. package/dist/src/ox-select-floor.d.ts +39 -0
  8. package/dist/src/ox-select-floor.js +251 -0
  9. package/dist/src/ox-select-floor.js.map +1 -0
  10. package/dist/stories/image-for-select-floor.d.ts +1 -0
  11. package/dist/stories/image-for-select-floor.js +2 -0
  12. package/dist/stories/image-for-select-floor.js.map +1 -0
  13. package/dist/stories/{ox-input-/bsignature.stories.js → ox-input-signature.stories.js} +1 -1
  14. package/dist/stories/ox-input-signature.stories.js.map +1 -0
  15. package/dist/stories/ox-select-floor.stories.d.ts +41 -0
  16. package/dist/stories/ox-select-floor.stories.js +163 -0
  17. package/dist/stories/ox-select-floor.stories.js.map +1 -0
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/package.json +8 -4
  20. package/src/ox-input-file.ts +3 -2
  21. package/src/ox-input-signature.ts +30 -21
  22. package/src/ox-select-floor.ts +264 -0
  23. package/stories/image-for-select-floor.ts +2 -0
  24. package/stories/ox-select-floor.stories.ts +192 -0
  25. package/assets/images/icon-editor-gradient-direction.png +0 -0
  26. package/assets/images/icon-properties-label.png +0 -0
  27. package/assets/images/icon-properties-line-type.png +0 -0
  28. package/assets/images/icon-properties-table.png +0 -0
  29. package/dist/stories/ox-input-/bsignature.stories.js.map +0 -1
  30. /package/dist/stories/{ox-input-/bsignature.stories.d.ts" → ox-input-signature.stories.d.ts} +0 -0
  31. /package/stories/{ox-input-/bsignature.stories.ts" → ox-input-signature.stories.ts} +0 -0
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.2",
5
+ "version": "8.0.0-alpha.21",
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
  ]
@@ -212,8 +216,8 @@
212
216
  "@material/web": "^2.0.0",
213
217
  "@operato/color-picker": "^8.0.0-alpha.0",
214
218
  "@operato/i18n": "^8.0.0-alpha.0",
215
- "@operato/popup": "^8.0.0-alpha.0",
216
- "@operato/styles": "^8.0.0-alpha.0",
219
+ "@operato/popup": "^8.0.0-alpha.4",
220
+ "@operato/styles": "^8.0.0-alpha.4",
217
221
  "@operato/utils": "^8.0.0-alpha.0",
218
222
  "@polymer/paper-dropdown-menu": "^3.2.0",
219
223
  "@polymer/paper-item": "^3.0.1",
@@ -258,5 +262,5 @@
258
262
  "prettier --write"
259
263
  ]
260
264
  },
261
- "gitHead": "868f7fd1dfcf1758b0fd8f8c7f198489b3b3bbf8"
265
+ "gitHead": "2a7eabbdab4073648f5d8bd49c02f2e62b7d3a8a"
262
266
  }
@@ -79,12 +79,13 @@ export class OxInputFile extends OxFormField {
79
79
  border-bottom: var(--file-uploader-li-border-bottom);
80
80
  font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
81
81
  color: var(--md-sys-color-on-primary-container);
82
+ display: flex;
83
+ align-items: center;
82
84
  }
83
85
  li md-icon {
84
- float: right;
85
86
  cursor: pointer;
86
- margin: var(--file-uploader-li-icon-margin);
87
87
  font-size: var(--icon-size-small);
88
+ margin-left: auto;
88
89
  }
89
90
  li md-icon:hover,
90
91
  li md-icon:active {
@@ -1,7 +1,7 @@
1
1
  import '@material/web/icon/icon.js'
2
2
 
3
3
  import { css, html, nothing } from 'lit'
4
- import { customElement, property } from 'lit/decorators.js'
4
+ import { customElement, property, query, state } from 'lit/decorators.js'
5
5
 
6
6
  import { OxFormField } from './ox-form-field.js'
7
7
 
@@ -10,27 +10,19 @@ export class OxInputSignature extends OxFormField {
10
10
  static styles = [
11
11
  css`
12
12
  :host {
13
- position: relative;
14
- box-sizing: border-box;
15
-
16
13
  display: flex;
17
14
  flex-direction: column;
18
- place-content: center;
19
- border-radius: var(--border-radius);
20
- padding: var(--padding-default, 9px);
21
- min-height: 100px;
22
- text-transform: capitalize;
15
+ min-height: var(--signature-min-height, 80px);
16
+ min-width: var(--signature-min-width, 120px);
23
17
 
24
- border: var(--file-uploader-border);
25
- background-color: var(--md-sys-color-background);
26
- font: var(--file-uploader-font) !important;
27
- color: var(--file-uploader-color);
18
+ background-color: var(--signature-background-color, white);
28
19
 
29
20
  overflow: hidden;
30
21
  }
31
22
 
32
23
  .signature-preview {
33
24
  flex: 1;
25
+ align-self: stretch;
34
26
 
35
27
  border: 1px solid var(--md-sys-color-outline);
36
28
  background-size: contain;
@@ -46,18 +38,28 @@ export class OxInputSignature extends OxFormField {
46
38
 
47
39
  .controls {
48
40
  margin-top: 10px;
41
+ display: flex;
42
+ flex-direction: row;
43
+ gap: var(--spacing-medium);
44
+ }
45
+
46
+ .filler {
47
+ flex: 1;
49
48
  }
50
49
  `
51
50
  ]
52
51
 
53
- @property({ type: Boolean }) isDrawing = false
54
52
  @property({ type: String }) value: string | null = null
55
53
 
56
- private ctx!: CanvasRenderingContext2D
57
- private canvas!: HTMLCanvasElement
58
- private dialog!: HTMLDialogElement
54
+ @query('.signature-preview')
59
55
  private previewDiv!: HTMLDivElement
60
- private previewCtx!: CanvasRenderingContext2D
56
+ @query('dialog')
57
+ private dialog!: HTMLDialogElement
58
+ @query('canvas')
59
+ private canvas!: HTMLCanvasElement
60
+
61
+ private ctx!: CanvasRenderingContext2D
62
+ private isDrawing = false
61
63
 
62
64
  render() {
63
65
  return html`
@@ -77,6 +79,7 @@ export class OxInputSignature extends OxFormField {
77
79
  ></canvas>
78
80
  <div class="controls">
79
81
  <button @click="${this.clearCanvas}">Clear</button>
82
+ <div class="filler"></div>
80
83
  <button @click="${this.saveSignature}">Save</button>
81
84
  <button @click="${this.closeDialog}">Close</button>
82
85
  </div>
@@ -85,9 +88,6 @@ export class OxInputSignature extends OxFormField {
85
88
  }
86
89
 
87
90
  firstUpdated() {
88
- this.previewDiv = this.shadowRoot!.querySelector('.signature-preview')!
89
- this.dialog = this.shadowRoot!.querySelector('dialog')!
90
- this.canvas = this.dialog.querySelector('canvas')!
91
91
  this.ctx = this.canvas.getContext('2d')!
92
92
  this.ctx.strokeStyle = '#000'
93
93
  this.ctx.lineWidth = 2
@@ -101,6 +101,15 @@ export class OxInputSignature extends OxFormField {
101
101
  openDialog() {
102
102
  if (this.disabled) return
103
103
  this.dialog.showModal()
104
+
105
+ // 다이아로그가 열릴 때 현재 value를 캔버스에 그리기
106
+ if (this.value) {
107
+ const img = new Image()
108
+ img.onload = () => {
109
+ this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height)
110
+ }
111
+ img.src = this.value
112
+ }
104
113
  }
105
114
 
106
115
  closeDialog() {
@@ -0,0 +1,264 @@
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
+ opacity: 0.8;
61
+ z-index: 1;
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
+ z-index: 2;
69
+ transform: perspective(var(--ox-select-floor-perspective)) rotateX(var(--ox-select-floor-rotate-x-active));
70
+ box-shadow: 0 12px 24px rgba(59, 130, 246, 0.4);
71
+ }
72
+
73
+ [template-container] {
74
+ position: absolute;
75
+ right: 10px;
76
+ z-index: 1;
77
+ }
78
+ `
79
+ ]
80
+
81
+ @property({ type: Array }) cards: Card[] = []
82
+ @property({ type: String }) value?: string
83
+ @property({ type: Number }) bottomLimit = 70
84
+
85
+ @state() private selectedIndex = -1
86
+ @state() private activeIndex: number | null = null
87
+ @query('.carousel-container') private carouselContainer!: HTMLDivElement
88
+
89
+ private isDragging = false
90
+ private lastTouchY = 0
91
+ private lastMouseY = 0
92
+ private startY = 0
93
+
94
+ render() {
95
+ const length = this.cards.length
96
+ const cards = this.cards
97
+
98
+ return html`
99
+ <div
100
+ class="carousel-container"
101
+ @wheel=${this.handleWheel}
102
+ @mousedown=${this.handleMouseDown}
103
+ @mousemove=${this.handleMouseMove}
104
+ @mouseup=${this.handleMouseUp}
105
+ @mouseleave=${this.handleMouseLeave}
106
+ @touchstart=${this.handleTouchStart}
107
+ @touchmove=${this.handleTouchMove}
108
+ @touchend=${this.handleTouchEnd}
109
+ >
110
+ ${cards.map(({ image, name }, index) => {
111
+ return html`
112
+ <div
113
+ class="card ${this.getClassForCard(index)} ${this.isActive(index) ? 'active' : ''}"
114
+ style="bottom: ${(this.bottomLimit * index) / length}%;"
115
+ @click=${() => this.selectCard(index)}
116
+ @tap=${() => this.toggleActiveCard(index)}
117
+ >
118
+ <img src="${image}" @load=${(e: Event) => this.adjustCardHeight(e, index)} />
119
+ </div>
120
+ `
121
+ })}
122
+ ${cards.map(({ image, name }, index) => {
123
+ return html`
124
+ <div
125
+ style="bottom: ${(this.bottomLimit * index) / length}%;"
126
+ @click=${() => this.selectCard(index)}
127
+ template-container
128
+ >
129
+ <slot name="template-${index}"></slot>
130
+ </div>
131
+ `
132
+ })}
133
+ </div>
134
+ `
135
+ }
136
+
137
+ updated(changes: PropertyValues<this>) {
138
+ if (changes.has('value')) {
139
+ if (this.value) {
140
+ this.selectedIndex = this.cards.findIndex(card => card.name == this.value)
141
+ } else {
142
+ this.selectedIndex = -1
143
+ }
144
+ }
145
+ }
146
+
147
+ firstUpdated() {
148
+ this.scrollToSelectedCard()
149
+ }
150
+
151
+ getClassForCard(index: number) {
152
+ return index === this.selectedIndex ? 'selected' : 'compressed'
153
+ }
154
+
155
+ isActive(index: number): boolean {
156
+ return this.activeIndex === index
157
+ }
158
+
159
+ handleWheel(event: WheelEvent) {
160
+ event.preventDefault()
161
+ const delta = Math.sign(event.deltaY)
162
+ this.updateSelectedIndex(this.selectedIndex + delta)
163
+ }
164
+
165
+ handleTouchStart(event: TouchEvent) {
166
+ this.lastTouchY = event.touches[0].clientY
167
+ this.startY = this.lastTouchY
168
+ }
169
+
170
+ handleTouchMove(event: TouchEvent) {
171
+ const touch = event.touches[0]
172
+ const deltaY = touch.clientY - this.lastMouseY
173
+
174
+ if (!this.lastMouseY) {
175
+ this.lastMouseY = touch.clientY
176
+ }
177
+
178
+ if (Math.abs(deltaY) > 30) {
179
+ this.lastMouseY = touch.clientY
180
+ const direction = deltaY > 0 ? -1 : 1
181
+ this.updateSelectedIndex(this.selectedIndex + direction)
182
+ }
183
+ }
184
+
185
+ handleTouchEnd() {
186
+ this.isDragging = false
187
+ }
188
+
189
+ handleMouseDown(event: MouseEvent) {
190
+ this.isDragging = true
191
+ this.lastMouseY = event.clientY
192
+ }
193
+
194
+ handleMouseMove(event: MouseEvent) {
195
+ if (!this.isDragging) {
196
+ return
197
+ }
198
+
199
+ const deltaY = event.clientY - this.lastMouseY
200
+
201
+ if (!this.lastMouseY) {
202
+ this.lastMouseY = event.clientY
203
+ }
204
+
205
+ if (Math.abs(deltaY) > 30) {
206
+ this.lastMouseY = event.clientY
207
+ const direction = deltaY > 0 ? -1 : 1
208
+ this.updateSelectedIndex(this.selectedIndex + direction)
209
+ }
210
+ }
211
+
212
+ handleMouseUp() {
213
+ this.isDragging = false
214
+ }
215
+
216
+ handleMouseLeave() {
217
+ this.isDragging = false
218
+ }
219
+
220
+ private toggleActiveCard(index: number) {
221
+ if (this.activeIndex === index) {
222
+ this.activeIndex = null
223
+ } else {
224
+ this.activeIndex = index
225
+ }
226
+ }
227
+
228
+ private updateSelectedIndex(newIndex: number) {
229
+ this.activeIndex = null
230
+
231
+ this.selectedIndex = Math.max(-1, Math.min(newIndex, this.cards.length - 1))
232
+ this.scrollToSelectedCard()
233
+ }
234
+
235
+ private scrollToSelectedCard() {
236
+ const cardHeight = 320
237
+ const targetScrollTop = this.selectedIndex * cardHeight - (window.innerHeight / 2 - cardHeight / 2)
238
+
239
+ this.carouselContainer.scrollTo({ top: targetScrollTop, behavior: 'smooth' })
240
+ }
241
+
242
+ private selectCard(index: number) {
243
+ this.selectedIndex = index
244
+ this.notifySelection()
245
+ this.scrollToSelectedCard()
246
+ }
247
+
248
+ private notifySelection() {
249
+ this.value = this.selectedIndex !== -1 ? this.cards[this.selectedIndex]?.name : undefined
250
+
251
+ this.dispatchEvent(
252
+ new CustomEvent('change', {
253
+ detail: this.value
254
+ })
255
+ )
256
+ }
257
+
258
+ private adjustCardHeight(e: Event, index: number) {
259
+ const imgElement = e.target as HTMLImageElement
260
+ const aspectRatio = imgElement.naturalWidth / imgElement.naturalHeight
261
+ const newHeight = imgElement.offsetWidth / aspectRatio
262
+ imgElement.style.height = `${newHeight}px`
263
+ }
264
+ }