05/04 Update form creation page
This commit is contained in:
@@ -36,28 +36,45 @@
|
|||||||
width: max-content;
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── iPad / touch-device fluid grid override ── */
|
/* ── iPad / touch-device fluid grid override ──
|
||||||
|
--fg-w is set by ResizeObserver in JS (see bottom of file),
|
||||||
|
which measures the actual .insp-body clientWidth — no vw/scrollbar ambiguity.
|
||||||
|
Falls back to a safe 100% - padding calculation if JS hasn't run yet. */
|
||||||
@media (max-width: 1194px) {
|
@media (max-width: 1194px) {
|
||||||
.form-grid {
|
.form-grid {
|
||||||
/* Tighter column gap (4px) so empty intermediate columns between fields
|
--fg-gap: 4px;
|
||||||
don't create a large visual gap — e.g. between rating stars and upload */
|
--fg-w: calc(100% - 2rem); /* JS overrides this with measured px value */
|
||||||
--_gap: 4px;
|
--fg-cell: calc((var(--fg-w) - 11 * var(--fg-gap)) / 12);
|
||||||
--_cell: calc((min(calc(100vw - 2rem), 900px) - 11 * 4px) / 12);
|
grid-template-columns: repeat(12, var(--fg-cell));
|
||||||
grid-template-columns: repeat(12, var(--_cell));
|
grid-auto-rows: minmax(calc(var(--fg-cell) * 0.72), auto);
|
||||||
grid-auto-rows: minmax(calc(var(--_cell) * 0.72), auto);
|
gap: var(--fg-gap);
|
||||||
gap: 4px var(--_gap);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.insp-body { padding: 1rem; }
|
.insp-body { padding: 1rem; }
|
||||||
|
|
||||||
/* Rating stars: scale down to fit compressed grid cells on iPad */
|
/* Rating stars: scale down to fit compressed grid cells on iPad */
|
||||||
.rating-stars button { font-size: .85rem; }
|
.rating-stars button { font-size: .9rem; min-height: 36px; min-width: 28px; }
|
||||||
|
|
||||||
|
/* Pass/fail pills: allow wrapping on narrow cells */
|
||||||
|
.pf-group { flex-wrap: wrap !important; gap: .4rem !important; }
|
||||||
|
.pf-btn { flex-shrink: 0; }
|
||||||
|
|
||||||
/* Image upload cells: don't clip the chosen-file row */
|
/* Image upload cells: don't clip the chosen-file row */
|
||||||
.upload-zone { overflow: visible; }
|
.upload-zone { overflow: visible; }
|
||||||
|
|
||||||
|
/* Touch: larger tap targets for checkboxes and radios */
|
||||||
|
.form-check-input { width: 1.1em; height: 1.1em; margin-top: .15rem; }
|
||||||
}
|
}
|
||||||
.fg-cell {
|
.fg-cell {
|
||||||
overflow:hidden; display:flex; flex-direction:column; padding:.18rem .45rem;
|
/* overflow:visible so content (dropdowns, tall labels) is never silently clipped.
|
||||||
|
align-content:start keeps items pinned to the top of the cell. */
|
||||||
|
overflow: visible;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: start;
|
||||||
|
padding: .18rem .45rem;
|
||||||
|
/* Min height ensures touch targets are reachable even on very short row spans */
|
||||||
|
min-height: 36px;
|
||||||
}
|
}
|
||||||
.fg-cell .field-lbl {
|
.fg-cell .field-lbl {
|
||||||
font-size:.74rem; font-weight:500; color:#64748b;
|
font-size:.74rem; font-weight:500; color:#64748b;
|
||||||
@@ -119,7 +136,27 @@
|
|||||||
}
|
}
|
||||||
.upload-clear:hover { background:#fecaca; }
|
.upload-clear:hover { background:#fecaca; }
|
||||||
|
|
||||||
.signature-pad-wrap {
|
/* ── Pass / Fail pills ── */
|
||||||
|
.pf-group { display:flex; gap:.5rem; flex-wrap:wrap; }
|
||||||
|
.pf-btn {
|
||||||
|
display: flex; align-items: center; gap: .35rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: .4rem .85rem; border-radius: 20px;
|
||||||
|
font-size: .84rem; font-weight: 600;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: background .15s, color .15s, border-color .15s;
|
||||||
|
user-select: none;
|
||||||
|
min-height: 36px; /* 44px touch target via padding — no layout shift */
|
||||||
|
}
|
||||||
|
.pf-btn input[type=radio] { display: none; }
|
||||||
|
/* Pass variants */
|
||||||
|
.pf-btn.pf-pass { border-color: #16a34a; color: #16a34a; background: transparent; }
|
||||||
|
.pf-btn.pf-pass.pf-on,
|
||||||
|
.pf-btn.pf-pass:has(input:checked) { background: #16a34a; color: #fff; }
|
||||||
|
/* Fail variants */
|
||||||
|
.pf-btn.pf-fail { border-color: #dc2626; color: #dc2626; background: transparent; }
|
||||||
|
.pf-btn.pf-fail.pf-on,
|
||||||
|
.pf-btn.pf-fail:has(input:checked) { background: #dc2626; color: #fff; }
|
||||||
flex:1; min-height:0; border-radius:6px; border:1px solid #e2e8f0;
|
flex:1; min-height:0; border-radius:6px; border:1px solid #e2e8f0;
|
||||||
background:#f8fafc; position:relative; overflow:hidden;
|
background:#f8fafc; position:relative; overflow:hidden;
|
||||||
}
|
}
|
||||||
@@ -334,20 +371,14 @@
|
|||||||
{% elif field.type == 'pass_fail' %}
|
{% elif field.type == 'pass_fail' %}
|
||||||
<label class="field-lbl">{{ field.label }}{% if field.required %}<span class="required-mark"> *</span>{% endif %}</label>
|
<label class="field-lbl">{{ field.label }}{% if field.required %}<span class="required-mark"> *</span>{% endif %}</label>
|
||||||
{% set pf_options = field.options if field.options else ['Pass', 'Fail'] %}
|
{% set pf_options = field.options if field.options else ['Pass', 'Fail'] %}
|
||||||
<div class="pf-group" style="display:flex;gap:.5rem;flex-wrap:wrap;">
|
<div class="pf-group">
|
||||||
{% for opt in pf_options %}
|
{% for opt in pf_options %}
|
||||||
{% set is_pass = opt.lower() in ('pass','yes','ok','good') %}
|
{% set is_pass = opt.lower() in ('pass','yes','ok','good','acceptable','compliant') %}
|
||||||
<label class="pf-btn{{ ' active' if saved == opt }}" data-color="{{ '#16a34a' if is_pass else '#dc2626' }}"
|
<label class="pf-btn {{ 'pf-pass' if is_pass else 'pf-fail' }}{{ ' pf-on' if saved == opt }}"
|
||||||
style="display:flex;align-items:center;gap:.35rem;cursor:pointer;
|
data-fid="{{ fid }}">
|
||||||
padding:.4rem .85rem;border-radius:20px;font-size:.84rem;font-weight:600;
|
|
||||||
border:2px solid {{ '#16a34a' if is_pass else '#dc2626' }};
|
|
||||||
color:{{ ('#fff' if saved == opt else ('#16a34a' if is_pass else '#dc2626')) }};
|
|
||||||
background:{{ (('#16a34a' if is_pass else '#dc2626') if saved == opt else 'transparent') }};
|
|
||||||
transition:background .15s,color .15s;">
|
|
||||||
<input type="radio" name="field_{{ fid }}" value="{{ opt }}"
|
<input type="radio" name="field_{{ fid }}" value="{{ opt }}"
|
||||||
{{ 'checked' if saved == opt }}
|
{{ 'checked' if saved == opt }}
|
||||||
style="display:none;"
|
onchange="pfSelect(this)">
|
||||||
onchange="var g=this.closest('.pf-group');g.querySelectorAll('.pf-btn').forEach(function(l){l.style.background='';l.style.color=l.dataset.color;});this.parentElement.style.background=this.parentElement.dataset.color;this.parentElement.style.color='#fff';">
|
|
||||||
<span>{{ opt }}</span>
|
<span>{{ opt }}</span>
|
||||||
</label>
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -474,6 +505,43 @@
|
|||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
<script>
|
<script>
|
||||||
|
// ── Pass / Fail pill toggle ───────────────────────────────────────────────────
|
||||||
|
// Adds/removes .pf-on class instead of inline styles — works with CSS :has() fallback
|
||||||
|
function pfSelect(radio) {
|
||||||
|
const group = radio.closest('.pf-group');
|
||||||
|
if (!group) return;
|
||||||
|
group.querySelectorAll('.pf-btn').forEach(function(btn) {
|
||||||
|
btn.classList.remove('pf-on');
|
||||||
|
});
|
||||||
|
if (radio.checked) {
|
||||||
|
radio.closest('.pf-btn').classList.add('pf-on');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fluid grid width — ResizeObserver sets --fg-w on .form-grid ──────────────
|
||||||
|
// Avoids 100vw (includes scrollbar) and 100% on .form-grid itself (reports 0
|
||||||
|
// before first paint). Measures .insp-body which is always reliably sized.
|
||||||
|
(function () {
|
||||||
|
function applyGridWidth() {
|
||||||
|
const body = document.querySelector('.insp-body');
|
||||||
|
const grid = document.querySelector('.form-grid');
|
||||||
|
if (!body || !grid) return;
|
||||||
|
// Available width = insp-body inner width minus its horizontal padding (1rem each side on iPad)
|
||||||
|
const style = getComputedStyle(body);
|
||||||
|
const padH = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
|
||||||
|
const available = body.clientWidth - padH;
|
||||||
|
grid.style.setProperty('--fg-w', available + 'px');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run immediately and on every resize
|
||||||
|
applyGridWidth();
|
||||||
|
if (typeof ResizeObserver !== 'undefined') {
|
||||||
|
new ResizeObserver(applyGridWidth).observe(document.querySelector('.insp-body') || document.body);
|
||||||
|
} else {
|
||||||
|
window.addEventListener('resize', applyGridWidth);
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
// ── Rating stars ─────────────────────────────────────────────────────────────
|
// ── Rating stars ─────────────────────────────────────────────────────────────
|
||||||
function setRating(btn) {
|
function setRating(btn) {
|
||||||
const group = btn.closest('.rating-stars');
|
const group = btn.closest('.rating-stars');
|
||||||
|
|||||||
Reference in New Issue
Block a user