@livenetworks/ashlar 1.3.3 → 1.4.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.
Files changed (36) hide show
  1. package/js/ln-accordion/ln-accordion.js +1 -1
  2. package/js/ln-api-connector/ln-api-connector.js +1 -1
  3. package/js/ln-autoresize/ln-autoresize.js +1 -1
  4. package/js/ln-autosave/ln-autosave.js +1 -1
  5. package/js/ln-circular-progress/ln-circular-progress.js +1 -1
  6. package/js/ln-confirm/ln-confirm.js +1 -1
  7. package/js/ln-core/helpers.js +45 -0
  8. package/js/ln-core/index.js +1 -1
  9. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  10. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
  11. package/js/ln-data-store/ln-data-store.js +1 -1
  12. package/js/ln-data-table/ln-data-table.js +1 -1
  13. package/js/ln-date/README.md +11 -1
  14. package/js/ln-date/ln-date.js +1 -1
  15. package/js/ln-date/src/ln-date.js +104 -1
  16. package/js/ln-dropdown/ln-dropdown.js +1 -1
  17. package/js/ln-filter/ln-filter.js +1 -1
  18. package/js/ln-form/ln-form.js +1 -1
  19. package/js/ln-modal/ln-modal.js +1 -1
  20. package/js/ln-number/ln-number.js +1 -1
  21. package/js/ln-number/src/ln-number.js +50 -2
  22. package/js/ln-popover/ln-popover.js +1 -1
  23. package/js/ln-search/ln-search.js +1 -1
  24. package/js/ln-sortable/ln-sortable.js +1 -1
  25. package/js/ln-table/ln-table.js +1 -1
  26. package/js/ln-tabs/ln-tabs.js +1 -1
  27. package/js/ln-time/ln-time.js +1 -1
  28. package/js/ln-toggle/ln-toggle.js +1 -1
  29. package/js/ln-tooltip/ln-tooltip.js +1 -1
  30. package/js/ln-translations/ln-translations.js +1 -1
  31. package/js/ln-validate/ln-validate.js +1 -1
  32. package/package.json +55 -55
  33. package/scss/components/_date.scss +21 -0
  34. package/scss/config/mixins/_form.scss +101 -54
  35. package/scss/config/mixins/_layout.scss +7 -9
  36. package/scss/ln-ashlar.scss +1 -0
@@ -25,9 +25,12 @@
25
25
  --margin-block: var(--size-xs);
26
26
  margin-bottom: var(--margin-block);
27
27
 
28
- // Required indicator — red * after label text
29
- // Triggered by sibling input[required] via parent :has()
30
- .form-element:has([required]) > & {
28
+ // Required indicator — red * after label text.
29
+ // Supports standard wrapping classes/tags (.form-element, .form-group, label)
30
+ // and general siblings preceding a required input.
31
+ :is(.form-element, .form-group, .form-field):has([required]) > &,
32
+ label:has([required]) &,
33
+ &:has(~ :is(input, select, textarea)[required]) {
31
34
  &::after {
32
35
  content: ' *';
33
36
  color: hsl(var(--color-error));
@@ -141,82 +144,126 @@
141
144
  padding-right: 2.5rem;
142
145
  }
143
146
 
144
- // ─── Input group — label wrapping icon(s) + input ──────────────────
147
+ // ─── Field group — generic bordered flex shell ────────────────────
145
148
  //
146
- // Pattern:
147
- // <label>
148
- // <svg class="ln-icon"/> ← leading icon (optional)
149
- // <input type="…">
150
- // <svg class="ln-icon"/> ← trailing icon (optional)
151
- // </label>
149
+ // One mixin for any control that is a bordered shell wrapping a row of
150
+ // children: a leading/trailing icon, the input (or select), and/or an
151
+ // interactive button (search submit, date-picker trigger).
152
152
  //
153
- // The <label> becomes the visual container (border, padding, focus
154
- // ring). The nested <input> strips its form-input container chrome so
155
- // that horizontal rhythm is owned by the label's px + gap, and vertical
156
- // rhythm by the label's py. Without this, the input keeps its own
157
- // padding and fights the wrapper — see the asymmetric spacing that
158
- // motivated this mixin.
153
+ // <label> <span data-ln-date-field>
154
+ // <svg class="ln-icon"/> <input type="text" …>
155
+ // <input …> <button>…</button>
156
+ // </label> </span>
159
157
  //
160
- // Applied via semantic selector in components/_form.scss no class
161
- // needed on the <label>.
158
+ // The wrapper is the VISUAL SHELL only: form-input supplies border, bg,
159
+ // radius, transition; the mixin adds flex layout and zeroes the shell's
160
+ // own padding. Each child owns its padding. Children strip only the chrome
161
+ // the shell now owns (border, bg, box-shadow). Focus is handled once on the
162
+ // shell via :focus-within; children suppress their own focus ring.
163
+ //
164
+ // Applied via semantic selectors (no class on the wrapper):
165
+ // - components/_form.scss → label:has(.ln-icon):has(text input)
166
+ // - components/_date.scss → [data-ln-date-field]
162
167
 
163
- @mixin form-input-icon-group {
164
- @include inline-flex;
168
+ @mixin form-field-group {
169
+ @include form-input; // visual shell: border, bg, radius, transition, outline
170
+ @include inline-flex; // override form-input's block display
165
171
  @include items-center;
166
- gap: var(--gap);
167
- // Compact padding: tight vertical to keep total height close to a
168
- // regular form-input despite the icon's fixed size.
169
- --padding-y: var(--size-xs);
170
- --padding-x: var(--size-sm);
171
- padding: var(--padding-y) var(--padding-x);
172
- background: var(--color-bg);
173
- @include border;
174
- border-radius: var(--radius);
175
- @include transition;
176
- font-size: var(--font-size);
177
- line-height: var(--line-height);
178
- // Wrapping-label reset: undo form-label defaults that would otherwise leak.
179
- margin-bottom: 0;
172
+ position: relative; // anchors any absolute-positioned children (e.g. native pickers)
173
+ overflow: hidden; // clips child corners to the shell's border-radius automatically
174
+ width: auto; // cancel form-input's w-full
175
+ padding: 0; // shell owns NO padding; children manage their own
176
+ margin-bottom: 0; // defensive: wrapping <label> carries form-label margin
180
177
 
181
178
  &:focus-within {
182
179
  @include _form-focus-style;
180
+
181
+ > input,
182
+ > select {
183
+ box-shadow: none; // suppress child's own form-input focus ring
184
+ }
185
+ }
186
+
187
+ // Propagate validation states to the parent visual shell
188
+ &:has(> .ln-validate-invalid) {
189
+ @include form-validate-invalid;
190
+ }
191
+ &:has(> .ln-validate-valid) {
192
+ @include form-validate-valid;
193
+ }
194
+
195
+ // Propagate disabled states to the parent visual shell
196
+ &:has(> input:disabled),
197
+ &:has(> select:disabled),
198
+ &:has(> button:disabled) {
199
+ @include opacity-50;
200
+ @include cursor-not-allowed;
201
+ --color-bg: var(--bg-sunken);
202
+ background: var(--color-bg);
203
+
204
+ * {
205
+ @include cursor-not-allowed;
206
+ }
183
207
  }
184
208
 
185
209
  > .ln-icon {
186
210
  @include flex-shrink-0;
211
+ padding: var(--size-xs) var(--size-sm); // icon provides its own row spacing
187
212
  --color-fg: var(--fg-subtle);
188
213
  color: var(--color-fg);
189
214
  }
190
215
 
191
- > input {
192
- // Strip container chrome inherited from form-input — label owns it now.
193
- padding: 0;
216
+ > input,
217
+ > select {
218
+ // form-input is already applied globally — strip only shell-owned chrome.
194
219
  @include border-none;
195
220
  background: transparent;
196
- outline: none;
197
221
  box-shadow: none;
198
-
199
- // Tighten leading for a single-line control. form-input inherits
200
- // --lh-body-md (1.6) which is tuned for prose readability, not form
201
- // controls — that leaves ~7px of wasted leading above and below the
202
- // glyph inside the input's own line box, which compounds with the
203
- // label's padding and makes icon-group feel airy. 1.25 keeps
204
- // descenders (g/p/j/y) safe and brings the label down to ~36px total.
205
- line-height: 1.25;
206
-
207
- // Take the remaining flex space. width: auto cancels form-input's
208
- // width: 100%; flex-1 + min-width: 0 lets the field shrink below
209
- // its intrinsic min-content inside a narrow label.
210
222
  width: auto;
211
223
  @include flex-1;
212
224
  min-width: 0;
213
225
 
214
- &:focus-visible {
215
- border-color: transparent;
216
- box-shadow: none;
226
+ // Tighten inner padding when adjacent to icons or buttons inside the group
227
+ &:not(:first-child) {
228
+ padding-left: var(--size-xs);
217
229
  }
230
+ &:not(:last-child) {
231
+ padding-right: var(--size-xs);
232
+ }
233
+ }
218
234
 
219
- // Hide clear button when input is empty
235
+ > button {
236
+ @include button-base;
237
+ @include flex-shrink-0;
238
+ @include border-none;
239
+ align-self: stretch;
240
+ display: inline-flex;
241
+ align-items: center;
242
+ justify-content: center;
243
+ padding-top: 0;
244
+ padding-bottom: 0;
245
+ // Tighter horizontal padding for icon-only button inside groups to keep it compact and squarish
246
+ padding-left: var(--size-sm);
247
+ padding-right: var(--size-sm);
248
+ border-radius: 0; // Square the button to sit flush with the input border
249
+
250
+ > .ln-icon {
251
+ --color-fg: var(--fg-subtle);
252
+ color: var(--color-fg);
253
+ }
254
+ }
255
+ }
256
+
257
+ // ─── Icon input group — search/icon field (thin wrapper) ──────────
258
+ //
259
+ // form-field-group + the search-clear control, which is the only piece
260
+ // unique to this binding.
261
+
262
+ @mixin form-input-icon-group {
263
+ @include form-field-group;
264
+
265
+ > input {
266
+ // Hide the clear button while the field is empty.
220
267
  &:placeholder-shown ~ [data-ln-search-clear] {
221
268
  @include hidden;
222
269
  }
@@ -4,11 +4,15 @@
4
4
 
5
5
  // Layout — grid, stack, container
6
6
 
7
- @mixin grid {
7
+ @mixin _grid-base {
8
8
  display: grid;
9
9
  --gap: var(--size-lg);
10
10
  gap: var(--gap);
11
11
  grid-template-columns: repeat(1, minmax(0, 1fr));
12
+ }
13
+
14
+ @mixin grid {
15
+ @include _grid-base;
12
16
 
13
17
  @include mq-up(md) {
14
18
  grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -20,10 +24,7 @@
20
24
  }
21
25
 
22
26
  @mixin grid-2 {
23
- display: grid;
24
- --gap: var(--size-lg);
25
- gap: var(--gap);
26
- grid-template-columns: repeat(1, minmax(0, 1fr));
27
+ @include _grid-base;
27
28
 
28
29
  @include mq-up(md) {
29
30
  grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -31,10 +32,7 @@
31
32
  }
32
33
 
33
34
  @mixin grid-4 {
34
- display: grid;
35
- --gap: var(--size-lg);
36
- gap: var(--gap);
37
- grid-template-columns: repeat(1, minmax(0, 1fr));
35
+ @include _grid-base;
38
36
 
39
37
  @include mq-up(md) {
40
38
  grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -10,6 +10,7 @@
10
10
 
11
11
  @use 'components/button';
12
12
  @use 'components/form';
13
+ @use 'components/date';
13
14
  @use 'components/layout';
14
15
  @use 'components/table';
15
16
  @use 'components/tabs';