@bagelink/vue 1.15.267 → 1.15.271
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.
- package/dist/components/FilterQuery.vue.d.ts.map +1 -1
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/dialog/Dialog.vue.d.ts +1 -0
- package/dist/dialog/Dialog.vue.d.ts.map +1 -1
- package/dist/i18n/index.d.ts +42 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/index.cjs +103 -76
- package/dist/index.mjs +10261 -9990
- package/dist/style.css +1 -1
- package/dist/utils/queryFilter.d.ts +13 -1
- package/dist/utils/queryFilter.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FilterQuery.vue +123 -11
- package/src/components/dataTable/DataTable.vue +24 -5
- package/src/components/form/inputs/RichText/editor.css +28 -0
- package/src/components/form/inputs/RichText/index.vue +57 -5
- package/src/dialog/Dialog.vue +9 -9
- package/src/i18n/locales/en.json +9 -2
- package/src/i18n/locales/es.json +9 -2
- package/src/i18n/locales/fr.json +9 -2
- package/src/i18n/locales/he.json +9 -2
- package/src/i18n/locales/it.json +9 -2
- package/src/i18n/locales/ru.json +9 -2
- package/src/utils/queryFilter.ts +128 -11
|
@@ -27,7 +27,7 @@ type DeepKeyOf<T> = T extends Primitive ? never : {
|
|
|
27
27
|
/**
|
|
28
28
|
* SCIM 2.0 comparison operators
|
|
29
29
|
*/
|
|
30
|
-
export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr' | 'in' | 'nin';
|
|
30
|
+
export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr' | 'in' | 'nin' | 'between';
|
|
31
31
|
/**
|
|
32
32
|
* SCIM 2.0 logical operators
|
|
33
33
|
*/
|
|
@@ -103,6 +103,18 @@ export declare class QueryFilter<T extends Record<string, any>> {
|
|
|
103
103
|
* Present check (field has a value)
|
|
104
104
|
*/
|
|
105
105
|
pr<K extends DeepKeyOf<T>>(field: K): this;
|
|
106
|
+
/**
|
|
107
|
+
* In list — native membership / array overlap: field in ["a","b"]
|
|
108
|
+
*/
|
|
109
|
+
in<K extends DeepKeyOf<T>>(field: K, values: Primitive[]): this;
|
|
110
|
+
/**
|
|
111
|
+
* Not in list: field nin ["a","b"]
|
|
112
|
+
*/
|
|
113
|
+
nin<K extends DeepKeyOf<T>>(field: K, values: Primitive[]): this;
|
|
114
|
+
/**
|
|
115
|
+
* Inclusive range: field between [lo, hi]
|
|
116
|
+
*/
|
|
117
|
+
between<K extends DeepKeyOf<T>>(field: K, lo: number | string, hi: number | string): this;
|
|
106
118
|
/**
|
|
107
119
|
* AND logical operator
|
|
108
120
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AAEjD,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GACpC,KAAK,GACL;KACA,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;CACtD,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;AAEpB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GACzB,IAAI,GACL,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -66,6 +66,11 @@ function isComplete(c: { field: string, op: ComparisonOperator, value?: any }) {
|
|
|
66
66
|
if (isQueryOptionField(c.field as string)) return c.value !== '' && c.value != null
|
|
67
67
|
if (c.op === 'pr') return true
|
|
68
68
|
if (c.op === 'in' || c.op === 'nin') return Array.isArray(c.value) && c.value.length > 0
|
|
69
|
+
if (c.op === 'between') {
|
|
70
|
+
return Array.isArray(c.value) && c.value.length === 2
|
|
71
|
+
&& c.value[0] !== '' && c.value[0] != null
|
|
72
|
+
&& c.value[1] !== '' && c.value[1] != null
|
|
73
|
+
}
|
|
69
74
|
return c.value !== '' && c.value !== null && c.value !== undefined
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -102,12 +107,16 @@ const currentTexts = computed(() => ({
|
|
|
102
107
|
pr: $t('filter.operators.pr'),
|
|
103
108
|
in: $t('filter.operators.in'),
|
|
104
109
|
nin: $t('filter.operators.nin'),
|
|
110
|
+
between: $t('filter.operators.between'),
|
|
105
111
|
},
|
|
106
112
|
placeholders: {
|
|
107
113
|
selectField: $t('filter.placeholders.selectField'),
|
|
108
114
|
selectOption: $t('filter.placeholders.selectOption'),
|
|
109
115
|
selectDate: $t('filter.placeholders.selectDate'),
|
|
110
116
|
enterValue: $t('filter.placeholders.enterValue'),
|
|
117
|
+
from: $t('filter.placeholders.from'),
|
|
118
|
+
to: $t('filter.placeholders.to'),
|
|
119
|
+
commaSeparated: $t('filter.placeholders.commaSeparated'),
|
|
111
120
|
},
|
|
112
121
|
buttons: {
|
|
113
122
|
addCondition: $t('filter.buttons.addCondition'),
|
|
@@ -157,23 +166,25 @@ const allOperatorOptions = computed(() => [
|
|
|
157
166
|
{ label: currentTexts.value.operators.pr, value: 'pr' as ComparisonOperator },
|
|
158
167
|
{ label: currentTexts.value.operators.in, value: 'in' as ComparisonOperator },
|
|
159
168
|
{ label: currentTexts.value.operators.nin, value: 'nin' as ComparisonOperator },
|
|
169
|
+
{ label: currentTexts.value.operators.between, value: 'between' as ComparisonOperator },
|
|
160
170
|
])
|
|
161
171
|
|
|
162
172
|
function getOperatorsForType(fieldType: string, hasOptions: boolean, isMultiple: boolean) {
|
|
173
|
+
const allow = (ops: string[]) => allOperatorOptions.value.filter(o => ops.includes(o.value))
|
|
163
174
|
if (isMultiple && hasOptions) {
|
|
164
|
-
return
|
|
175
|
+
return allow(['in', 'nin', 'pr'])
|
|
165
176
|
}
|
|
166
177
|
if (hasOptions) {
|
|
167
|
-
return
|
|
178
|
+
return allow(['eq', 'ne', 'in', 'nin', 'pr'])
|
|
168
179
|
}
|
|
169
180
|
switch (fieldType) {
|
|
170
181
|
case 'boolean':
|
|
171
|
-
return
|
|
182
|
+
return allow(['eq', 'ne', 'pr'])
|
|
172
183
|
case 'number':
|
|
173
184
|
case 'date':
|
|
174
|
-
return
|
|
185
|
+
return allow(['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'between', 'in', 'nin', 'pr'])
|
|
175
186
|
default:
|
|
176
|
-
return
|
|
187
|
+
return allow(['eq', 'ne', 'co', 'sw', 'ew', 'in', 'nin', 'pr'])
|
|
177
188
|
}
|
|
178
189
|
}
|
|
179
190
|
|
|
@@ -281,7 +292,13 @@ function updateConditionAtIndex(index: number, updates: Partial<{ field: string,
|
|
|
281
292
|
const updated = { ...existing }
|
|
282
293
|
if (updates.field !== undefined) updated.field = updates.field
|
|
283
294
|
if (updates.op !== undefined) updated.op = updates.op
|
|
284
|
-
if (updates.value !== undefined)
|
|
295
|
+
if (updates.value !== undefined) {
|
|
296
|
+
updated.value = Array.isArray(updates.value)
|
|
297
|
+
// Coerce each list/range element (e.g. "18" → 18) so numeric filters
|
|
298
|
+
// emit bare numbers in the bracket literal.
|
|
299
|
+
? updates.value.map(v => parseValue(typeof v === 'string' ? v : String(v)))
|
|
300
|
+
: parseValue(updates.value)
|
|
301
|
+
}
|
|
285
302
|
if (updates.connector !== undefined) updated.connector = updates.connector
|
|
286
303
|
|
|
287
304
|
newModel[index] = updated
|
|
@@ -334,6 +351,38 @@ function isMultiOperator(operator: ComparisonOperator): boolean {
|
|
|
334
351
|
return operator === 'in' || operator === 'nin'
|
|
335
352
|
}
|
|
336
353
|
|
|
354
|
+
function isBetweenOperator(operator: ComparisonOperator): boolean {
|
|
355
|
+
return operator === 'between'
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** in/nin on a field WITHOUT predefined options → comma-separated free text. */
|
|
359
|
+
function isFreeListOperator(condition: UICondition): boolean {
|
|
360
|
+
return isMultiOperator(condition.operator) && !getFieldOptions(condition.field)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Convert an array value to comma-separated text for the free-list input. */
|
|
364
|
+
function listToText(value: string | string[]): string {
|
|
365
|
+
return Array.isArray(value) ? value.join(', ') : (value ?? '')
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Split comma-separated free-list text into a trimmed string[]. */
|
|
369
|
+
function textToList(text: string): string[] {
|
|
370
|
+
return text.split(',').map(s => s.trim()).filter(s => s !== '')
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Read one bound of a `between` value ([lo, hi]); index 0 = lo, 1 = hi. */
|
|
374
|
+
function betweenBound(condition: UICondition, index: 0 | 1): string {
|
|
375
|
+
return Array.isArray(condition.value) ? (condition.value[index] ?? '') : ''
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Update one bound of a `between` value, preserving the other. */
|
|
379
|
+
function onBetweenBoundChange(id: string, index: 0 | 1, v: string) {
|
|
380
|
+
const cond = conditions.value.find(c => c.id === id)
|
|
381
|
+
const current = cond && Array.isArray(cond.value) ? [...cond.value] : ['', '']
|
|
382
|
+
current[index] = v
|
|
383
|
+
onValueChange(id, [current[0] ?? '', current[1] ?? ''])
|
|
384
|
+
}
|
|
385
|
+
|
|
337
386
|
const activeFiltersCount = computed(() => {
|
|
338
387
|
return conditions.value.filter((c: UICondition) => Boolean(c.field) && (c.operator === 'pr' || Boolean(c.value))).length
|
|
339
388
|
})
|
|
@@ -364,11 +413,19 @@ function onOperatorChange(id: string, operator: ComparisonOperator) {
|
|
|
364
413
|
const index = getConditionIndex(id)
|
|
365
414
|
if (index < 0) return
|
|
366
415
|
const current = conditions.value[index]
|
|
367
|
-
const
|
|
368
|
-
const isMulti = operator === 'in' || operator === 'nin'
|
|
416
|
+
const prevOp = current?.operator
|
|
369
417
|
const updates: Parameters<typeof updateConditionAtIndex>[1] = { op: operator }
|
|
370
|
-
|
|
371
|
-
|
|
418
|
+
|
|
419
|
+
// Reset the value shape when the operator's expected value type changes.
|
|
420
|
+
const wasList = prevOp === 'in' || prevOp === 'nin'
|
|
421
|
+
const wasBetween = prevOp === 'between'
|
|
422
|
+
const isList = operator === 'in' || operator === 'nin'
|
|
423
|
+
const isBetween = operator === 'between'
|
|
424
|
+
|
|
425
|
+
if (isBetween && !wasBetween) updates.value = ['', '']
|
|
426
|
+
else if (isList && !wasList) updates.value = []
|
|
427
|
+
else if (!isList && !isBetween && (wasList || wasBetween)) updates.value = ''
|
|
428
|
+
|
|
372
429
|
updateConditionAtIndex(index, updates)
|
|
373
430
|
}
|
|
374
431
|
|
|
@@ -466,8 +523,43 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
466
523
|
|
|
467
524
|
<!-- Value input - type-specific -->
|
|
468
525
|
<template v-if="needsValue(condition.operator)">
|
|
526
|
+
<!-- between: two type-aware bound inputs (min / max) -->
|
|
527
|
+
<div
|
|
528
|
+
v-if="isBetweenOperator(condition.operator)"
|
|
529
|
+
:key="`${condition.id}-${condition.field}-between`"
|
|
530
|
+
class="flex gap-025 align-items-center between-inputs"
|
|
531
|
+
>
|
|
532
|
+
<DateInput
|
|
533
|
+
v-if="getFieldType(condition.field) === 'date'"
|
|
534
|
+
:model-value="betweenBound(condition, 0)"
|
|
535
|
+
:placeholder="currentTexts.placeholders.from" class="m-0 light-input borderHover"
|
|
536
|
+
@update:model-value="(v: any) => onBetweenBoundChange(condition.id, 0, String(v))"
|
|
537
|
+
/>
|
|
538
|
+
<TextInput
|
|
539
|
+
v-else
|
|
540
|
+
:model-value="betweenBound(condition, 0)" placeholder="0"
|
|
541
|
+
:type="getFieldType(condition.field) === 'number' ? 'number' : 'text'"
|
|
542
|
+
class="m-0 light-input borderHover"
|
|
543
|
+
@update:model-value="(v: string) => onBetweenBoundChange(condition.id, 0, v)"
|
|
544
|
+
/>
|
|
545
|
+
<span class="opacity-4 txt-12">–</span>
|
|
546
|
+
<DateInput
|
|
547
|
+
v-if="getFieldType(condition.field) === 'date'"
|
|
548
|
+
:model-value="betweenBound(condition, 1)"
|
|
549
|
+
:placeholder="currentTexts.placeholders.to" class="m-0 light-input borderHover"
|
|
550
|
+
@update:model-value="(v: any) => onBetweenBoundChange(condition.id, 1, String(v))"
|
|
551
|
+
/>
|
|
552
|
+
<TextInput
|
|
553
|
+
v-else
|
|
554
|
+
:model-value="betweenBound(condition, 1)" placeholder="0"
|
|
555
|
+
:type="getFieldType(condition.field) === 'number' ? 'number' : 'text'"
|
|
556
|
+
class="m-0 light-input borderHover"
|
|
557
|
+
@update:model-value="(v: string) => onBetweenBoundChange(condition.id, 1, v)"
|
|
558
|
+
/>
|
|
559
|
+
</div>
|
|
560
|
+
<!-- in/nin with predefined options: multiselect -->
|
|
469
561
|
<SelectInput
|
|
470
|
-
v-if="getFieldOptions(condition.field) && isMultiOperator(condition.operator)"
|
|
562
|
+
v-else-if="getFieldOptions(condition.field) && isMultiOperator(condition.operator)"
|
|
471
563
|
:key="`${condition.id}-${condition.field}-multi`"
|
|
472
564
|
:model-value="Array.isArray(condition.value) ? condition.value : []"
|
|
473
565
|
:options="getFieldOptions(condition.field)!"
|
|
@@ -475,6 +567,15 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
475
567
|
class="m-0 light-input borderHover" searchable multiselect
|
|
476
568
|
@update:model-value="(v: string[]) => onValueChange(condition.id, v)"
|
|
477
569
|
/>
|
|
570
|
+
<!-- in/nin without options: comma-separated free-text list -->
|
|
571
|
+
<TextInput
|
|
572
|
+
v-else-if="isFreeListOperator(condition)"
|
|
573
|
+
:key="`${condition.id}-${condition.field}-freelist`"
|
|
574
|
+
:model-value="listToText(condition.value)"
|
|
575
|
+
:placeholder="currentTexts.placeholders.commaSeparated"
|
|
576
|
+
class="m-0 light-input borderHover"
|
|
577
|
+
@update:model-value="(v: string) => onValueChange(condition.id, textToList(v))"
|
|
578
|
+
/>
|
|
478
579
|
<SelectInput
|
|
479
580
|
v-else-if="getFieldOptions(condition.field)"
|
|
480
581
|
:key="`${condition.id}-${condition.field}-single`"
|
|
@@ -553,6 +654,17 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
553
654
|
--input-background-color: transparent !important;
|
|
554
655
|
}
|
|
555
656
|
|
|
657
|
+
.between-inputs {
|
|
658
|
+
min-width: 0;
|
|
659
|
+
}
|
|
660
|
+
.between-inputs > * {
|
|
661
|
+
min-width: 0;
|
|
662
|
+
flex: 1 1 0;
|
|
663
|
+
}
|
|
664
|
+
.between-inputs > span {
|
|
665
|
+
flex: 0 0 auto;
|
|
666
|
+
}
|
|
667
|
+
|
|
556
668
|
.and-or-select .selectinput-btn {
|
|
557
669
|
border: none;
|
|
558
670
|
width: 100%;
|
|
@@ -185,7 +185,7 @@ const headerFontSize = computed(() => {
|
|
|
185
185
|
<table class="infinite-wrapper" :class="{ selecting: computedSelectedItems.length }">
|
|
186
186
|
<thead class="row first-row">
|
|
187
187
|
<th v-if="isSortable" class="col-drag" />
|
|
188
|
-
<th v-if="isSelectable
|
|
188
|
+
<th v-if="isSelectable" class="col-check">
|
|
189
189
|
<input ref="allSelectorEl" type="checkbox" @click.stop @change="(e) => toggleSelectAll(e)">
|
|
190
190
|
</th>
|
|
191
191
|
<th
|
|
@@ -210,7 +210,7 @@ const headerFontSize = computed(() => {
|
|
|
210
210
|
<td v-if="isSortable" class="col-drag">
|
|
211
211
|
<Icon icon="drag_indicator" :size="1.2" class="drag-handle-col grab" />
|
|
212
212
|
</td>
|
|
213
|
-
<td v-if="isSelectable
|
|
213
|
+
<td v-if="isSelectable" class="col-check">
|
|
214
214
|
<div @click.stop>
|
|
215
215
|
<input v-model="selectedItems" type="checkbox" :value="row?.id || ''">
|
|
216
216
|
</div>
|
|
@@ -244,13 +244,32 @@ const headerFontSize = computed(() => {
|
|
|
244
244
|
<style scoped>
|
|
245
245
|
|
|
246
246
|
.col-drag {
|
|
247
|
-
width:
|
|
247
|
+
width: 1rem;
|
|
248
|
+
padding: 0 0.3rem 0 0;
|
|
249
|
+
text-align: center;
|
|
250
|
+
vertical-align: middle;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.col-drag .drag-handle-col {
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.col-check {
|
|
260
|
+
width: 1rem;
|
|
248
261
|
padding: 0 0.25rem;
|
|
262
|
+
vertical-align: middle;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.col-check > div {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: center;
|
|
249
268
|
}
|
|
250
269
|
|
|
251
270
|
.col-drag .bgl_icon-font {
|
|
252
271
|
color: var(--bgl-black-tint);
|
|
253
|
-
opacity: 0
|
|
272
|
+
opacity: 0;
|
|
254
273
|
transition: opacity 0.15s;
|
|
255
274
|
}
|
|
256
275
|
|
|
@@ -434,7 +453,7 @@ background: var(--bgl-gray-light);
|
|
|
434
453
|
}
|
|
435
454
|
|
|
436
455
|
.row-item input[type='checkbox'] {
|
|
437
|
-
margin-top: 0
|
|
456
|
+
margin-top: 0 !important;
|
|
438
457
|
accent-color: var(--bgl-accent-color);
|
|
439
458
|
}
|
|
440
459
|
|
|
@@ -200,6 +200,34 @@ th {
|
|
|
200
200
|
opacity: 0.8;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/* Image alignment: full width (default) vs floated start/end.
|
|
204
|
+
Targets align-* classes directly (not .image-figure) so alignment survives
|
|
205
|
+
getCleanHTML(), which strips the editor-only .image-figure class from output. */
|
|
206
|
+
.richtext-editor-content figure.align-full {
|
|
207
|
+
float: none;
|
|
208
|
+
width: 100%;
|
|
209
|
+
margin-inline: auto;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.richtext-editor-content figure.align-start {
|
|
213
|
+
float: inline-start;
|
|
214
|
+
max-width: 50%;
|
|
215
|
+
margin-block: 0.25rem 0.75rem;
|
|
216
|
+
margin-inline: 0 1.5rem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.richtext-editor-content figure.align-end {
|
|
220
|
+
float: inline-end;
|
|
221
|
+
max-width: 50%;
|
|
222
|
+
margin-block: 0.25rem 0.75rem;
|
|
223
|
+
margin-inline: 1.5rem 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.richtext-editor-content figure.align-start img,
|
|
227
|
+
.richtext-editor-content figure.align-end img {
|
|
228
|
+
width: 100%;
|
|
229
|
+
}
|
|
230
|
+
|
|
203
231
|
/* Responsive iframe wrapper */
|
|
204
232
|
.richtext-editor-content div:has(> iframe) {
|
|
205
233
|
position: relative;
|
|
@@ -229,7 +229,8 @@ const imageForm = ref({
|
|
|
229
229
|
width: '',
|
|
230
230
|
height: '',
|
|
231
231
|
credit: '',
|
|
232
|
-
figcaption: false
|
|
232
|
+
figcaption: false,
|
|
233
|
+
align: 'full' // full, start, end
|
|
233
234
|
})
|
|
234
235
|
let pendingImageData: {
|
|
235
236
|
selection: Selection
|
|
@@ -703,13 +704,18 @@ function openImageModal(existingImage: HTMLElement | null = null) {
|
|
|
703
704
|
const figcaption = existingImage.querySelector('figcaption')
|
|
704
705
|
const credit = existingImage.getAttribute('data-credit') || img?.getAttribute('data-credit') || ''
|
|
705
706
|
|
|
707
|
+
// Read alignment from the figure (or bare img) data-align attribute
|
|
708
|
+
const alignSource = existingImage.tagName === 'IMG' ? img : existingImage
|
|
709
|
+
const align = alignSource?.getAttribute('data-align') || 'full'
|
|
710
|
+
|
|
706
711
|
imageForm.value = {
|
|
707
712
|
src: img?.src || '',
|
|
708
713
|
alt: img?.alt || '',
|
|
709
714
|
width: img?.getAttribute('data-width') || img?.style.width || '',
|
|
710
715
|
height: img?.getAttribute('data-height') || img?.style.height || '',
|
|
711
716
|
credit,
|
|
712
|
-
figcaption: !!figcaption
|
|
717
|
+
figcaption: !!figcaption,
|
|
718
|
+
align
|
|
713
719
|
}
|
|
714
720
|
} else {
|
|
715
721
|
// Reset form for new image
|
|
@@ -719,7 +725,8 @@ function openImageModal(existingImage: HTMLElement | null = null) {
|
|
|
719
725
|
width: '',
|
|
720
726
|
height: '',
|
|
721
727
|
credit: '',
|
|
722
|
-
figcaption: false
|
|
728
|
+
figcaption: false,
|
|
729
|
+
align: 'full'
|
|
723
730
|
}
|
|
724
731
|
}
|
|
725
732
|
|
|
@@ -749,7 +756,9 @@ function submitImage() {
|
|
|
749
756
|
img.style.width = `${imageForm.value.width}px`
|
|
750
757
|
}
|
|
751
758
|
} else {
|
|
752
|
-
|
|
759
|
+
// Floated images default to a partial width so text can wrap alongside;
|
|
760
|
+
// full-width images fill the container.
|
|
761
|
+
img.style.width = (imageForm.value.align === 'start' || imageForm.value.align === 'end') ? '45%' : '100%'
|
|
753
762
|
}
|
|
754
763
|
|
|
755
764
|
if (imageForm.value.height) {
|
|
@@ -842,6 +851,12 @@ function submitImage() {
|
|
|
842
851
|
selection.addRange(range)
|
|
843
852
|
}
|
|
844
853
|
|
|
854
|
+
// Apply alignment to the figure wrapper (full = full width, start/end = float)
|
|
855
|
+
const align = imageForm.value.align || 'full'
|
|
856
|
+
elementToUpdate.classList.remove('align-full', 'align-start', 'align-end')
|
|
857
|
+
elementToUpdate.classList.add(`align-${align}`)
|
|
858
|
+
elementToUpdate.setAttribute('data-align', align)
|
|
859
|
+
|
|
845
860
|
updateContentWithHistory(doc)
|
|
846
861
|
showImageModal.value = false
|
|
847
862
|
pendingImageData = null
|
|
@@ -1765,7 +1780,9 @@ function getCleanHTML(doc: Document | null): string {
|
|
|
1765
1780
|
// that remains after video-container conversion
|
|
1766
1781
|
clone.querySelectorAll('.bgl_vid').forEach((el) => { el.remove() })
|
|
1767
1782
|
|
|
1768
|
-
// Strip editor-only classes from <figure> elements so output is pure semantic HTML
|
|
1783
|
+
// Strip editor-only classes from <figure> elements so output is pure semantic HTML.
|
|
1784
|
+
// Keep alignment classes (align-full/align-start/align-end) so floated images
|
|
1785
|
+
// render correctly outside the editor.
|
|
1769
1786
|
clone.querySelectorAll('figure').forEach((fig) => {
|
|
1770
1787
|
fig.classList.remove('image-figure', 'video-figure', 'embed-figure')
|
|
1771
1788
|
if (fig.classList.length === 0) { fig.removeAttribute('class') }
|
|
@@ -2887,6 +2904,33 @@ async function initEditor() {
|
|
|
2887
2904
|
outline-offset: 2px;
|
|
2888
2905
|
}
|
|
2889
2906
|
|
|
2907
|
+
/* ── Image alignment ───────────────────────────────────────── */
|
|
2908
|
+
/* Default: full width block. */
|
|
2909
|
+
figure.align-full {
|
|
2910
|
+
float: none;
|
|
2911
|
+
width: 100%;
|
|
2912
|
+
margin-inline: 0;
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
figure.align-start {
|
|
2916
|
+
float: inline-start;
|
|
2917
|
+
max-width: 50%;
|
|
2918
|
+
margin-block: 0.25em 0.75em;
|
|
2919
|
+
margin-inline: 0 1.25em;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
figure.align-end {
|
|
2923
|
+
float: inline-end;
|
|
2924
|
+
max-width: 50%;
|
|
2925
|
+
margin-block: 0.25em 0.75em;
|
|
2926
|
+
margin-inline: 1.25em 0;
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
figure.align-start img,
|
|
2930
|
+
figure.align-end img {
|
|
2931
|
+
width: 100%;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2890
2934
|
/* Selected state — applied via JS on click, stripped by getCleanHTML() */
|
|
2891
2935
|
.media-selected {
|
|
2892
2936
|
outline: 2px solid #007bff !important;
|
|
@@ -3484,6 +3528,14 @@ const imgTransform = computed({
|
|
|
3484
3528
|
help="Examples: auto, 300px"
|
|
3485
3529
|
/>
|
|
3486
3530
|
</div>
|
|
3531
|
+
<SelectInput
|
|
3532
|
+
v-model="imageForm.align" label="Alignment" :options="[
|
|
3533
|
+
{ value: 'full', label: 'Full width (default)' },
|
|
3534
|
+
{ value: 'start', label: 'Float to start (text wraps)' },
|
|
3535
|
+
{ value: 'end', label: 'Float to end (text wraps)' },
|
|
3536
|
+
]"
|
|
3537
|
+
help="Full width fills the container. Start/End float the image so text flows alongside."
|
|
3538
|
+
/>
|
|
3487
3539
|
<TextInput v-model="imageForm.credit" label="Photo Credit" placeholder="Photographer name (optional)" />
|
|
3488
3540
|
<CheckInput
|
|
3489
3541
|
v-model="imageForm.figcaption" label="Show alt text as caption"
|
package/src/dialog/Dialog.vue
CHANGED
|
@@ -125,20 +125,20 @@ defineExpose({ close })
|
|
|
125
125
|
<div v-if="showGrip" class="dialog-grip" aria-hidden="true" />
|
|
126
126
|
<!-- Header -->
|
|
127
127
|
<header
|
|
128
|
-
v-if="title || dismissable" class="flex space-between flex-shrink-0 align-items-start gap-05"
|
|
129
|
-
:class="{ 'border-bottom': !flat, 'border-none': !title, 'm_ps-1': title, 'p-05 m_p-05': !title, 'ps-1-5 pe-1 py-1 ': !thin && title, 'ps-1 pe-05 py-05': thin && title }"
|
|
128
|
+
v-if="title || $slots.header || dismissable" class="flex space-between flex-shrink-0 align-items-start gap-05"
|
|
129
|
+
:class="{ 'border-bottom': !flat, 'border-none': !title && !$slots.header, 'm_ps-1': title || $slots.header, 'p-05 m_p-05': !title && !$slots.header, 'ps-1-5 pe-1 py-1 ': !thin && (title || $slots.header), 'ps-1 pe-05 py-05': thin && (title || $slots.header) }"
|
|
130
130
|
>
|
|
131
131
|
<Btn
|
|
132
132
|
v-if="dismissable && closePlacement === 'header-start'" flat icon="close" aria-label="Close"
|
|
133
133
|
class="dialog-close relative z-2" thin @click="close"
|
|
134
134
|
/>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
<h3
|
|
136
|
+
v-if="title || $slots.header" class="dialog-title m-0 txt20 semi w-100p"
|
|
137
|
+
:class="{ 'txt-center balance': centerTitle, 'word-break-all': !centerTitle, 'ellipsis-1': ellipsisTitle }"
|
|
138
|
+
>
|
|
139
|
+
<slot name="header">{{ title }}</slot>
|
|
140
|
+
</h3>
|
|
141
|
+
<div v-if="!title && !$slots.header" class="flex-grow-1" />
|
|
142
142
|
<Btn
|
|
143
143
|
v-if="dismissable && closePlacement === 'header-end'" flat icon="close" aria-label="Close"
|
|
144
144
|
class="dialog-close relative z-2" thin @click="close"
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -78,13 +78,17 @@
|
|
|
78
78
|
"ew": "Ends with",
|
|
79
79
|
"pr": "Present",
|
|
80
80
|
"in": "In list",
|
|
81
|
-
"nin": "Not in list"
|
|
81
|
+
"nin": "Not in list",
|
|
82
|
+
"between": "Between"
|
|
82
83
|
},
|
|
83
84
|
"placeholders": {
|
|
84
85
|
"selectField": "Select field",
|
|
85
86
|
"selectOption": "Select option",
|
|
86
87
|
"selectDate": "Select date",
|
|
87
|
-
"enterValue": "Value"
|
|
88
|
+
"enterValue": "Value",
|
|
89
|
+
"from": "From",
|
|
90
|
+
"to": "To",
|
|
91
|
+
"commaSeparated": "Comma-separated values"
|
|
88
92
|
},
|
|
89
93
|
"buttons": {
|
|
90
94
|
"addCondition": "Add condition",
|
|
@@ -496,5 +500,8 @@
|
|
|
496
500
|
"charts": {
|
|
497
501
|
"total": "Total",
|
|
498
502
|
"share": "Share"
|
|
503
|
+
},
|
|
504
|
+
"guidedHelp": {
|
|
505
|
+
"hint": "The walkthrough is always available on the question mark."
|
|
499
506
|
}
|
|
500
507
|
}
|
package/src/i18n/locales/es.json
CHANGED
|
@@ -77,13 +77,17 @@
|
|
|
77
77
|
"ew": "Termina con",
|
|
78
78
|
"pr": "Presente",
|
|
79
79
|
"in": "En lista",
|
|
80
|
-
"nin": "No en lista"
|
|
80
|
+
"nin": "No en lista",
|
|
81
|
+
"between": "Entre"
|
|
81
82
|
},
|
|
82
83
|
"placeholders": {
|
|
83
84
|
"selectField": "Seleccionar campo",
|
|
84
85
|
"selectOption": "Seleccionar opción",
|
|
85
86
|
"selectDate": "Seleccionar fecha",
|
|
86
|
-
"enterValue": "Valor"
|
|
87
|
+
"enterValue": "Valor",
|
|
88
|
+
"from": "Desde",
|
|
89
|
+
"to": "Hasta",
|
|
90
|
+
"commaSeparated": "Valores separados por comas"
|
|
87
91
|
},
|
|
88
92
|
"buttons": {
|
|
89
93
|
"addCondition": "Añadir condición",
|
|
@@ -270,5 +274,8 @@
|
|
|
270
274
|
"addToCalendar": {
|
|
271
275
|
"addTo": "Agregar a {name}",
|
|
272
276
|
"addToCalendar": "Agregar al calendario"
|
|
277
|
+
},
|
|
278
|
+
"guidedHelp": {
|
|
279
|
+
"hint": "La guía siempre está disponible en el signo de interrogación."
|
|
273
280
|
}
|
|
274
281
|
}
|
package/src/i18n/locales/fr.json
CHANGED
|
@@ -77,13 +77,17 @@
|
|
|
77
77
|
"ew": "Se termine par",
|
|
78
78
|
"pr": "Présent",
|
|
79
79
|
"in": "Dans la liste",
|
|
80
|
-
"nin": "Pas dans la liste"
|
|
80
|
+
"nin": "Pas dans la liste",
|
|
81
|
+
"between": "Entre"
|
|
81
82
|
},
|
|
82
83
|
"placeholders": {
|
|
83
84
|
"selectField": "Sélectionner un champ",
|
|
84
85
|
"selectOption": "Sélectionner une option",
|
|
85
86
|
"selectDate": "Sélectionner une date",
|
|
86
|
-
"enterValue": "Valeur"
|
|
87
|
+
"enterValue": "Valeur",
|
|
88
|
+
"from": "De",
|
|
89
|
+
"to": "À",
|
|
90
|
+
"commaSeparated": "Valeurs séparées par des virgules"
|
|
87
91
|
},
|
|
88
92
|
"buttons": {
|
|
89
93
|
"addCondition": "Ajouter une condition",
|
|
@@ -269,5 +273,8 @@
|
|
|
269
273
|
"addToCalendar": {
|
|
270
274
|
"addTo": "Ajouter à {name}",
|
|
271
275
|
"addToCalendar": "Ajouter au calendrier"
|
|
276
|
+
},
|
|
277
|
+
"guidedHelp": {
|
|
278
|
+
"hint": "Le guide est toujours disponible sur le point d'interrogation."
|
|
272
279
|
}
|
|
273
280
|
}
|
package/src/i18n/locales/he.json
CHANGED
|
@@ -78,13 +78,17 @@
|
|
|
78
78
|
"ew": "מסתיים ב",
|
|
79
79
|
"pr": "קיים",
|
|
80
80
|
"in": "ברשימה",
|
|
81
|
-
"nin": "לא ברשימה"
|
|
81
|
+
"nin": "לא ברשימה",
|
|
82
|
+
"between": "בין"
|
|
82
83
|
},
|
|
83
84
|
"placeholders": {
|
|
84
85
|
"selectField": "בחר שדה",
|
|
85
86
|
"selectOption": "בחירה",
|
|
86
87
|
"selectDate": "בחירת תאריך",
|
|
87
|
-
"enterValue": "ערך"
|
|
88
|
+
"enterValue": "ערך",
|
|
89
|
+
"from": "מ־",
|
|
90
|
+
"to": "עד",
|
|
91
|
+
"commaSeparated": "ערכים מופרדים בפסיקים"
|
|
88
92
|
},
|
|
89
93
|
"buttons": {
|
|
90
94
|
"addCondition": "הוספת תנאי",
|
|
@@ -517,5 +521,8 @@
|
|
|
517
521
|
"charts": {
|
|
518
522
|
"total": "סה״כ",
|
|
519
523
|
"share": "חלק"
|
|
524
|
+
},
|
|
525
|
+
"guidedHelp": {
|
|
526
|
+
"hint": "ההסבר תמיד זמין בסימן השאלה."
|
|
520
527
|
}
|
|
521
528
|
}
|