@operato/popup 2.0.0-beta.9 → 7.0.0-rc.10

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.
@@ -1,4 +1,5 @@
1
1
  import '@material/web/icon/icon.js'
2
+ import '@operato/input/ox-input-search.js'
2
3
 
3
4
  import { css, html, LitElement, PropertyValues } from 'lit'
4
5
  import { customElement, property, query } from 'lit/decorators.js'
@@ -55,8 +56,8 @@ export class OxFloatingOverlay extends LitElement {
55
56
  height: 100vh;
56
57
  height: 100dvh;
57
58
 
58
- background-color: var(--md-sys-color-primary);
59
- opacity: 0.2;
59
+ background-color: var(--md-sys-color-shadow, #000);
60
+ opacity: 0.4;
60
61
  }
61
62
 
62
63
  [overlayed] {
@@ -75,11 +76,13 @@ export class OxFloatingOverlay extends LitElement {
75
76
  transform: translate(-50%, -50%);
76
77
 
77
78
  opacity: 0;
79
+ border: 2px solid var(--md-sys-color-primary);
78
80
  }
79
81
 
80
82
  [overlayed][hovering='center'][opened] {
81
83
  opacity: 1;
82
84
  transition: opacity 0.3s ease-in;
85
+ background-color: var(--md-sys-color-surface-container-lowest);
83
86
  }
84
87
 
85
88
  [hovering='center'] {
@@ -186,9 +189,9 @@ export class OxFloatingOverlay extends LitElement {
186
189
  display: flex;
187
190
  flex-direction: row;
188
191
  align-items: center;
189
- background-color: var(--md-sys-color-primary-container);
192
+ background-color: var(--md-sys-color-primary);
190
193
  font-weight: var(--md-sys-typescale-label-large-weight, var(--md-ref-typeface-weight-medium, 500));
191
- color: var(--md-sys-color-on-primary-container);
194
+ color: var(--md-sys-color-on-primary);
192
195
  }
193
196
 
194
197
  slot[name='header'] {
@@ -235,13 +238,24 @@ export class OxFloatingOverlay extends LitElement {
235
238
  }
236
239
 
237
240
  [search] {
241
+ --help-icon-color: var(--md-sys-color-primary);
242
+ --help-icon-hover-color: var(--md-sys-color-primary);
243
+ --help-icon-opacity: 0.2;
244
+ --help-icon-size: 20px;
245
+
246
+ --md-icon-size: 20px;
247
+
248
+ --input-search-padding: var(--spacing-medium);
249
+ --input-search-focus-border-bottom: none;
250
+ --input-search-font: normal 16px var(--theme-font);
251
+
238
252
  display: flex;
239
253
  justify-content: space-between;
240
254
  align-items: center;
241
255
 
242
256
  align-self: center;
243
- color: var(--md-sys-color-on-secondary-container);
244
- background-color: var(--opacity-light-color);
257
+ color: var(--md-sys-color-primary);
258
+ background-color: var(--md-sys-color-on-primary);
245
259
  border-radius: 999em;
246
260
  padding: 0 var(--spacing-medium);
247
261
  }
@@ -250,6 +264,12 @@ export class OxFloatingOverlay extends LitElement {
250
264
  align-self: center;
251
265
  }
252
266
 
267
+ ox-input-search {
268
+ color: var(--md-sys-color-primary);
269
+ background-color: var(--md-sys-color-on-primary);
270
+ border-radius: var(--md-sys-shape-corner-full);
271
+ }
272
+
253
273
  @media screen and (max-width: 460px) {
254
274
  [closable][historyback] {
255
275
  display: block;
@@ -8,7 +8,7 @@ import Sortable from 'sortablejs'
8
8
  import { OxPopup } from './ox-popup'
9
9
 
10
10
  function guaranteeFocus(element: HTMLElement) {
11
- // 1. 옵션 엘리먼트의 하위 첫번째 focusible 엘리먼트에 focus 기회를 준다.
11
+ // 1. Give focus opportunity to the first focusable element within the option element.
12
12
  const focusible = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
13
13
 
14
14
  if (focusible) {
@@ -16,7 +16,7 @@ function guaranteeFocus(element: HTMLElement) {
16
16
  return
17
17
  }
18
18
 
19
- // 2. 자신을 포함해서 가장 가까운 부모에게 focus 기회를 준다.
19
+ // 2. Give focus opportunity to the closest parent, including itself.
20
20
  const closest = element.closest(
21
21
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
22
22
  ) as HTMLElement
@@ -35,10 +35,11 @@ export class OxPopupList extends OxPopup {
35
35
  :host {
36
36
  display: none;
37
37
  align-items: stretch;
38
- background-color: var(--ox-popup-list-background-color, var(--md-sys-color-surface));
38
+ background-color: var(--ox-popup-list-background-color, var(--md-sys-color-surface-container-lowest));
39
39
  z-index: 100;
40
40
  box-shadow: 2px 3px 10px 5px rgba(0, 0, 0, 0.15);
41
41
  padding: var(--spacing-small) 0;
42
+ border-radius: var(--md-sys-shape-corner-small, 5px);
42
43
 
43
44
  color: var(--ox-popup-list-color, var(--md-sys-color-primary-container));
44
45
  font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
@@ -283,15 +284,20 @@ export class OxPopupList extends OxPopup {
283
284
  const to = e.relatedTarget as HTMLElement
284
285
 
285
286
  if (!this.contains(to)) {
286
- /* 분명히 범위가 아닌 엘리먼트로 포커스가 옮겨졌다면, ox-popup-list 닫혀야 한다. */
287
+ /* If the focus has clearly moved to an element outside of my range, the ox-popup-list should be closed. */
287
288
  // @ts-ignore for debug
288
- !this.debug && !window.POPUP_DEBUG && this.close()
289
+ !this.preventCloseOnBlur && !this.debug && !window.POPUP_DEBUG && this.close()
289
290
  }
290
291
  }.bind(this)
291
292
 
292
293
  protected _onclick: (e: MouseEvent) => void = function (this: OxPopupList, e: MouseEvent) {
293
294
  e.stopPropagation()
294
295
 
296
+ // Check if the click event target is a checkbox
297
+ if ((e.target as HTMLElement).closest('input[type="checkbox"]')) {
298
+ return // Do not proceed if it's a checkbox click
299
+ }
300
+
295
301
  const option = (e.target as HTMLElement)?.closest('[option]')
296
302
  if (option) {
297
303
  this.setActive(option, true)
@@ -323,6 +329,10 @@ export class OxPopupList extends OxPopup {
323
329
  )
324
330
  },
325
331
  onMove: e => {
332
+ // Check if the drag event target is a checkbox
333
+ if ((e.dragged as HTMLElement).querySelector('input[type="checkbox"]')) {
334
+ return false // Prevent sorting if it's a checkbox drag
335
+ }
326
336
  this.locked = true
327
337
  }
328
338
  })
@@ -72,7 +72,7 @@ export class OxPopupMenu extends OxPopup {
72
72
  }
73
73
 
74
74
  ::slotted(ox-popup-menuitem[active]) {
75
- border-left: 3px solid var(--md-ref-palette-primary50);
75
+ border-left: 3px solid var(--md-sys-color-primary);
76
76
  font-weight: var(--md-sys-typescale-label-large-weight, var(--md-ref-typeface-weight-medium, 500));
77
77
  }
78
78
 
package/src/ox-popup.ts CHANGED
@@ -34,6 +34,7 @@ export class OxPopup extends LitElement {
34
34
 
35
35
  :host([active]) {
36
36
  display: flex;
37
+ flex-direction: column;
37
38
  }
38
39
 
39
40
  :host(*:focus) {
@@ -42,7 +43,7 @@ export class OxPopup extends LitElement {
42
43
  `
43
44
  ]
44
45
 
45
- @property({ type: Boolean }) debug: boolean = false
46
+ @property({ type: Boolean, attribute: 'prevent-close-on-blur' }) preventCloseOnBlur: boolean = false
46
47
 
47
48
  @state() _parent: Element | null = null
48
49
 
@@ -57,8 +58,8 @@ export class OxPopup extends LitElement {
57
58
 
58
59
  if (!this.contains(to)) {
59
60
  /* 분명히 내 범위가 아닌 엘리먼트로 포커스가 옮겨졌다면, ox-popup은 닫혀야 한다. */
60
- // @ts-ignore for debug
61
- !this.debug && !window.POPUP_DEBUG && this.close()
61
+ // @ts-ignore for preventCloseOnBlur
62
+ !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()
62
63
  }
63
64
  }.bind(this)
64
65
 
@@ -104,8 +105,8 @@ export class OxPopup extends LitElement {
104
105
  }.bind(this)
105
106
 
106
107
  protected _onwindowblur: (e: Event) => void = function (this: OxPopup, e: Event) {
107
- // @ts-ignore for debug
108
- !this.debug && !window.POPUP_DEBUG && this.close()
108
+ // @ts-ignore for preventCloseOnBlur
109
+ !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()
109
110
  }.bind(this)
110
111
 
111
112
  connectedCallback() {
@@ -143,7 +144,8 @@ export class OxPopup extends LitElement {
143
144
  width,
144
145
  height,
145
146
  parent,
146
- style
147
+ style,
148
+ preventCloseOnBlur
147
149
  }: {
148
150
  template: unknown
149
151
  top?: number
@@ -154,9 +156,13 @@ export class OxPopup extends LitElement {
154
156
  height?: string
155
157
  parent?: Element | null
156
158
  style?: string
159
+ preventCloseOnBlur?: boolean
157
160
  }): OxPopup {
158
161
  const owner = parent || document.body
159
162
  const target = document.createElement('ox-popup') as OxPopup
163
+ if (preventCloseOnBlur) {
164
+ target.preventCloseOnBlur = preventCloseOnBlur
165
+ }
160
166
  if (style) {
161
167
  target.style.cssText = style
162
168
  }
package/src/ox-prompt.ts CHANGED
@@ -1,4 +1,5 @@
1
- import '@material/web/button/elevated-button.js'
1
+ import '@material/web/button/filled-button.js'
2
+ import '@material/web/button/outlined-button.js'
2
3
  import '@material/web/icon/icon.js'
3
4
  import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
4
5
 
@@ -98,27 +99,22 @@ export class OxPrompt extends LitElement {
98
99
 
99
100
  #confirm {
100
101
  --md-filled-button-container-color: var(--md-sys-color-primary);
101
- --md-filled-button-label-text-color: var(--md-sys-color-primary-container);
102
+ --md-filled-button-label-text-color: var(--md-sys-color-on-primary);
102
103
  --md-filled-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);
103
104
  --md-filled-button-container-height: var(--form-element-height-medium);
104
105
  --md-filled-button-container-shape: var(--md-sys-shape-corner-small);
105
106
  --md-filled-button-leading-space: var(--spacing-large);
106
107
  --md-filled-button-trailing-space: var(--spacing-large);
107
- --md-filled-button-hover-label-text-color: var(--md-sys-color-secondary-container);
108
- --md-filled-button-pressed-label-text-color: var(--md-sys-color-secondary-container);
109
- --md-filled-button-focus-label-text-color: var(--md-sys-color-secondary-container);
110
108
  }
111
109
 
112
110
  #cancel {
111
+ --md-outlined-button-container-color: var(--md-sys-color-surface-variant);
113
112
  --md-outlined-button-label-text-color: var(--md-sys-color-on-surface-variant);
114
113
  --md-outlined-button-label-text-size: var(--md-sys-typescale-label-large-size, 0.875rem);
115
114
  --md-outlined-button-container-height: var(--form-element-height-medium);
116
115
  --md-outlined-button-container-shape: var(--md-sys-shape-corner-small);
117
116
  --md-outlined-button-leading-space: var(--spacing-large);
118
117
  --md-outlined-button-trailing-space: var(--spacing-large);
119
- --md-outlined-button-hover-label-text-color: var(--md-sys-color-primary);
120
- --md-outlined-button-pressed-outline-color: var(--md-sys-color-primary);
121
- --md-outlined-button-pressed-label-text-color: var(--md-sys-color-primary);
122
118
  }
123
119
  `
124
120
  ]
@@ -163,6 +159,11 @@ export class OxPrompt extends LitElement {
163
159
  */
164
160
  @property({ type: Object }) cancelButton?: { text: string; color?: string }
165
161
 
162
+ /**
163
+ * Prevents the popup from closing when it loses focus (blur event).
164
+ */
165
+ @property({ type: Boolean, attribute: 'prevent-close-on-blur' }) preventCloseOnBlur: boolean = false
166
+
166
167
  /**
167
168
  * A callback function called when the popup is closed, providing the result of the user's interaction.
168
169
  */
@@ -187,14 +188,14 @@ export class OxPrompt extends LitElement {
187
188
  ${this.confirmButton
188
189
  ? html`
189
190
  <md-filled-button id="confirm" @click=${(e: Event) => this.onConfirm()}
190
- >${this.confirmButton?.text}</md-elevated-button
191
+ >${this.confirmButton?.text}</md-filled-button
191
192
  >
192
193
  `
193
194
  : nothing}
194
195
  ${this.cancelButton
195
196
  ? html`
196
197
  <md-outlined-button id="cancel" @click=${(e: Event) => this.onCancel()}
197
- >${this.cancelButton?.text}</md-elevated-button
198
+ >${this.cancelButton?.text}</md-outlined-button
198
199
  >
199
200
  `
200
201
  : nothing}
@@ -224,7 +225,7 @@ export class OxPrompt extends LitElement {
224
225
  if (!this.contains(to)) {
225
226
  /* 분명히 내 범위가 아닌 엘리먼트로 포커스가 옮겨졌다면, ox-prompt은 닫혀야 한다. */
226
227
  // @ts-ignore for debug
227
- if (window.POPUP_DEBUG) {
228
+ if (this.preventCloseOnBlur || window.POPUP_DEBUG) {
228
229
  return
229
230
  }
230
231
 
@@ -279,7 +280,7 @@ export class OxPrompt extends LitElement {
279
280
 
280
281
  protected _onwindowblur: (e: Event) => void = function (this: OxPrompt, e: Event) {
281
282
  // @ts-ignore for debug
282
- if (window.POPUP_DEBUG) {
283
+ if (this.preventCloseOnBlur || window.POPUP_DEBUG) {
283
284
  return
284
285
  }
285
286
 
@@ -326,6 +327,7 @@ export class OxPrompt extends LitElement {
326
327
  * @param {string} [options.width] - The maximum width of the popup (CSS string).
327
328
  * @param {string} [options.height] - The maximum height of the popup (CSS string).
328
329
  * @param {Element | null} [options.parent] - The parent element to which the popup should be attached. If not provided, it will be attached to the document body.
330
+ * @param {boolean} [options.preventCloseOnBlur] - Prevents the popup from closing when it loses focus (blur event).
329
331
  * @param {(result: { value: boolean }) => void} [options.callback] - A callback function that will be invoked when the user interacts with the popup, providing the result of the interaction.
330
332
  * @returns {Promise<boolean>} A Promise that resolves based on user interaction with the popup.
331
333
  */
@@ -345,6 +347,7 @@ export class OxPrompt extends LitElement {
345
347
  width,
346
348
  height,
347
349
  parent,
350
+ preventCloseOnBlur,
348
351
  callback
349
352
  }: {
350
353
  template?: unknown
@@ -362,6 +365,7 @@ export class OxPrompt extends LitElement {
362
365
  width?: string
363
366
  height?: string
364
367
  parent?: Element | null
368
+ preventCloseOnBlur?: boolean
365
369
  callback?: (result: { value: boolean }) => void
366
370
  }): Promise<boolean> {
367
371
  const owner = parent || document.body
@@ -374,6 +378,7 @@ export class OxPrompt extends LitElement {
374
378
  target.footer = footer
375
379
  target.confirmButton = confirmButton
376
380
  target.cancelButton = cancelButton
381
+ target.preventCloseOnBlur = !!preventCloseOnBlur
377
382
 
378
383
  render(template, target)
379
384
 
@@ -6,12 +6,14 @@ import '../src/ox-popup-list.js'
6
6
  import { html, TemplateResult } from 'lit'
7
7
 
8
8
  import { OxPopupList } from '../src/ox-popup-list.js'
9
+ import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
9
10
 
10
11
  export default {
11
12
  title: 'sortable OxPopupList',
12
13
  component: 'ox-popup-list',
13
14
  argTypes: {
14
- debug: { control: 'boolean' }
15
+ preventCloseOnBlur: { control: 'boolean' },
16
+ theme: { control: 'select', options: ['light', 'dark'] }
15
17
  }
16
18
  }
17
19
 
@@ -22,7 +24,8 @@ interface Story<T> {
22
24
  }
23
25
 
24
26
  interface ArgTypes {
25
- debug: boolean
27
+ preventCloseOnBlur: boolean
28
+ theme?: 'light' | 'dark'
26
29
  }
27
30
 
28
31
  function popup(e: MouseEvent) {
@@ -88,7 +91,13 @@ const columns = [
88
91
  }
89
92
  ]
90
93
 
91
- const Template: Story<ArgTypes> = ({ debug }: ArgTypes) => html`
94
+ const Template: Story<ArgTypes> = ({ preventCloseOnBlur, theme }: ArgTypes) => html`
95
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet" />
96
+
97
+ <link href="/themes/light.css" rel="stylesheet" />
98
+ <link href="/themes/dark.css" rel="stylesheet" />
99
+ <link href="/themes/spacing.css" rel="stylesheet" />
100
+
92
101
  <link
93
102
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
94
103
  rel="stylesheet"
@@ -102,6 +111,10 @@ const Template: Story<ArgTypes> = ({ debug }: ArgTypes) => html`
102
111
  rel="stylesheet"
103
112
  />
104
113
 
114
+ <style>
115
+ ${MDTypeScaleStyles.cssText}
116
+ </style>
117
+
105
118
  <style>
106
119
  #place {
107
120
  width: 100%;
@@ -109,6 +122,9 @@ const Template: Story<ArgTypes> = ({ debug }: ArgTypes) => html`
109
122
  background: lightgreen;
110
123
  text-align: center;
111
124
  line-height: 100px;
125
+
126
+ background-color: var(--md-sys-color-primary-container);
127
+ color: var(--md-sys-color-on-primary-container);
112
128
  }
113
129
 
114
130
  ox-popup-list {
@@ -160,6 +176,10 @@ const Template: Story<ArgTypes> = ({ debug }: ArgTypes) => html`
160
176
  }
161
177
  </style>
162
178
 
179
+ <script>
180
+ document.body.classList.add('${theme}')
181
+ </script>
182
+
163
183
  <div id="place" @click=${(e: MouseEvent) => popup(e)}>
164
184
  Click this to popup list
165
185
  <ox-popup-list
@@ -170,7 +190,7 @@ const Template: Story<ArgTypes> = ({ debug }: ArgTypes) => html`
170
190
  }}
171
191
  multiple
172
192
  sortable
173
- ?debug=${debug}
193
+ ?prevent-close-on-blur=${preventCloseOnBlur}
174
194
  >
175
195
  <div slot="header" titler>Plain Text <md-outlined-button>save</md-outlined-button></div>
176
196
  ${columns.map(
@@ -9,7 +9,7 @@ export default {
9
9
  component: 'ox-prompt',
10
10
  argTypes: {
11
11
  type: { control: 'select', options: ['success', 'error', 'warning', 'info', 'question'] },
12
- debug: { control: 'boolean' },
12
+ preventCloseOnBlur: { control: 'boolean' },
13
13
  theme: { control: 'select', options: ['light', 'dark'] }
14
14
  }
15
15
  }
@@ -22,7 +22,7 @@ interface Story<T> {
22
22
 
23
23
  interface ArgTypes {
24
24
  type: 'success' | 'error' | 'warning' | 'info' | 'question'
25
- debug: boolean
25
+ preventCloseOnBlur: boolean
26
26
  theme?: 'light' | 'dark'
27
27
  }
28
28
 
@@ -30,23 +30,22 @@ function popup(
30
30
  e: MouseEvent,
31
31
  type: 'success' | 'error' | 'warning' | 'info' | 'question' = 'warning',
32
32
  theme: 'light' | 'dark',
33
- debug: boolean
33
+ preventCloseOnBlur: boolean
34
34
  ) {
35
35
  const noImage = new URL('/assets/images/no-image.png', import.meta.url).href
36
36
 
37
- //@ts-ignore
38
- window.POPUP_DEBUG = debug
39
-
40
37
  OxPrompt.open({
41
38
  title: 'Are you sure ?',
42
39
  text: 'Are you sure to exit this page ?',
43
40
  type,
41
+ icon: noImage,
44
42
  confirmButton: { text: 'Confirm' },
45
- cancelButton: { text: 'Cancel' }
43
+ cancelButton: { text: 'Cancel' },
44
+ preventCloseOnBlur
46
45
  })
47
46
  }
48
47
 
49
- const Template: Story<ArgTypes> = ({ type, debug, theme = 'light' }: ArgTypes) => html`
48
+ const Template: Story<ArgTypes> = ({ type, preventCloseOnBlur, theme = 'light' }: ArgTypes) => html`
50
49
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet" />
51
50
 
52
51
  <link href="/themes/light.css" rel="stylesheet" />
@@ -85,7 +84,11 @@ const Template: Story<ArgTypes> = ({ type, debug, theme = 'light' }: ArgTypes) =
85
84
  document.body.classList.add('${theme}')
86
85
  </script>
87
86
 
88
- <div id="place" @click=${(e: MouseEvent) => popup(e, type, theme, debug)} class="md-typescale-display-medium">
87
+ <div
88
+ id="place"
89
+ @click=${(e: MouseEvent) => popup(e, type, theme, preventCloseOnBlur)}
90
+ class="md-typescale-display-medium"
91
+ >
89
92
  Click this to prompt image
90
93
  </div>
91
94
  `
@@ -93,5 +96,5 @@ const Template: Story<ArgTypes> = ({ type, debug, theme = 'light' }: ArgTypes) =
93
96
  export const Regular = Template.bind({})
94
97
  Regular.args = {
95
98
  type: 'warning',
96
- debug: true
99
+ preventCloseOnBlur: true
97
100
  }
@@ -48,11 +48,9 @@ body {
48
48
  --grid-record-wide-fontsize: var(--md-sys-typescale-label-medium-size);
49
49
  --grid-record-selected-background-color: var(--md-sys-color-primary-container);
50
50
  --grid-record-selected-color: var(--md-sys-color-primary);
51
- --grid-record-focused-background-color: var(--md-sys-color-secondary-container);
52
51
  --grid-record-focused-border: 1px solid var(--md-sys-color-outline-variant);
53
52
  --grid-record-focused-cell-background-color: var(--md-sys-color-secondary-container);
54
53
  --grid-record-focused-cell-border: 1px dashed var(--md-sys-color-outline);
55
- --grid-record-focused-color: var(--md-sys-color-secondary);
56
54
  --grid-record-focused-box-shadow: 0px 2px 0px 0px rgb(0 0 0 / 10%);
57
55
  --grid-record-emphasized-background-color: var(--md-sys-color-tertiary-container);
58
56
  --grid-record-emphasized-color: var(--md-sys-color-tertiary);
@@ -63,7 +61,7 @@ body {
63
61
 
64
62
  --grid-record-dirty-border-top: 24px solid rgba(var(--md-sys-color-primary-rgb), 0.6);
65
63
  --grid-record-dirty-border-left: 24px solid transparent;
66
- --grid-record-dirty-icon-font: bold 10px/12px var(--mdc-icon-font, 'Material Icons');
64
+ --grid-record-dirty-icon-font: bold 10px/12px var(--md-icon-font, 'Material Symbols Outlined');
67
65
  --grid-record-dirty-icon-size: var(--fontsize-large);
68
66
  --grid-record-dirty-color: var(--md-sys-color-surface);
69
67