@vaadin/field-highlighter 25.3.0-alpha8 → 25.3.0-beta1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/field-highlighter",
3
- "version": "25.3.0-alpha8",
3
+ "version": "25.3.0-beta1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,30 +32,33 @@
32
32
  "field"
33
33
  ],
34
34
  "dependencies": {
35
- "@vaadin/a11y-base": "25.3.0-alpha8",
36
- "@vaadin/component-base": "25.3.0-alpha8",
37
- "@vaadin/overlay": "25.3.0-alpha8",
38
- "@vaadin/vaadin-themable-mixin": "25.3.0-alpha8",
35
+ "@vaadin/a11y-base": "25.3.0-beta1",
36
+ "@vaadin/component-base": "25.3.0-beta1",
37
+ "@vaadin/overlay": "25.3.0-beta1",
38
+ "@vaadin/popover": "25.3.0-beta1",
39
+ "@vaadin/tooltip": "25.3.0-beta1",
40
+ "@vaadin/vaadin-themable-mixin": "25.3.0-beta1",
39
41
  "lit": "^3.0.0"
40
42
  },
41
43
  "devDependencies": {
42
- "@vaadin/aura": "25.3.0-alpha8",
43
- "@vaadin/chai-plugins": "25.3.0-alpha8",
44
- "@vaadin/checkbox": "25.3.0-alpha8",
45
- "@vaadin/checkbox-group": "25.3.0-alpha8",
46
- "@vaadin/date-picker": "25.3.0-alpha8",
47
- "@vaadin/date-time-picker": "25.3.0-alpha8",
48
- "@vaadin/item": "25.3.0-alpha8",
49
- "@vaadin/list-box": "25.3.0-alpha8",
50
- "@vaadin/radio-group": "25.3.0-alpha8",
51
- "@vaadin/select": "25.3.0-alpha8",
52
- "@vaadin/test-runner-commands": "25.3.0-alpha8",
44
+ "@vaadin/aura": "25.3.0-beta1",
45
+ "@vaadin/chai-plugins": "25.3.0-beta1",
46
+ "@vaadin/checkbox": "25.3.0-beta1",
47
+ "@vaadin/checkbox-group": "25.3.0-beta1",
48
+ "@vaadin/custom-field": "25.3.0-beta1",
49
+ "@vaadin/date-picker": "25.3.0-beta1",
50
+ "@vaadin/date-time-picker": "25.3.0-beta1",
51
+ "@vaadin/item": "25.3.0-beta1",
52
+ "@vaadin/list-box": "25.3.0-beta1",
53
+ "@vaadin/radio-group": "25.3.0-beta1",
54
+ "@vaadin/select": "25.3.0-beta1",
55
+ "@vaadin/test-runner-commands": "25.3.0-beta1",
53
56
  "@vaadin/testing-helpers": "^2.0.0",
54
- "@vaadin/text-area": "25.3.0-alpha8",
55
- "@vaadin/text-field": "25.3.0-alpha8",
56
- "@vaadin/time-picker": "25.3.0-alpha8",
57
- "@vaadin/vaadin-lumo-styles": "25.3.0-alpha8",
57
+ "@vaadin/text-area": "25.3.0-beta1",
58
+ "@vaadin/text-field": "25.3.0-beta1",
59
+ "@vaadin/time-picker": "25.3.0-beta1",
60
+ "@vaadin/vaadin-lumo-styles": "25.3.0-beta1",
58
61
  "sinon": "^22.0.0"
59
62
  },
60
- "gitHead": "ccbb4aaffb63c745c6da0426b532d4d05e47af29"
63
+ "gitHead": "295432e44a6967e1aff36462b2e3e1d0e64eb8cc"
61
64
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 - 2026 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { CSSResult } from 'lit';
7
+
8
+ export const aiFieldMarkerStyles: CSSResult;
9
+
10
+ export const aiFieldMarkerHostStyles: CSSResult;
@@ -0,0 +1,279 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 - 2026 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { css } from 'lit';
7
+ import { screenReaderOnly } from '@vaadin/a11y-base/src/styles/sr-only-styles.js';
8
+
9
+ export const aiFieldMarkerHostStyles = css`
10
+ @keyframes --vaadin-ai-field-marker-slide {
11
+ 0% {
12
+ --vaadin-ai-field-marker-mask-pos: -100px;
13
+ }
14
+
15
+ 100% {
16
+ --vaadin-ai-field-marker-mask-pos: calc(100% + 100px);
17
+ }
18
+ }
19
+
20
+ @keyframes --vaadin-ai-field-marker-remove-mask {
21
+ 100% {
22
+ mask-image: none;
23
+ }
24
+ }
25
+ `;
26
+
27
+ /**
28
+ * Styles applied to the `<vaadin-ai-field-marker>` element itself: the badge
29
+ * button anchored to the field's top corner and the popover content.
30
+ */
31
+ export const aiFieldMarkerStyles = css`
32
+ :has(> vaadin-ai-field-marker) {
33
+ position: relative;
34
+ }
35
+
36
+ /* --vaadin-ai-field-marker-mask-pos is registered from JS: an @property rule only
37
+ takes effect at document scope, and this sheet is injected into the field's
38
+ root node, which is a shadow root for a nested field. */
39
+
40
+ vaadin-ai-field-marker[hidden] {
41
+ display: none !important;
42
+ }
43
+
44
+ :where(vaadin-ai-field-marker) {
45
+ display: contents;
46
+ --_vaadin-ai-field-marker-color-1: light-dark(#932fffd9, #bc64ff);
47
+ --_vaadin-ai-field-marker-color-2: light-dark(#004cffcc, #539aff);
48
+
49
+ &::before {
50
+ content: '';
51
+ position: absolute;
52
+ inset: -6px;
53
+ border-radius: 9px;
54
+ z-index: -1;
55
+ pointer-events: none;
56
+ background-color: color-mix(in srgb, var(--_vaadin-ai-field-marker-color-2) 30%, transparent);
57
+ background-image:
58
+ radial-gradient(66.92% 123.25% at 100% 88.78%, var(--_vaadin-ai-field-marker-color-1) 0%, transparent 100%),
59
+ radial-gradient(42.57% 69.91% at 14.85% 33.33%, var(--_vaadin-ai-field-marker-color-2) 0%, transparent 100%);
60
+ opacity: 0.16;
61
+ mask-image: linear-gradient(
62
+ 90deg,
63
+ #000,
64
+ #000 var(--vaadin-ai-field-marker-mask-pos),
65
+ transparent calc(var(--vaadin-ai-field-marker-mask-pos) + 20px),
66
+ transparent
67
+ );
68
+ animation: --vaadin-ai-field-marker-slide 700ms 200ms both;
69
+ animation-timing-function: cubic-bezier(0.78, 0, 0.22, 1);
70
+ }
71
+
72
+ .badge {
73
+ all: initial;
74
+ position: absolute;
75
+ top: -6px;
76
+ inset-inline-end: -6px;
77
+ box-sizing: border-box;
78
+ display: inline-flex;
79
+ align-items: center;
80
+ padding: 4px;
81
+ border: none;
82
+ border-radius: var(--vaadin-radius-m);
83
+ margin: 0;
84
+ background: transparent;
85
+ color: var(--vaadin-ai-field-marker-badge-icon-color, var(--vaadin-text-color-secondary));
86
+ font: inherit;
87
+ font-size: 1rem;
88
+ line-height: 1;
89
+ cursor: pointer;
90
+ transition: color 200ms;
91
+ animation: --vaadin-ai-field-marker-fade-in 300ms 700ms backwards;
92
+
93
+ &:hover {
94
+ color: var(--vaadin-ai-field-marker-badge-icon-color, var(--vaadin-text-color));
95
+ }
96
+
97
+ &::before {
98
+ content: '';
99
+ display: block;
100
+ width: 1lh;
101
+ height: 1lh;
102
+ background: currentColor;
103
+ --_icon-ai-badge: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none"><path d="M7.18848 8.48926H5.46289L6.32715 5.8623L7.18848 8.48926Z" fill="black"/><path fill-rule="evenodd" clip-rule="evenodd" d="M11 0C13.7614 0 16 2.23858 16 5V11C16 13.7614 13.7614 16 11 16H5C2.23858 16 0 13.7614 0 11V5C0 2.23858 2.23858 0 5 0H11ZM5.58203 4.52051L3.25977 11H4.63672L5.10742 9.56934H7.54297L8.01172 11H9.45215L7.12988 4.52051H5.58203ZM10.1211 4.52051V11H11.5068V4.52051H10.1211Z" fill="black"/></svg>');
104
+ mask-image: var(--_icon-ai-badge);
105
+ }
106
+
107
+ &::after {
108
+ content: '';
109
+ position: absolute;
110
+ width: 24px;
111
+ height: 24px;
112
+ top: 50%;
113
+ left: 50%;
114
+ translate: -50% -50%;
115
+ }
116
+ }
117
+
118
+ .badge:focus-visible {
119
+ outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
120
+ }
121
+
122
+ > vaadin-popover::part(content) {
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: var(--vaadin-gap-s);
126
+ padding: var(--vaadin-padding-m);
127
+ max-width: 20em;
128
+ }
129
+
130
+ .message {
131
+ margin: 0;
132
+ }
133
+
134
+ /* The hidden AI-fill description linked to the field's input via
135
+ aria-describedby: visually hidden but kept in the accessibility tree,
136
+ using the sr-only styles from @vaadin/a11y-base. */
137
+ ${screenReaderOnly}
138
+
139
+ .actions {
140
+ display: flex;
141
+ gap: var(--vaadin-gap-xs);
142
+ }
143
+
144
+ .actions > button {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: var(--vaadin-gap-s);
148
+ pointer-events: auto;
149
+ box-sizing: border-box;
150
+ padding: var(--vaadin-padding-block-container) var(--vaadin-padding-inline-container);
151
+ margin: calc(var(--vaadin-padding-block-container) * -1) calc(var(--vaadin-padding-inline-container) * -1);
152
+ border: 0;
153
+ border-radius: var(--vaadin-radius-m);
154
+ background: transparent;
155
+ color: var(--vaadin-text-color);
156
+ font: inherit;
157
+ font-weight: 500;
158
+ cursor: pointer;
159
+ transition: background-color 100ms;
160
+
161
+ &:hover {
162
+ background: var(--vaadin-background-container);
163
+ }
164
+
165
+ &:active {
166
+ background: var(--vaadin-background-container-strong);
167
+ }
168
+
169
+ &:focus-visible {
170
+ outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
171
+ }
172
+
173
+ &::before {
174
+ content: '';
175
+ display: inline-block;
176
+ width: 1em;
177
+ height: 1em;
178
+ background: currentColor;
179
+ mask: var(--_vaadin-icon-undo);
180
+ }
181
+ }
182
+ }
183
+
184
+ /* The confidence indicator: a sibling of the marker slotted into the
185
+ field's helper text section. The level class name picks the color and how
186
+ much of the pie icon is filled. */
187
+ :has(> vaadin-ai-field-marker) > [slot='helper'].ai-confidence {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: var(--vaadin-gap-s);
191
+ color: var(--_vaadin-ai-field-marker-confidence-color);
192
+
193
+ &::before {
194
+ content: '';
195
+ flex: none;
196
+ box-sizing: border-box;
197
+ width: var(--vaadin-icon-size, 1lh);
198
+ height: var(--vaadin-icon-size, 1lh);
199
+ /* The padding insets the pie from the ring: the wedge is clipped to the
200
+ content box, while the tint fills the whole circle behind it. */
201
+ padding: calc(var(--vaadin-icon-size, 1lh) / 12);
202
+ border: 1px solid color-mix(in srgb, currentColor 50%, transparent);
203
+ border-radius: 50%;
204
+ background-color: color-mix(in srgb, currentColor 15%, transparent);
205
+ background-image: conic-gradient(currentColor var(--_vaadin-ai-field-marker-confidence-fill, 0%), #0000 0%);
206
+ background-clip: content-box, border-box;
207
+ }
208
+
209
+ /* The levels are told apart by how much of the pie is filled, so the base
210
+ styles keep them in the same neutral color. The themes map the custom
211
+ properties to their own semantic colors. */
212
+ &.ai-confidence-low {
213
+ --_vaadin-ai-field-marker-confidence-color: var(
214
+ --vaadin-ai-field-marker-confidence-low-color,
215
+ var(--vaadin-text-color-secondary)
216
+ );
217
+ --_vaadin-ai-field-marker-confidence-fill: 25%;
218
+ }
219
+
220
+ &.ai-confidence-medium {
221
+ --_vaadin-ai-field-marker-confidence-color: var(
222
+ --vaadin-ai-field-marker-confidence-medium-color,
223
+ var(--vaadin-text-color-secondary)
224
+ );
225
+ --_vaadin-ai-field-marker-confidence-fill: 50%;
226
+ }
227
+
228
+ &.ai-confidence-high {
229
+ --_vaadin-ai-field-marker-confidence-color: var(
230
+ --vaadin-ai-field-marker-confidence-high-color,
231
+ var(--vaadin-text-color-secondary)
232
+ );
233
+ --_vaadin-ai-field-marker-confidence-fill: 75%;
234
+ }
235
+ }
236
+
237
+ /* While the AI is working, the confidence describes a value that is about
238
+ to be replaced, so hide it along with the marker. */
239
+ [ai-working] > [slot='helper'].ai-confidence {
240
+ display: none;
241
+ }
242
+
243
+ [ai-working],
244
+ :has(> vaadin-ai-field-marker)::part(input-field),
245
+ :has(> vaadin-ai-field-marker)::part(input-fields),
246
+ :has(> vaadin-ai-field-marker)::part(group-field) {
247
+ mask-image: linear-gradient(
248
+ 90deg,
249
+ #000 calc(var(--vaadin-ai-field-marker-mask-pos) - 100px),
250
+ rgba(0, 0, 0, 0.3) calc(var(--vaadin-ai-field-marker-mask-pos) - 70px),
251
+ rgba(0, 0, 0, 0.3) var(--vaadin-ai-field-marker-mask-pos),
252
+ #000 calc(var(--vaadin-ai-field-marker-mask-pos) + 70px),
253
+ #000 calc(var(--vaadin-ai-field-marker-mask-pos) + 100px)
254
+ );
255
+ animation:
256
+ --vaadin-ai-field-marker-slide 1s cubic-bezier(0.78, 0, 0.22, 1) forwards,
257
+ --vaadin-ai-field-marker-remove-mask 0s 1s forwards;
258
+ }
259
+
260
+ [ai-working] {
261
+ animation: --vaadin-ai-field-marker-slide 1s ease-in-out infinite;
262
+ }
263
+
264
+ /* While the AI is working, the badge and glow describe a value that is about
265
+ to be replaced, so hide the marker until the field leaves the working
266
+ state. Hiding rather than unmarking keeps the previous mark for a fill that
267
+ is cancelled or fails. */
268
+ [ai-working] > vaadin-ai-field-marker {
269
+ display: none;
270
+ }
271
+
272
+ @keyframes --vaadin-ai-field-marker-fade-in {
273
+ 0% {
274
+ opacity: 0;
275
+ }
276
+ }
277
+
278
+ ${aiFieldMarkerHostStyles}
279
+ `;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 - 2026 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
7
+
8
+ /**
9
+ * Localization texts for {@link AiFieldMarker}.
10
+ */
11
+ export interface AiFieldMarkerI18n {
12
+ /**
13
+ * The message shown in the popover explaining the AI fill.
14
+ */
15
+ message?: string;
16
+
17
+ /**
18
+ * The label of the revert control.
19
+ */
20
+ revert?: string;
21
+
22
+ /**
23
+ * The accessible label of the badge button and the popover dialog.
24
+ */
25
+ badgeLabel?: string;
26
+
27
+ /**
28
+ * The tooltip text of the badge button.
29
+ */
30
+ badgeTooltip?: string;
31
+
32
+ /**
33
+ * The texts of the confidence indicator.
34
+ */
35
+ confidence?: {
36
+ low?: string;
37
+ medium?: string;
38
+ high?: string;
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Fired from the field element when the user activates the revert control.
44
+ * The host is expected to restore the field's previous value.
45
+ *
46
+ * The event bubbles from the field rather than from the marker, so it is not
47
+ * declared on a listener map: listen for it on the field or a container, and
48
+ * annotate the listener with this type.
49
+ */
50
+ export type AiFieldRevertEvent = CustomEvent<{ value: unknown }>;
51
+
52
+ /**
53
+ * An element used internally by Vaadin. Not intended to be used separately.
54
+ *
55
+ * Annotates a field as AI-filled: appended as a direct child of the field,
56
+ * it slots itself into the field via a slot injected into the field's shadow
57
+ * root, draws an "AI" badge anchored to the field, and offers a popover that
58
+ * explains the AI fill and lets the user revert the value.
59
+ *
60
+ * The marker manages the annotation through its own lifecycle: adding it to
61
+ * the field marks the field, removing it clears the mark. While an AI fill is
62
+ * in progress, set the `working` property to show an "AI is working" shimmer
63
+ * on the field along with a client-side read-only guard.
64
+ *
65
+ * The popover can show custom content — such as a summary of what the AI
66
+ * based the value on — below the explanation, given as a DOM node through
67
+ * the `content` property.
68
+ *
69
+ * Set the `confidence` property to show the confidence level of the filled
70
+ * value (`low`, `medium` or `high`) as an indicator in the field's helper
71
+ * text section, ahead of a helper the field itself may have. While the
72
+ * indicator is shown, the field is marked with `has-helper`, so that the
73
+ * helper text section is laid out the same as for a helper of its own.
74
+ *
75
+ * ### Styling
76
+ *
77
+ * The following state attributes are set on the field element for styling:
78
+ *
79
+ * Attribute | Description
80
+ * ----------------|-------------
81
+ * `ai-working` | Set while an AI is working on the field.
82
+ *
83
+ * The confidence indicator is rendered into the field's light DOM as a
84
+ * `<span>` with the `ai-confidence` class name and the level as an additional
85
+ * `ai-confidence-low`, `ai-confidence-medium` or `ai-confidence-high` one.
86
+ *
87
+ * The following custom CSS properties are available for styling:
88
+ *
89
+ * Custom CSS property |
90
+ * :----------------------------------------------------|
91
+ * `--vaadin-ai-field-marker-badge-icon-color` |
92
+ * `--vaadin-ai-field-marker-confidence-high-color` |
93
+ * `--vaadin-ai-field-marker-confidence-low-color` |
94
+ * `--vaadin-ai-field-marker-confidence-medium-color` |
95
+ *
96
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
97
+ *
98
+ * @fires {CustomEvent} ai-field-revert - Fired from the field element when the user activates the revert control. The host restores the value.
99
+ */
100
+ declare class AiFieldMarker extends I18nMixin<typeof HTMLElement, AiFieldMarkerI18n>(HTMLElement) {
101
+ /**
102
+ * The object used to localize this component. To change the default
103
+ * localization, replace this with an object that provides all properties, or
104
+ * just the individual properties you want to change.
105
+ *
106
+ * The object has the following JSON structure and default values:
107
+ *
108
+ * ```
109
+ * {
110
+ * // The message shown in the popover explaining the AI fill.
111
+ * message: 'This field value was modified by AI.',
112
+ * // The label of the revert control.
113
+ * revert: 'Revert Value',
114
+ * // The accessible label of the badge button and the popover dialog.
115
+ * badgeLabel: 'AI-provided value',
116
+ * // The tooltip text of the badge button.
117
+ * badgeTooltip: 'Field value modified by AI.\nClick for details',
118
+ * // The texts of the confidence indicator.
119
+ * confidence: {
120
+ * low: 'Low confidence',
121
+ * medium: 'Medium confidence',
122
+ * high: 'High confidence'
123
+ * }
124
+ * }
125
+ * ```
126
+ */
127
+ i18n: AiFieldMarkerI18n;
128
+
129
+ /**
130
+ * A DOM node to show in the popover, between the message and the revert
131
+ * control — for example a summary of what the AI based the value on.
132
+ *
133
+ * The node is rendered as given, and moved into the marker's own light DOM.
134
+ * The host owns it: setting the property to another node or to `null`
135
+ * removes the previous node from the popover.
136
+ *
137
+ * ```js
138
+ * const source = document.createElement('a');
139
+ * source.href = '/documents/invoice.pdf';
140
+ * source.textContent = 'invoice.pdf';
141
+ * marker.content = source;
142
+ * ```
143
+ */
144
+ content: Node | null | undefined;
145
+
146
+ /**
147
+ * Whether an AI is currently working on the field. While `true`, the field
148
+ * shows an "AI is working" shimmer and is made read-only on the client so
149
+ * the user cannot edit a value the AI is about to overwrite; only the
150
+ * client-side `readonly` state is touched, and setting the property back to
151
+ * `false` restores it. The marker badge is hidden for the duration, since
152
+ * the value it annotates is about to be replaced. For assistive technology,
153
+ * the field is marked with `aria-busy`.
154
+ */
155
+ working: boolean;
156
+
157
+ /**
158
+ * The confidence level of the AI-filled value, shown as an indicator
159
+ * in the field's helper text section. Possible values are `low`,
160
+ * `medium` and `high`; when not set, no indicator is shown. The
161
+ * indicator texts can be localized with the `i18n` property.
162
+ */
163
+ confidence: 'high' | 'low' | 'medium' | null;
164
+ }
165
+
166
+ declare global {
167
+ interface HTMLElementTagNameMap {
168
+ 'vaadin-ai-field-marker': AiFieldMarker;
169
+ }
170
+ }
171
+
172
+ export { AiFieldMarker };