@threadlabs/looma 0.25.0 → 0.25.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.
@@ -1,5 +1,6 @@
1
1
  <template component="ui-input-group" controller="./ui-input-group.js">
2
2
  <defs>
3
+ <prop name="multiline" type="boolean" default="false">Groups a Textarea with an action below its text inside the shared frame.</prop>
3
4
  <prop name="prefixKind" type="keyword" values="text, icon" default="text"
4
5
  >Text affixes have their own framing. An icon prefix shares the field surface and uses one compact icon-to-value gap.</prop>
5
6
  <prop name="disabled" type="boolean" default="false">Fades the group's framing to match a disabled input inside it. Disable the input itself too.</prop>
@@ -46,6 +47,17 @@
46
47
  --_ui-default-input-focus-outline: none;
47
48
  --_ui-default-input-radius: 0;
48
49
  --_ui-default-input-surface: transparent;
50
+ --_ui-default-textarea-border-width: 0;
51
+ --_ui-default-textarea-border: transparent;
52
+ --_ui-default-textarea-border-hover: transparent;
53
+ --_ui-default-textarea-focus-border: transparent;
54
+ --_ui-default-textarea-invalid-border: transparent;
55
+ --_ui-default-textarea-invalid-focus-shadow: none;
56
+ --_ui-default-textarea-shadow: none;
57
+ --_ui-default-textarea-focus-shadow: none;
58
+ --_ui-default-textarea-focus-outline: none;
59
+ --_ui-default-textarea-radius: 0;
60
+ --_ui-default-textarea-surface: transparent;
49
61
 
50
62
  display: flex;
51
63
  align-items: stretch;
@@ -79,13 +91,13 @@
79
91
  }
80
92
  }
81
93
 
82
- :host:hover:not(:has(input:focus, input:disabled, input:read-only, [aria-invalid="true"], :user-invalid)) {
94
+ :host:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :user-invalid)) {
83
95
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
84
96
  }
85
97
 
86
98
  /* The ring shows when the field's input would show its own: when it is focus-visible, which a
87
99
  text input is for a pointer as well as a keyboard. A focused action button draws its own. */
88
- :host:has(.field input:focus-visible) {
100
+ :host:has(.field :is(input, textarea):focus-visible) {
89
101
  border-color: var(--ui-control-focus, var(--ui-accent-solid));
90
102
  box-shadow:
91
103
  var(--ui-control-inset),
@@ -96,7 +108,7 @@
96
108
  border-color: var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
97
109
  }
98
110
 
99
- :host:has(input:is([aria-invalid="true"], :user-invalid):focus-visible) {
111
+ :host:has(:is(input, textarea):is([aria-invalid="true"], :user-invalid):focus-visible) {
100
112
  box-shadow: var(
101
113
  --ui-input-group-invalid-focus-shadow,
102
114
  var(--ui-control-inset),
@@ -110,14 +122,14 @@
110
122
 
111
123
  /* Forced colors drop box shadows, so the focus ring becomes an outline. */
112
124
  @media (forced-colors: active) {
113
- :host:has(input:focus-visible) {
125
+ :host:has(:is(input, textarea):focus-visible) {
114
126
  outline: var(--ui-focus-width) solid Highlight;
115
127
  outline-offset: var(--ui-border-width);
116
128
  }
117
129
  }
118
130
 
119
131
  :host([disabled]),
120
- :host:has(input:disabled) {
132
+ :host:has(:is(input, textarea):disabled) {
121
133
  background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
122
134
  box-shadow: none;
123
135
  cursor: not-allowed;
@@ -172,6 +184,23 @@
172
184
  padding-inline: var(--ui-space-1);
173
185
  }
174
186
 
187
+ /* Multiline text owns the first row; its action remains inside the same frame below it. */
188
+ :host([multiline]) {
189
+ display: grid;
190
+ grid-template-columns: auto minmax(0, 1fr) auto;
191
+ }
192
+
193
+ :host([multiline]) .field {
194
+ grid-column: 2;
195
+ }
196
+
197
+ :host([multiline]) .action {
198
+ grid-column: 1 / -1;
199
+ justify-content: end;
200
+ padding: var(--ui-space-2);
201
+ padding-block-start: 0;
202
+ }
203
+
175
204
  .affix:empty,
176
205
  .action:empty {
177
206
  display: none;
@@ -21,7 +21,7 @@ function connect(host) {
21
21
  let described = null;
22
22
  const warned = new Set();
23
23
  const wire = () => {
24
- const input = element.querySelector(".field input");
24
+ const input = element.querySelector(".field :is(input, textarea)");
25
25
  const affixes = Array.from(element.querySelectorAll(":scope > .affix")).filter((affix) => {
26
26
  const text = affix.textContent.trim() !== "" && !affix.querySelector(INTERACTIVE);
27
27
  if (text) affix.setAttribute("aria-hidden", "true");
@@ -50,7 +50,7 @@ function connect(host) {
50
50
  };
51
51
  // Click, not pointerdown: a tap's click is a user activation, so a touch keyboard opens.
52
52
  const onClick = (event) => {
53
- if (pressedAround(event) && !host.props.disabled.value) element.querySelector(".field input")?.focus();
53
+ if (pressedAround(event) && !host.props.disabled.value) element.querySelector(".field :is(input, textarea)")?.focus();
54
54
  };
55
55
  element.addEventListener("mousedown", onMousedown);
56
56
  element.addEventListener("click", onClick);
@@ -26,6 +26,30 @@
26
26
  ></textarea>
27
27
  <style>
28
28
  /* This component's hooks style the element they are set on, and never a nested one. */
29
+ @property --ui-textarea-border-width {
30
+ syntax: "*";
31
+ inherits: false;
32
+ }
33
+ @property --ui-textarea-border {
34
+ syntax: "*";
35
+ inherits: false;
36
+ }
37
+ @property --ui-textarea-border-hover {
38
+ syntax: "*";
39
+ inherits: false;
40
+ }
41
+ @property --ui-textarea-focus-border {
42
+ syntax: "*";
43
+ inherits: false;
44
+ }
45
+ @property --ui-textarea-focus-outline {
46
+ syntax: "*";
47
+ inherits: false;
48
+ }
49
+ @property --ui-textarea-surface {
50
+ syntax: "*";
51
+ inherits: false;
52
+ }
29
53
  @property --ui-textarea-focus-shadow {
30
54
  syntax: "*";
31
55
  inherits: false;
@@ -58,13 +82,14 @@
58
82
  padding: var(--ui-space-2) var(--ui-space-3);
59
83
  appearance: none;
60
84
  resize: vertical;
61
- border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
62
- border-radius: var(--ui-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)));
63
- background: var(--ui-control-surface, var(--ui-surface-elevated));
85
+ border: var(--ui-textarea-border-width, var(--_ui-default-textarea-border-width, var(--ui-border-width))) solid
86
+ var(--ui-textarea-border, var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong))));
87
+ border-radius: var(--ui-textarea-radius, var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md))));
88
+ background: var(--ui-textarea-surface, var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
64
89
  color: var(--ui-text-primary);
65
90
  font: inherit;
66
91
  line-height: var(--ui-line-height-md);
67
- box-shadow: var(--ui-textarea-shadow, var(--ui-control-inset));
92
+ box-shadow: var(--ui-textarea-shadow, var(--_ui-default-textarea-shadow, var(--ui-control-inset)));
68
93
  transition:
69
94
  border-color var(--ui-motion-fast) var(--ui-motion-ease),
70
95
  box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
@@ -83,29 +108,32 @@
83
108
  }
84
109
 
85
110
  :host:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:user-invalid) {
86
- border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
111
+ border-color: var(--ui-textarea-border-hover, var(--_ui-default-textarea-border-hover, var(--ui-control-border-hover, var(--ui-text-secondary))));
87
112
  }
88
113
 
89
114
  :host:focus-visible {
90
- border-color: var(--ui-control-focus, var(--ui-accent-solid));
91
- outline: none;
92
- box-shadow: var(--ui-textarea-focus-shadow, var(--ui-control-inset), var(--ui-control-focus-halo-shadow));
115
+ border-color: var(--ui-textarea-focus-border, var(--_ui-default-textarea-focus-border, var(--ui-control-focus, var(--ui-accent-solid))));
116
+ outline: var(--ui-textarea-focus-outline, var(--_ui-default-textarea-focus-outline, none));
117
+ box-shadow: var(--ui-textarea-focus-shadow, var(--_ui-default-textarea-focus-shadow, var(--ui-control-inset), var(--ui-control-focus-halo-shadow)));
93
118
  }
94
119
 
95
120
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
96
121
  :host[aria-invalid="true"],
97
122
  :host:user-invalid {
98
- border-color: var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
123
+ border-color: var(--ui-textarea-invalid-border, var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))));
99
124
  }
100
125
 
101
126
  :host:is([aria-invalid="true"], :user-invalid):focus-visible {
102
127
  box-shadow: var(
103
128
  --ui-textarea-invalid-focus-shadow,
104
- var(--ui-control-inset),
105
- 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
106
- in srgb,
107
- var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
108
- transparent
129
+ var(
130
+ --_ui-default-textarea-invalid-focus-shadow,
131
+ var(--ui-control-inset),
132
+ 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
133
+ in srgb,
134
+ var(--ui-textarea-invalid-border, var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))) 22%,
135
+ transparent
136
+ )
109
137
  )
110
138
  );
111
139
  }