Fix dropdown behavior.
This commit is contained in:
parent
d2061f5c1c
commit
6f175c103e
3 changed files with 68 additions and 23 deletions
|
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-widget [data-canvas] {
|
.grid-widget [data-canvas] {
|
||||||
z-index: 9999;
|
z-index: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
|
|
||||||
.grid-widget [data-dot] {
|
.grid-widget [data-dot] {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 10000;
|
z-index: 2;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: visible;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
@ -57,6 +57,26 @@
|
||||||
flex: 0 0 120px;
|
flex: 0 0 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-widget .dropdown-menu {
|
||||||
|
z-index: 2000;
|
||||||
|
min-width: 220px;
|
||||||
|
padding: .5rem .75rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-widget .dropdown-menu > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-widget .dropdown-menu > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-widget .dropdown-menu .form-range {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.grid-widget [data-toolbar] .badge,
|
.grid-widget [data-toolbar] .badge,
|
||||||
.grid-widget [data-toolbar] .input-group-text {
|
.grid-widget [data-toolbar] .input-group-text {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@ document.addEventListener('keydown', (e) => {
|
||||||
function initGridWidget(root, opts = {}) {
|
function initGridWidget(root, opts = {}) {
|
||||||
const DEFAULT_DOC = { version: 1, cellSize: 25, shapes: [] };
|
const DEFAULT_DOC = { version: 1, cellSize: 25, shapes: [] };
|
||||||
const MAX_HISTORY = 100;
|
const MAX_HISTORY = 100;
|
||||||
|
const SHAPE_DEFAULTS = {
|
||||||
|
strokeWidth: 0.12,
|
||||||
|
strokeOpacity: 1,
|
||||||
|
fillOpacity: 1
|
||||||
|
};
|
||||||
|
|
||||||
const canvasEl = root.querySelector('[data-canvas]');
|
const canvasEl = root.querySelector('[data-canvas]');
|
||||||
const clearEl = root.querySelector('[data-clear]');
|
const clearEl = root.querySelector('[data-clear]');
|
||||||
|
|
@ -39,7 +44,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
if (cellSizeEl && cellSizeValEl) bindRangeWithLabel(cellSizeEl, cellSizeValEl, v => `${v}px`);
|
if (cellSizeEl && cellSizeValEl) bindRangeWithLabel(cellSizeEl, cellSizeValEl, v => `${v}px`);
|
||||||
if (fillOpacityEl && fillValEl) bindRangeWithLabel(fillOpacityEl, fillValEl, v => `${parseInt(Number(v) * 100)}%`);
|
if (fillOpacityEl && fillValEl) bindRangeWithLabel(fillOpacityEl, fillValEl, v => `${parseInt(Number(v) * 100)}%`);
|
||||||
if (strokeOpacityEl && strokeValEl) bindRangeWithLabel(strokeOpacityEl, strokeValEl, v => `${parseInt(Number(v) * 100)}%`);
|
if (strokeOpacityEl && strokeValEl) bindRangeWithLabel(strokeOpacityEl, strokeValEl, v => `${parseInt(Number(v) * 100)}%`);
|
||||||
if (strokeWidthEl && widthValEl) bindRangeWithLabel(strokeWidthEl, widthValEl, v => `${parseInt(Number(v) * 100)}%`);
|
if (strokeWidthEl && widthValEl) bindRangeWithLabel(strokeWidthEl, widthValEl, v => `${parseInt(Number(v) * cellSizeEl.value)}p×`);
|
||||||
|
|
||||||
const storageKey = opts.storageKey ?? 'gridDoc';
|
const storageKey = opts.storageKey ?? 'gridDoc';
|
||||||
|
|
||||||
|
|
@ -81,6 +86,22 @@ function initGridWidget(root, opts = {}) {
|
||||||
|
|
||||||
let activePointerId = null;
|
let activePointerId = null;
|
||||||
|
|
||||||
|
toolBarEl.querySelectorAll('[data-bs-toggle="dropdown"]').forEach((toggle) => {
|
||||||
|
bootstrap.Dropdown.getOrCreateInstance(toggle, {
|
||||||
|
popperConfig(defaultConfig) {
|
||||||
|
return {
|
||||||
|
...defaultConfig,
|
||||||
|
strategy: 'fixed',
|
||||||
|
modifiers: [
|
||||||
|
...(defaultConfig.modifiers || []),
|
||||||
|
{ name: 'preventOverflow', options: { boundary: 'viewport' } },
|
||||||
|
{ name: 'flip', options: { boundary: 'viewport', padding: 8 } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
const key = e.key.toLowerCase();
|
const key = e.key.toLowerCase();
|
||||||
|
|
@ -224,8 +245,8 @@ function initGridWidget(root, opts = {}) {
|
||||||
if (!s || typeof s !== 'object' || !allowed.has(s.type)) return [];
|
if (!s || typeof s !== 'object' || !allowed.has(s.type)) return [];
|
||||||
|
|
||||||
const color = typeof s.color === 'string' ? s.color : '#000000';
|
const color = typeof s.color === 'string' ? s.color : '#000000';
|
||||||
const fillOpacity = clamp01(s.fillOpacity, 1);
|
const fillOpacity = clamp01(s.fillOpacity, SHAPE_DEFAULTS.fillOpacity);
|
||||||
const strokeOpacity = clamp01(s.strokeOpacity, 1);
|
const strokeOpacity = clamp01(s.strokeOpacity, SHAPE_DEFAULTS.strokeOpacity);
|
||||||
|
|
||||||
if (s.type === 'line') {
|
if (s.type === 'line') {
|
||||||
if (!['x1', 'y1', 'x2', 'y2'].every(k => isFiniteNum(s[k]))) return [];
|
if (!['x1', 'y1', 'x2', 'y2'].every(k => isFiniteNum(s[k]))) return [];
|
||||||
|
|
@ -233,7 +254,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
x1: +s.x1, y1: +s.y1, x2: +s.x2, y2: +s.y2,
|
x1: +s.x1, y1: +s.y1, x2: +s.x2, y2: +s.y2,
|
||||||
color,
|
color,
|
||||||
strokeWidth: normStroke(s.strokeWidth),
|
strokeWidth: normStroke(s.strokeWidth, SHAPE_DEFAULTS.strokeWidth),
|
||||||
strokeOpacity
|
strokeOpacity
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +271,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
type: 'path',
|
type: 'path',
|
||||||
points,
|
points,
|
||||||
color,
|
color,
|
||||||
strokeWidth: normStroke(s.strokeWidth, 0.12),
|
strokeWidth: normStroke(s.strokeWidth, SHAPE_DEFAULTS.strokeWidth),
|
||||||
strokeOpacity
|
strokeOpacity
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +284,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
fill: !!s.fill,
|
fill: !!s.fill,
|
||||||
fillOpacity,
|
fillOpacity,
|
||||||
strokeOpacity,
|
strokeOpacity,
|
||||||
strokeWidth: normStroke(s.strokeWidth)
|
strokeWidth: normStroke(s.strokeWidth, SHAPE_DEFAULTS.strokeWidth)
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -473,7 +494,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.strokeStyle = shape.color || '#000000';
|
ctx.strokeStyle = shape.color || '#000000';
|
||||||
ctx.lineWidth = Math.max(1, toPx(shape.strokeWidth ?? 0.12));
|
ctx.lineWidth = Math.max(1, toPx(shape.strokeWidth ?? SHAPE_DEFAULTS.strokeWidth));
|
||||||
|
|
||||||
if (shape.type === 'rect' || shape.type === 'ellipse') {
|
if (shape.type === 'rect' || shape.type === 'ellipse') {
|
||||||
const x = toPx(shape.x);
|
const x = toPx(shape.x);
|
||||||
|
|
@ -481,7 +502,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
const w = toPx(shape.w);
|
const w = toPx(shape.w);
|
||||||
const h = toPx(shape.h);
|
const h = toPx(shape.h);
|
||||||
|
|
||||||
ctx.globalAlpha = clamp01(shape.strokeOpacity, 1);
|
ctx.globalAlpha = clamp01(shape.strokeOpacity, SHAPE_DEFAULTS.strokeOpacity);
|
||||||
if (shape.type === 'rect') {
|
if (shape.type === 'rect') {
|
||||||
ctx.strokeRect(x, y, w, h);
|
ctx.strokeRect(x, y, w, h);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -494,7 +515,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
if (shape.fill) {
|
if (shape.fill) {
|
||||||
ctx.globalAlpha = clamp01(shape.fillOpacity, 1);
|
ctx.globalAlpha = clamp01(shape.fillOpacity, SHAPE_DEFAULTS.fillOpacity);
|
||||||
ctx.fillStyle = shape.color;
|
ctx.fillStyle = shape.color;
|
||||||
if (shape.type === 'rect') {
|
if (shape.type === 'rect') {
|
||||||
ctx.fillRect(x, y, w, h);
|
ctx.fillRect(x, y, w, h);
|
||||||
|
|
@ -514,7 +535,7 @@ function initGridWidget(root, opts = {}) {
|
||||||
const x2 = toPx(shape.x2);
|
const x2 = toPx(shape.x2);
|
||||||
const y2 = toPx(shape.y2);
|
const y2 = toPx(shape.y2);
|
||||||
|
|
||||||
ctx.globalAlpha = clamp01(shape.strokeOpacity, 1);
|
ctx.globalAlpha = clamp01(shape.strokeOpacity, SHAPE_DEFAULTS.strokeOpacity);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x1, y1);
|
ctx.moveTo(x1, y1);
|
||||||
ctx.lineTo(x2, y2);
|
ctx.lineTo(x2, y2);
|
||||||
|
|
@ -523,9 +544,9 @@ function initGridWidget(root, opts = {}) {
|
||||||
} else if (shape.type === 'path') {
|
} else if (shape.type === 'path') {
|
||||||
const toPx = (v) => v * cellSize;
|
const toPx = (v) => v * cellSize;
|
||||||
|
|
||||||
ctx.globalAlpha = clamp01(shape.strokeOpacity, 1);
|
ctx.globalAlpha = clamp01(shape.strokeOpacity, SHAPE_DEFAULTS.strokeOpacity);
|
||||||
|
|
||||||
ctx.lineWidth = Math.max(1, toPx(shape.strokeWidth ?? 0.12));
|
ctx.lineWidth = Math.max(1, toPx(shape.strokeWidth ?? SHAPE_DEFAULTS.strokeWidth));
|
||||||
ctx.lineJoin = 'round';
|
ctx.lineJoin = 'round';
|
||||||
ctx.lineCap = 'round';
|
ctx.lineCap = 'round';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,11 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span class="badge text-bg-secondary" data-cell-size-val>25px</span>
|
<span class="badge text-bg-secondary" data-cell-size-val>25px</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu p-2">
|
||||||
<li>
|
<li>
|
||||||
|
<div class="small text-secondary mb-1">Cell Size</div>
|
||||||
<input type="range" min="1" max="100" step="1" value="25" data-cell-size
|
<input type="range" min="1" max="100" step="1" value="25" data-cell-size
|
||||||
class="form-range toolbar-range">
|
class="form-range w-100">
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -30,10 +31,11 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span class="badge text-bg-secondary" data-fill-opacity-val>100%</span>
|
<span class="badge text-bg-secondary" data-fill-opacity-val>100%</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu p-2">
|
||||||
<li>
|
<li>
|
||||||
|
<div class="small text-secondary mb-1">Fill Opacity</div>
|
||||||
<input type="range" min="0" max="1" step="0.01" value="1" data-fill-opacity
|
<input type="range" min="0" max="1" step="0.01" value="1" data-fill-opacity
|
||||||
class="form-range toolbar-range">
|
class="form-range w-100">
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -47,10 +49,11 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span class="badge text-bg-secondary" data-stroke-opacity-val>100%</span>
|
<span class="badge text-bg-secondary" data-stroke-opacity-val>100%</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu p-2">
|
||||||
<li>
|
<li>
|
||||||
|
<div class="small text-secondary mb-1">Stroke Opacity</div>
|
||||||
<input type="range" min="0" max="1" step="0.01" value="1" data-stroke-opacity
|
<input type="range" min="0" max="1" step="0.01" value="1" data-stroke-opacity
|
||||||
class="form-range toolbar-range">
|
class="form-range w-100">
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,10 +68,11 @@
|
||||||
</svg>
|
</svg>
|
||||||
<span class="badge text-bg-secondary" data-stroke-width-val>12%</span>
|
<span class="badge text-bg-secondary" data-stroke-width-val>12%</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu p-2">
|
||||||
<li>
|
<li>
|
||||||
|
<div class="small text-secondary mb-1">Stroke Width</div>
|
||||||
<input type="range" min="0" max="1" step="0.01" value="0.12" data-stroke-width
|
<input type="range" min="0" max="1" step="0.01" value="0.12" data-stroke-width
|
||||||
class="form-range toolbar-range">
|
class="form-range w-100">
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue