@oscarpalmer/oui 0.30.0 → 0.32.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.
@@ -1,7 +1,7 @@
1
1
  import { attributable } from "../internal/attributable.mjs";
2
2
  import { clamp } from "@oscarpalmer/atoms/number";
3
3
  import { setAttribute } from "@oscarpalmer/toretto/attribute";
4
- import { on } from "@oscarpalmer/toretto/event";
4
+ import { dispatch, on } from "@oscarpalmer/toretto/event";
5
5
  import { findAncestor } from "@oscarpalmer/toretto/find";
6
6
  import { isPlainObject } from "@oscarpalmer/atoms/is";
7
7
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
@@ -172,10 +172,9 @@ function onTab(event) {
172
172
  const element = findAncestor(event, SELECTOR);
173
173
  const instance = instances.get(element);
174
174
  if (instance == null) return;
175
- const dispatch = new CustomEvent("focus-trap:tab", { cancelable: true });
176
- element.dispatchEvent(dispatch);
175
+ const dispatched = dispatch(element, "focus-trap");
177
176
  setTimeout(() => {
178
- if (dispatch.defaultPrevented) return;
177
+ if (dispatched.defaultPrevented) return;
179
178
  if (!instance.disabled) event.preventDefault();
180
179
  const elements = getFocusable(element);
181
180
  const index = elements.indexOf(event.target);
@@ -130,16 +130,12 @@ function onPointerdown(event) {
130
130
  };
131
131
  globals.offset = item.state.options.position.getOffset(globals, item, element, position);
132
132
  globals.original = item.state.options.position.getOriginal(globals, item, position);
133
- const dispatch = new CustomEvent(`${item.state.name}:begin`, {
134
- detail: {
135
- element,
136
- position: { ...globals.original }
137
- },
138
- cancelable: true
139
- });
140
- item.state.element.dispatchEvent(dispatch);
133
+ const dispatched = dispatch(item.state.element, `${item.state.name}:begin`, { detail: {
134
+ element,
135
+ position: { ...globals.original }
136
+ } });
141
137
  setTimeout(() => {
142
- if (dispatch.defaultPrevented) {
138
+ if (dispatched.defaultPrevented) {
143
139
  item.state.options.drag.onCancel?.(globals, item);
144
140
  reset();
145
141
  return;
@@ -165,13 +161,9 @@ function onPointermove(event) {
165
161
  calculated: next,
166
162
  original: position
167
163
  });
168
- const dispatch = new CustomEvent(`${current.state.name}:move`, {
169
- detail,
170
- cancelable: true
171
- });
172
- current.state.element.dispatchEvent(dispatch);
164
+ const dispatched = dispatch(current.state.element, `${current.state.name}:move`, { detail });
173
165
  setTimeout(() => {
174
- if (dispatch.defaultPrevented) cancelDrag(current);
166
+ if (dispatched.defaultPrevented) cancelDrag(current);
175
167
  });
176
168
  }
177
169
  function onPointerup(event) {
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/js/menu.mjs CHANGED
@@ -2,7 +2,7 @@ import { createEmbeddedFloatable, getOnBeforeToggleListener } from "./floatable/
2
2
  import { attributable } from "./internal/attributable.mjs";
3
3
  import { NAVIGABLE_KEYS_HORIZONTAL, NAVIGABLE_KEYS_VERTICAL, getNavigable, updateNavigableFocus } from "./internal/navigable.mjs";
4
4
  import { setAria, setRole } from "@oscarpalmer/toretto/aria";
5
- import { setAttribute } from "@oscarpalmer/toretto/attribute";
5
+ import { setAttribute, setAttributes } from "@oscarpalmer/toretto/attribute";
6
6
  import { on } from "@oscarpalmer/toretto/event";
7
7
  import { findAncestor } from "@oscarpalmer/toretto/find";
8
8
  //#region src/js/menu.ts
@@ -104,7 +104,10 @@ function initializeMenu(menu) {
104
104
  expanded: "false",
105
105
  haspopup: "menu"
106
106
  });
107
- setAttribute(menu.content, "tabindex", "-1");
107
+ setAttributes(menu.content, {
108
+ popover: "",
109
+ tabindex: "-1"
110
+ });
108
111
  setRole(menu.content, "menu");
109
112
  }
110
113
  function onDeactivate(_, navigable) {
@@ -1,7 +1,7 @@
1
1
  import { attributable } from "../internal/attributable.mjs";
2
2
  import { OuiDraggable, addDraggable, getDraggableStates, removeDraggable } from "../internal/draggable.mjs";
3
3
  import { setAttribute } from "@oscarpalmer/toretto/attribute";
4
- import { getPosition, on } from "@oscarpalmer/toretto/event";
4
+ import { dispatch, getPosition, on } from "@oscarpalmer/toretto/event";
5
5
  import { getStyles, setStyle, setStyles } from "@oscarpalmer/toretto/style";
6
6
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
7
7
  //#region src/js/movable/movable.embedded.ts
@@ -110,16 +110,12 @@ function onEnd(event, globals, item, reset) {
110
110
  }
111
111
  const container = state.container?.getBoundingClientRect();
112
112
  if (container != null) setOffset(state, container);
113
- const end = new CustomEvent("movable:end", {
114
- detail: {
115
- from: { ...globals.original },
116
- to: getPosition(event)
117
- },
118
- cancelable: true
119
- });
120
- state.element.dispatchEvent(end);
113
+ const dispatched = dispatch(state.element, "movable:end", { detail: {
114
+ from: { ...globals.original },
115
+ to: getPosition(event)
116
+ } });
121
117
  setTimeout(() => {
122
- if (end.defaultPrevented) onCancel(globals, item);
118
+ if (dispatched.defaultPrevented) onCancel(globals, item);
123
119
  reset();
124
120
  });
125
121
  }
@@ -18,7 +18,6 @@ var OuiResizable = class {
18
18
  }
19
19
  set() {
20
20
  const [width, height] = getAndSetDimensions(this.#state.element, this.#state.options);
21
- console.log("set", width, height);
22
21
  this.#state.width = width;
23
22
  this.#state.height = height;
24
23
  }
@@ -131,10 +130,9 @@ function onPointerdown(event) {
131
130
  const position = getPosition(event);
132
131
  if (stored == null || position == null) return;
133
132
  if (supportsTouch.value) event.preventDefault();
134
- const dispatch = new CustomEvent("resizable:begin", { cancelable: true });
135
- element.dispatchEvent(dispatch);
133
+ const dispatched = dispatch(element, "resizable:begin");
136
134
  setTimeout(() => {
137
- if (dispatch.defaultPrevented) return;
135
+ if (dispatched.defaultPrevented) return;
138
136
  current = stored;
139
137
  start = position;
140
138
  current.state.styles.set();
@@ -151,24 +149,19 @@ function onPointermove(event) {
151
149
  height,
152
150
  width
153
151
  });
154
- const dispatch = new CustomEvent("resizable:resize", {
155
- cancelable: true,
156
- detail: {
157
- height,
158
- width
159
- }
160
- });
161
- current.state.element.dispatchEvent(dispatch);
152
+ const dispatched = dispatch(current.state.element, "resizable:resize", { detail: {
153
+ height,
154
+ width
155
+ } });
162
156
  setTimeout(() => {
163
- if (dispatch.defaultPrevented) cancelResize(current);
157
+ if (dispatched.defaultPrevented) cancelResize(current);
164
158
  });
165
159
  }
166
160
  function onPointerup() {
167
161
  if (current == null) return;
168
- const dispatch = new CustomEvent("resizable:end", { cancelable: true });
169
- current.state.element.dispatchEvent(dispatch);
162
+ const dispatched = dispatch(current.state.element, "resizable:end");
170
163
  setTimeout(() => {
171
- if (current == null || dispatch.defaultPrevented) {
164
+ if (current == null || dispatched.defaultPrevented) {
172
165
  cancelResize(current);
173
166
  return;
174
167
  }
@@ -2,7 +2,7 @@ import { attributable } from "../internal/attributable.mjs";
2
2
  import { OuiDraggable, addDraggable, getDraggableStates, removeDraggable } from "../internal/draggable.mjs";
3
3
  import { getAttribute, setAttribute, setAttributes } from "@oscarpalmer/toretto/attribute";
4
4
  import { createElement } from "@oscarpalmer/toretto/create";
5
- import { getPosition } from "@oscarpalmer/toretto/event";
5
+ import { dispatch, getPosition } from "@oscarpalmer/toretto/event";
6
6
  import { findAncestor, getElementFromPosition } from "@oscarpalmer/toretto/find";
7
7
  import { setStyle } from "@oscarpalmer/toretto/style";
8
8
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
@@ -35,7 +35,7 @@ function afterEnd(insert, from, events, elements, position, valid) {
35
35
  elements.origin?.removeAttribute(ATTRIBUTE_ORIGIN);
36
36
  from.state.origin = void 0;
37
37
  resetPlaceholders();
38
- if (!valid || events.some((event) => event.defaultPrevented)) return;
38
+ if (!valid || events.some((event) => event?.defaultPrevented)) return;
39
39
  if (insert !== "insert") {
40
40
  elements.target.append(elements.origin);
41
41
  return;
@@ -130,12 +130,11 @@ function onEnd(event, globals, item, reset) {
130
130
  bubbles: false,
131
131
  cancelable: true
132
132
  };
133
- const fromEvent = new CustomEvent("sortable:end", options);
134
- const toEvent = new CustomEvent("sortable:end", options);
135
- item.instance.element.dispatchEvent(fromEvent);
136
- if (item.instance !== toInstance) toInstance?.element.dispatchEvent(toEvent);
133
+ const dispatchedFrom = dispatch(item.instance.element, "sortable:end", options);
134
+ let dispatchedTo;
135
+ if (item.instance !== toInstance && toInstance != null) dispatchedTo = dispatch(toInstance.element, "sortable:end", options);
137
136
  setTimeout(() => {
138
- afterEnd(insert, item, [fromEvent, toEvent], {
137
+ afterEnd(insert, item, [dispatchedFrom, dispatchedTo], {
139
138
  origin,
140
139
  target: element
141
140
  }, position, valid);
@@ -193,27 +192,23 @@ function setPlaceholder(state, item, origin) {
193
192
  const element = getDragPlaceholder();
194
193
  placeholder.drag.default.innerHTML = "";
195
194
  updatePlaceholder(item, element);
196
- const event = new CustomEvent("sortable:placeholder", {
197
- cancelable: true,
198
- detail: {
199
- origin,
200
- parent: item.state.element,
201
- placeholder: element,
202
- create: (value) => {
203
- if (!isHTMLOrSVGElement(value)) return;
204
- const element = value;
205
- placeholder.drag.custom = element;
206
- updatePlaceholder(item, element);
207
- state.element = {
208
- rectangle: element.getBoundingClientRect(),
209
- node: element
210
- };
211
- }
195
+ const dispatched = dispatch(item.instance.element, "sortable:placeholder", { detail: {
196
+ origin,
197
+ parent: item.state.element,
198
+ placeholder: element,
199
+ create: (value) => {
200
+ if (!isHTMLOrSVGElement(value)) return;
201
+ const element = value;
202
+ placeholder.drag.custom = element;
203
+ updatePlaceholder(item, element);
204
+ state.element = {
205
+ rectangle: element.getBoundingClientRect(),
206
+ node: element
207
+ };
212
208
  }
213
- });
214
- item.instance.element.dispatchEvent(event);
209
+ } });
215
210
  setTimeout(() => {
216
- if (!event.defaultPrevented && placeholder.drag.custom == null) placeholder.drag.default?.append(element.cloneNode(true));
211
+ if (!dispatched.defaultPrevented && placeholder.drag.custom == null) placeholder.drag.default?.append(element.cloneNode(true));
217
212
  document.body.append(placeholder.drag.custom ?? placeholder.drag.default ?? "");
218
213
  });
219
214
  }
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/oui",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Styles, components, and utilities for building websites and applications.",
5
5
  "keywords": [
6
6
  "css",
@@ -95,10 +95,10 @@
95
95
  },
96
96
  "dependencies": {
97
97
  "@oscarpalmer/atoms": "^0.188.1",
98
- "@oscarpalmer/toretto": "^0.47.3"
98
+ "@oscarpalmer/toretto": "^0.48.0"
99
99
  },
100
100
  "devDependencies": {
101
- "@oxlint/plugins": "^1.74",
101
+ "@oxlint/plugins": "^1.75",
102
102
  "@vitest/coverage-istanbul": "^4.1",
103
103
  "autoprefixer": "^10.5",
104
104
  "concurrently": "^10",
@@ -106,7 +106,7 @@
106
106
  "postcss-cli": "^11",
107
107
  "postcss-input-range": "^6",
108
108
  "postcss-scss": "^4",
109
- "sass": "^1.101",
109
+ "sass": "^1.102",
110
110
  "stylelint": "^17.14",
111
111
  "stylelint-config-standard-scss": "^17",
112
112
  "tsdown": "^0.22",
@@ -4,24 +4,7 @@
4
4
 
5
5
  .oui {
6
6
  :where(&-pill, &-tag) {
7
- --oui-shadow-mixed: color-mix(
8
- in oklab,
9
- var(--oui-shadow, var(--oui-gray-6)),
10
- transparent 92.5%
11
- );
12
-
13
- @include mixins.shadow(var(--oui-shadow-mixed));
14
-
15
- display: inline-flex;
16
- flex-flow: row nowrap;
17
- gap: 0.5em;
18
- background-color: color-mix(
19
- in oklab,
20
- var(--oui-background, var(--oui-gray-0)),
21
- transparent 25%
22
- );
23
- color: var(--oui-foreground, var(--oui-gray-8));
24
- line-height: 1.25;
7
+ @include mixins.badge;
25
8
  }
26
9
 
27
10
  :where(&-pill) {
@@ -2,7 +2,52 @@
2
2
 
3
3
  @use './mixins.scss' as mixins;
4
4
 
5
+ [oui-combobox-anchor] {
6
+ :where(&) {
7
+ @include mixins.input;
8
+
9
+ --oui-background-value: var(--oui-absolute);
10
+
11
+ padding: 0.5em 2em 0.5em 0.75em;
12
+ border-radius: 0.25em;
13
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='4'%3E%3Cpath d='M4 0h6L7 4'/%3E%3C/svg%3E");
14
+ background-position: right 0.5em center;
15
+ background-repeat: no-repeat;
16
+ background-size: 1em;
17
+ }
18
+
19
+ :where(&:not([disabled], [readonly], [aria-disabled='true' i]):focus),
20
+ :where(
21
+ &:not([disabled], [readonly], [aria-disabled='true' i]):has(
22
+ + [oui-combobox-content]:focus-within
23
+ )
24
+ ) {
25
+ @include mixins.input-focus;
26
+ }
27
+
28
+ @media (hover: hover) {
29
+ :where(&:not([disabled], [readonly], [aria-disabled='true' i]):hover) {
30
+ @include mixins.input-hover;
31
+ }
32
+ }
33
+
34
+ :where(&:is([disabled], [readonly], [aria-disabled='true' i])) {
35
+ --oui-foreground-percentage: 25%;
36
+
37
+ font-style: italic;
38
+ }
39
+
40
+ :where(&:is([disabled], [readonly], [aria-disabled='true' i]):focus) {
41
+ --oui-outline-percentage: 75%;
42
+ }
43
+
44
+ :where(&:is([disabled], [aria-disabled='true' i])) {
45
+ --oui-background-percentage: 50%;
46
+ }
47
+ }
48
+
5
49
  :where([oui-combobox-content]) {
50
+ @include mixins.floatable;
6
51
  @include mixins.shadow-overlay;
7
52
 
8
53
  width: fit-content;
@@ -11,16 +56,161 @@
11
56
  max-height: max(25vh, 32rem);
12
57
  min-width: var(--oui-combobox-width, auto);
13
58
  flex-flow: column nowrap;
59
+ border-radius: 0.25em;
14
60
 
15
61
  &:popover-open {
16
62
  display: flex !important;
17
63
  }
18
64
  }
19
65
 
66
+ :where([oui-combobox-empty]) {
67
+ padding: 1em;
68
+ text-align: center;
69
+ font-style: italic;
70
+ }
71
+
72
+ :where([oui-combobox-filter], [oui-combobox-header]) {
73
+ &::after {
74
+ content: '';
75
+ width: 100%;
76
+ height: 0.25em;
77
+ background-image: linear-gradient(to bottom, rgb(0 0 0 / 12.5%), transparent);
78
+ position: absolute;
79
+ bottom: -0.25em;
80
+ left: 0;
81
+ }
82
+ }
83
+
84
+ :where([oui-combobox-filter]) {
85
+ padding: 0.75em;
86
+ background-color: var(--oui-yellow-0);
87
+ border-radius: 0.25em 0.25em 0 0;
88
+ position: relative;
89
+ z-index: 1;
90
+ }
91
+
92
+ :where([oui-combobox-filter-input]) {
93
+ @include mixins.input;
94
+
95
+ --oui-background-value: var(--oui-absolute);
96
+ --oui-border-value: var(--oui-yellow-3);
97
+
98
+ width: 100%;
99
+ padding: 0.5em 0.75em;
100
+ outline-width: 0;
101
+ border-radius: 0.25em;
102
+ font-size: 0.875rem;
103
+
104
+ :where(&:not([disabled], [readonly], [aria-disabled='true' i]):focus) {
105
+ @include mixins.input-focus;
106
+ }
107
+
108
+ @media (hover: hover) {
109
+ :where(&:not([disabled], [readonly], [aria-disabled='true' i]):hover) {
110
+ @include mixins.input-hover;
111
+ }
112
+ }
113
+
114
+ :where(&:is([disabled], [readonly], [aria-disabled='true' i])) {
115
+ --oui-foreground-percentage: 25%;
116
+
117
+ font-style: italic;
118
+ }
119
+
120
+ :where(&:is([disabled], [readonly], [aria-disabled='true' i]):focus) {
121
+ --oui-outline-percentage: 75%;
122
+ }
123
+
124
+ :where(&:is([disabled], [aria-disabled='true' i])) {
125
+ --oui-background-percentage: 50%;
126
+ }
127
+ }
128
+
129
+ :where([oui-combobox-filter-label]) {
130
+ @include mixins.visually-hidden;
131
+
132
+ display: inline-flex;
133
+ position: absolute;
134
+ }
135
+
136
+ :where([oui-combobox-header]) {
137
+ padding: 0.5em calc(0.75em / 0.75);
138
+ background-color: var(--oui-gray-0);
139
+ font-size: 0.75em;
140
+ font-weight: bold;
141
+ position: sticky;
142
+ top: 0;
143
+ z-index: 1;
144
+ }
145
+
20
146
  :where([oui-combobox-list]) {
21
147
  overflow: auto;
148
+ position: relative;
149
+ }
150
+
151
+ [oui-combobox-option] {
152
+ :where(&) {
153
+ padding: 0.5em 0.75em;
154
+ outline: 4px solid transparent;
155
+ outline-offset: -4px;
156
+ line-height: 1.25;
157
+ cursor: pointer;
158
+ transition: 0.125s ease-in-out;
159
+ }
160
+
161
+ :where(&[aria-selected='true' i]) {
162
+ background-color: var(--oui-blue-3);
163
+ color: var(--oui-blue-9);
164
+ }
165
+
166
+ :where(oui-combobox[multiple] &[aria-selected='true' i])::before {
167
+ content: '✓';
168
+ margin: 0 0.75em 0 0;
169
+ }
170
+
171
+ :where(oui-combobox:not([disabled], [readonly], [aria-disabled='true' i]) &) {
172
+ @media (hover: hover) {
173
+ :where(&:hover) {
174
+ background-color: var(--oui-blue-1);
175
+ color: var(--oui-blue-8);
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ :where([oui-combobox-option-active]) {
182
+ background-color: var(--oui-blue-1);
183
+ outline-color: var(--oui-blue-4);
184
+ color: var(--oui-blue-8);
185
+ }
186
+
187
+ :where([oui-combobox-selection]) {
188
+ max-height: 8rem;
189
+ display: flex;
190
+ padding: 0.5em;
191
+ margin: -0.5em -0.5em -0.5em -0.75em;
192
+ flex-flow: row wrap;
193
+ gap: 0.5em;
194
+ overflow: auto;
195
+ list-style: none;
22
196
  }
23
197
 
24
- :where([oui-combobox-option]) {
25
- cursor: pointer;
198
+ [oui-combobox-selection-item] {
199
+ :where(&) {
200
+ @include mixins.badge;
201
+
202
+ border-radius: 3em;
203
+ padding: 0.5em 1em;
204
+ gap: 0.75em;
205
+ font-size: 0.875em;
206
+ transition: 0.125s ease-in-out;
207
+ }
208
+
209
+ @media (hover: hover) {
210
+ :where(oui-combobox:not([disabled], [readonly], [aria-disabled='true' i]) &:hover) {
211
+ --oui-background: var(--oui-red-0);
212
+ --oui-foreground: var(--oui-red-8);
213
+ --oui-shadow: var(--oui-red-6);
214
+ }
215
+ }
26
216
  }