File: form-layout.scss

package info (click to toggle)
cockpit 239-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 67,268 kB
  • sloc: javascript: 245,474; ansic: 72,273; python: 23,634; xml: 6,155; sh: 2,919; makefile: 923; sed: 5
file content (391 lines) | stat: -rw-r--r-- 10,646 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/* Form layout */

@import "./variables.scss";

:root {
  // CSS variable to define the number of (label + control) columns.
  // It dynamically changes on narrow pages (see media query below).
  --ct-form-columns: 2;
}

// Cockpit Form Layout: Automatically have Cockpit display your form in
// an optimal layout.
//
// By default, all labels are aligned and sized properly and form elements
// stretch to take up the remaining space.
//
//
// There are additional classes and attributes you can add to each
// control directly under `ct-form`:
//
// `ct-form-relax`: Form elements normally stretch to take up the
// full space. You can relax their width by adding this class to the
// control. Inputs with a size attribute are auto-relaxed and do not
// need this class.
//
// `ct-form-stretch`: If a control has a width specified
// elsewhere, you can force it to stretch. This is mainly useful when
// using <div role="group"> to group elements.
//
// `ct-form-full`: Force a widget to be the full width of the form,
// invading the label space.
//
// role="group": When there are two related elements, such as a text
// input and a dropdown, you can group them together using this HTML
// attribute. It's similar in purpose to a <fieldset>, but works for
// layouts in Chrome (unlike fieldset). This can be attached to any
// container element, but will most likely be used with <div>. The role
// adds semantic meaning to the element for screen readers, and we key
// the CSS off of the role.
//
// `ct-form-box`: Visual styling for encapsulating a block of sub-options.
// Creates a gray box around elements.
//
// <hr>: While this is an element, it has a special meaning and is used
// to add some vertical spacing to a form.
//
//
// Alternate grid sizing:
// You can override division of space for controls by adding a class
// at grid level (.ct-form) to adjust size for "split" widgets:
// `ct-form-maxmin: First widget is wide; second is small.
// `ct-form-minmax`: First widget is small; second is wide.
//
//
// Most of the time, you can simply ignore all the optional classes (and
// attribute and hr element) and simply wrap your labels & controls in
// a <form class="ct-form"> and layout magic happens.

.ct-form {
  // Locally redefine padding to Bootstrap values for this SASS block
  --padding-y: var(--pf-global--spacer--md);
  --padding-x: var(--pf-global--spacer--lg);
  // Bootstrap & PatternFly use a 1px border around widgets
  --border-width: 1px;
  --widget-height: 2.25rem; // (36px for PF4 widgets)

  align-self: start; // Don't vertically fill content by default
  display: grid;
  grid-gap: var(--padding-y) var(--padding-x);
  // Repeat a label that is a minimum of 4em and its control that
  // fills the remaining space by a CSS variable (default: 2)
  grid-template-columns: repeat(var(--ct-form-columns), max-content 1fr);
  justify-items: stretch;
  align-content: baseline;

  // All <label> elements describing form elements in PatternFly are
  // supposed to have a `control-label` class (PF3) or `pf-c-form__label` (PF4).
  // These precede control elements.
  > .control-label,
  > .pf-c-form__label {
    padding: 0;
    margin: 0;
    text-align: left;
    font-weight: var(--pf-global--FontWeight--bold);
    font-size: var(--pf-global--FontSize--sm);
  }

  > :not(hr):not(p):not(.pf-c-form-control) {
    line-height: var(--widget-height);
  }

  > p {
    margin: 0;
  }

  // Put all control elements to the right of the labels,
  // stretching to the rightmost column
  > :not(.control-label):not(.pf-c-form__label):not(hr):not(.ct-form-full) {
    grid-column: 2 / -1;
  }

  // Auto-stretch elements to the grid (except when relaxed)
  > :not(.ct-form-relax):not(.spinner) {
    width: auto;
  }

  // Horizontal rules directly under a form-layout container serve to
  // add some vertical space in forms. This is useful for visually
  // grouping similar elements with whitespace.
  //
  // It's not the same as actually grouping elements (which can be done
  // in the usual ways as well as adding a role="group".
  > hr {
    border: none;
    grid-column: 1 / -1;
    height: 0;
    // Reset padding to ensure all browsers treat this the same
    margin: 0;
    padding: 0;
  }

  // Auto-relax inputs with size
  > input[size],
  > .ct-validation-wrapper > input[size] {
    justify-self: start;
  }

  > .ct-validation-wrapper {
    display: flex;
    flex-direction: column;
  }

  // Hack to allow number inputs to be sized on WebKit-based browsers
  input[type=number] {
    -webkit-appearance: textarea;
  }

  // Special considerations for widgets (and widget-like elements)
  // This is a SASS mixin that will not be in the compiled CSS.
  @mixin widget-rules() {
    > input,
    > textarea,
    > select,
    > .bootstrap-select,
    > .ct-select,
    > .dropdown,
    > .combobox-container,
    > fieldset,
    > [role=group],
    > [data-field],
    > .form-group,
    > .btn-group,
    > .pf-c-form__group-control,
    > label.checkbox,
    > label.radio,
    > label.pf-c-radio,
    > label.pf-c-check,
    > .checkbox-inline,
    > .radio-inline {
      line-height: var(--pf-global--LineHeight--md);
    }
  }

  &, > .ct-validation-wrapper {
    @include widget-rules();
  }

  // Some elements need special width considerations
  // as PatternFly normally fixes the width
  > :not(.ct-form-relax):not(.spinner) {
    width: auto !important;
  }

  // Elements with role="group" are used to group elements —
  // fieldset was going to be used, but Chrome doesn't allow
  // grid or flex placement for fieldsets (yet).
  //
  // Adding a group role is the same thing accessibilty-wise
  // and lets us target all browsers properly.
  //
  // You can use this like:
  // <div role="group">
  //
  // And non-div elements are also supported.
  > [role=group],
  > .ct-validation-wrapper > [role=group] {
    align-self: start;
    align-content: center;
    display: grid;
    grid-gap: var(--padding-y);
    min-height: var(--widget-height);
    justify-content: start;
    // Only support 2 splits for now (can change to 3 later, if needed)
    grid-template-columns: repeat(2, auto);

    > .checkbox,
    > .radio {
        // Spacing is handled by grid, not margin
        margin: 0;

        &:first-child {
          margin-top: 0.5rem;
        }
    }
  }

  > [role=group],
  > .ct-validation-wrapper > [role=group],
  > .ct-validation-wrapper > [data-field] {
    // Allow dropdowns to expand as needed
    &:not(.ct-form-relax) {
      > .dropdown {
        width: auto !important;
      }

      // <select>s need to be coaxed to be 100%
      > .ct-select {
        width: 100%;
      }
    }
  }

  // Vertically align checkboxes and radios properly using flex
  label.checkbox,
  label.radio,
  .checkbox > label,
  .radio > label,
  .checkbox-inline,
  .radio-inline {
    display: inline-flex;
    padding-left: 0;
    padding-right: var(--padding-x);
    align-items: center;

    > input[type="checkbox"],
    > input[type="radio"] {
      margin: 0 0.5em 0 0;
      position: static;
    }
  }

  // Remove vertical spacing for fieldsets,
  // as this is handled by the grid gap
  fieldset {
    > .checkbox,
    > .radio {
      &:first-child {
        margin-top: 0;
      }

      &:last-child {
        margin-bottom: 0;
      }
    }
  }

  // List groups override the grid gap, so we're adding it manually
  .list-group {
    margin-bottom: var(--padding-y);
  }

  // Relax split elements to only take up one column
  // Stretch to full width
  > .ct-form-full {
    grid-column: 1 / -1;
  }

  // Move warnings, errors, info, etc. up a bit to associate with previous field
  > .help-block {
    position: relative;
    margin-top: -0.5rem;
  }

  .help-block {
    --help-line-height: calc(var(--pf-global--LineHeight--md) * 1rem);
    line-height: var(--help-line-height);

    &:empty {
      display: none;
    }

    .spinner {
      position: relative;
      // (baseline - height - border) / 2
      top: calc((var(--help-line-height) - 16px - 2px) / 2);
    }
  }

  .ct-form-box {
    background: var(--pf-global--palette--black-200);
    border-width: 1px;
    border-style: solid;
    border-color: var(--pf-global--palette--black-400);
    padding: 0.5rem 1rem;
    width: 100%;
  }
}

// Force a form element to stretch. Add as a class to `form-control`.
.ct-form-stretch {
  justify-content: stretch !important;
}

// Instruct a `form-control` to not stretch.
.ct-form-relax {
  justify-self: start;
}

@mixin deconstruct() {
  // Only use one column
  --ct-form-columns: 1;
  // Don't set the line height for widgets;
  // setting this to an invalid CSS rule invalidates it, so it falls back
  --widget-height: not-needed;
  // Completely deconstruct the grid layout
  grid-template-columns: initial;

  > * {
    // Don't restrict grid placement
    grid-column: auto;
    max-width: 100%;
  }

  // As control labels fill the row, left align and remove padding
  > .control-label,
  > .pf-c-form__label {
    margin: 0 0 -.5rem;
    padding: 1rem 0 0;
    text-align: left;
  }
}

@media (max-width: 640px) {
  // When inside of lists or modals & the page isn't wide enough,
  // collapse (label + control) columns down to 1, to force splits on
  // their own lines
  .listing-ct-body,
  .modal {
    .ct-form {
      @include deconstruct();
    }
  }
}

// Alternate layout, for a split, used at ct-form grid-level:
// First form widget is as small as possible;
// Second takes up the rest of the space
.ct-form-minmax {
  grid-template-columns: max-content min-content max-content 1fr;
}

// Alternate layout, for a split, used at ct-form grid-level:
// First form widget takes up as much space as it can;
// Second form widget is as small as possible
.ct-form-maxmin {
  grid-template-columns: max-content 1fr max-content min-content;
}

// Alternate to reduce spacing, used at ct-form grid-level
.ct-form-compact {
  --padding-y: var(--pf-global--spacer--xs);
}

// Compact form used for info tables
.ct-form-info {
  @extend .ct-form-compact;

  // Invalidate widget height, to fall back to normal line height
  --widget-height: invalidate-this;

  > .control-label,
  > .pf-c-form__label {
    font-weight: normal;
  }
}

@media (max-width: $screen-xs) {
  // When inside of lists or modals & the page is *very* narrow,
  // collapse the grid further, so labels are above controls
  //
  // Note: Padding variables below are outside the local scope of the
  // .ct-form block, so they default to the global PatternFly
  // values.

  .listing-ct-body,
  .modal {
    .ct-form {
      @include deconstruct();
    }
  }
}