@homecode/ui 5.20.20 → 5.20.22
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/esm/index.js +2 -1
- package/dist/esm/src/components/Autocomplete/Autocomplete.js +1 -0
- package/dist/esm/src/components/FormattedText/FormattedText.js +31 -5
- package/dist/esm/src/components/FormattedText/FormattedText.styl.js +2 -2
- package/dist/esm/src/components/Img/Img.js +26 -0
- package/dist/esm/src/components/Img/Img.styl.js +20 -0
- package/dist/esm/src/components/InputFile/InputFile.js +1 -0
- package/dist/esm/src/components/PromptComposer/promptComposerPlaceholder.js +10 -0
- package/dist/esm/src/components/PromptComposer/usePromptComposerEditor.js +2 -1
- package/dist/esm/types/src/components/FormattedText/FormattedText.d.ts +2 -1
- package/dist/esm/types/src/components/FormattedText/FormattedText.types.d.ts +10 -0
- package/dist/esm/types/src/components/FormattedText/index.d.ts +2 -2
- package/dist/esm/types/src/components/Img/Img.d.ts +7 -0
- package/dist/esm/types/src/components/Img/Img.test.d.ts +1 -0
- package/dist/esm/types/src/components/Img/Img.types.d.ts +10 -0
- package/dist/esm/types/src/components/PromptComposer/promptComposerPlaceholder.d.ts +9 -0
- package/dist/esm/types/src/components/PromptComposer/promptComposerPlaceholder.test.d.ts +1 -0
- package/dist/esm/types/src/components/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -24,12 +24,13 @@ export { Flex } from './src/components/Flex/Flex.js';
|
|
|
24
24
|
export { FoldedLine } from './src/components/FoldedLine/FoldedLine.js';
|
|
25
25
|
export { FoldedLineAction, FoldedLineActionsProvider, useFoldedLineActionsRegister } from './src/components/FoldedLine/FoldedLineActionsContext.js';
|
|
26
26
|
export { Form } from './src/components/Form/Form.js';
|
|
27
|
-
export { FormattedText } from './src/components/FormattedText/FormattedText.js';
|
|
27
|
+
export { FormattedText, findJsonEndExclusive } from './src/components/FormattedText/FormattedText.js';
|
|
28
28
|
export { Gap } from './src/components/Gap/Gap.js';
|
|
29
29
|
export { Gallery, normalizeGalleryItem } from './src/components/Gallery/Gallery.js';
|
|
30
30
|
export { GridLayout } from './src/components/GridLayout/GridLayout.js';
|
|
31
31
|
export { Heading } from './src/components/Heading/Heading.js';
|
|
32
32
|
export { Icon } from './src/components/Icon/Icon.js';
|
|
33
|
+
export { Img } from './src/components/Img/Img.js';
|
|
33
34
|
export { Input } from './src/components/Input/Input.js';
|
|
34
35
|
export { InputFile } from './src/components/InputFile/InputFile.js';
|
|
35
36
|
export { Label } from './src/components/Label/Label.js';
|
|
@@ -64,6 +64,7 @@ import '../VideoPlayer/VideoPlayer.styl.js';
|
|
|
64
64
|
import '../Gallery/Gallery.styl.js';
|
|
65
65
|
import '../GridLayout/GridLayout.styl.js';
|
|
66
66
|
import '../Heading/Heading.js';
|
|
67
|
+
import '../Img/Img.styl.js';
|
|
67
68
|
import '../InputFile/InputFile.styl.js';
|
|
68
69
|
import '../InputFile/Item/Item.styl.js';
|
|
69
70
|
import '@tiptap/core';
|
|
@@ -108,7 +108,24 @@ function applyInjectors(text, injectors) {
|
|
|
108
108
|
});
|
|
109
109
|
return result;
|
|
110
110
|
}
|
|
111
|
-
function FormattedText({ text, className, onButtonClick, }) {
|
|
111
|
+
function FormattedText({ text, className, onButtonClick, quotes, injectors, breaks = true, }) {
|
|
112
|
+
// Without `<br>`s the text keeps its own newlines (pre-wrap), so a block, such
|
|
113
|
+
// as a table, swallows the blank lines after it instead of showing them.
|
|
114
|
+
const blankLinesAfter = (content, from) => breaks ? 0 : content.slice(from).match(/^\s*/)[0].length;
|
|
115
|
+
/** A run of `> ` lines, grayed with a bar down its left side. */
|
|
116
|
+
const injectQuote = (content) => {
|
|
117
|
+
const matches = content.match(/(?:^|\n)(>[^\n]*(?:\n>[^\n]*)*)(?:\n|$)/);
|
|
118
|
+
if (!matches)
|
|
119
|
+
return null;
|
|
120
|
+
const lines = matches[1]
|
|
121
|
+
.split('\n')
|
|
122
|
+
.map(line => line.replace(/^>[ \t]?/, ''));
|
|
123
|
+
return {
|
|
124
|
+
elem: (jsx("div", { className: S.quote, "data-quote": true, children: lines.join('\n') })),
|
|
125
|
+
index: matches.index,
|
|
126
|
+
length: matches[0].length,
|
|
127
|
+
};
|
|
128
|
+
};
|
|
112
129
|
const injectLines = (content) => {
|
|
113
130
|
const matches = content.match(/(^|\n)---[ \t]*(?:\n|$)/);
|
|
114
131
|
if (!matches)
|
|
@@ -266,6 +283,7 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
266
283
|
const headingTags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
267
284
|
const processCellContent = (value, depth = 0) => applyInjectors(value, [
|
|
268
285
|
injectCodeBlock,
|
|
286
|
+
...(injectors ?? []),
|
|
269
287
|
injectPrettyJson,
|
|
270
288
|
...(depth === 0
|
|
271
289
|
? [
|
|
@@ -291,7 +309,10 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
291
309
|
injectButtons,
|
|
292
310
|
]);
|
|
293
311
|
const injectHeading = (content) => {
|
|
294
|
-
|
|
312
|
+
// Without `<br>`s the text keeps its own newlines (pre-wrap), so a heading,
|
|
313
|
+
// which is a block, swallows the blank lines around it instead of leaving
|
|
314
|
+
// an extra empty line.
|
|
315
|
+
const matches = content.match(breaks ? /(^|\n)(#{1,6})\s+([^\n]+)/ : /(^|\n+)(#{1,6})[ \t]+([^\n]+)\n*/);
|
|
295
316
|
if (!matches)
|
|
296
317
|
return null;
|
|
297
318
|
const Tag = headingTags[Math.min(matches[2].length, 6) - 1];
|
|
@@ -350,13 +371,18 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
350
371
|
return {
|
|
351
372
|
elem: (jsx(Table, { size: "s", variant: "plain", columns: columns, data: tableData, className: S.table })),
|
|
352
373
|
index: matches.index + (matches[1]?.length || 0),
|
|
353
|
-
length: matches[0].length -
|
|
374
|
+
length: matches[0].length -
|
|
375
|
+
(matches[1]?.length || 0) +
|
|
376
|
+
blankLinesAfter(content, matches.index + matches[0].length),
|
|
354
377
|
};
|
|
355
378
|
};
|
|
356
379
|
return (jsx("div", { className: className, children: applyInjectors(text, [
|
|
357
380
|
injectTables,
|
|
358
381
|
injectCodeBlock,
|
|
382
|
+
...(injectors ?? []),
|
|
359
383
|
injectPrettyJson,
|
|
384
|
+
// After code, so a `> ` line inside a code block stays code.
|
|
385
|
+
...(quotes ? [injectQuote] : []),
|
|
360
386
|
injectHeading,
|
|
361
387
|
injectLines,
|
|
362
388
|
injectLinks,
|
|
@@ -365,8 +391,8 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
365
391
|
injectBullet,
|
|
366
392
|
injectNumbered,
|
|
367
393
|
injectButtons,
|
|
368
|
-
injectBreaks,
|
|
394
|
+
...(breaks ? [injectBreaks] : []),
|
|
369
395
|
]) }));
|
|
370
396
|
}
|
|
371
397
|
|
|
372
|
-
export { FormattedText, hrefForFormattedLink };
|
|
398
|
+
export { FormattedText, findJsonEndExclusive, hrefForFormattedLink };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var css_248z = ".FormattedText_bullet__vbqlq{display:inline-block;margin-bottom:2px;margin-left:var(--p-2);margin-right:2px}.FormattedText_bullet__vbqlq:before{background-color:var(--accent-color);border-radius:50%;content:\"\";display:block;height:6px;margin:0 4px;width:6px}.FormattedText_numberedIndex__rZ6Kt{font-feature-settings:\"tnum\";color:var(--accent-color);display:inline-block;font-variant-numeric:tabular-nums;margin-left:var(--p-2);margin-right:6px;min-width:1.25em}.FormattedText_line__c2bmE{background-color:var(--accent-color-alpha-200);border-radius:2px;height:2px;margin:var(--p-2) 0;width:100%}.FormattedText_codeBlock__Xrbvn,.FormattedText_codeInline__jCbNL{background-color:var(--decent-color-alpha-400);border-radius:var(--p-2);box-shadow:0 1px 1px var(--accent-color-alpha-50),inset 0 1px 1px var(--decent-color-alpha-500);font-family:monospace;-webkit-user-select:text;-moz-user-select:text;user-select:text}.FormattedText_codeInline__jCbNL{border-radius:3px;display:inline;font-size:90%;padding:0 4px}.FormattedText_codeBlock__Xrbvn,.FormattedText_jsonPre__qOGmo{display:block;font-size:90%;margin:var(--p-2) 0;overflow-x:auto;padding:var(--p-2);white-space:pre-wrap}.FormattedText_jsonPre__qOGmo{background-color:var(--decent-color-alpha-300);border-radius:var(--p-2);box-shadow:0 1px 1px var(--accent-color-alpha-50),inset 0 1px 1px var(--accent-color-alpha-50);font-family:monospace;max-width:100%;-webkit-user-select:text;-moz-user-select:text;user-select:text}.FormattedText_table__ipMMS{margin-bottom:10px}";
|
|
2
|
-
var S = {"bullet":"FormattedText_bullet__vbqlq","numberedIndex":"FormattedText_numberedIndex__rZ6Kt","line":"FormattedText_line__c2bmE","codeInline":"FormattedText_codeInline__jCbNL","codeBlock":"FormattedText_codeBlock__Xrbvn","jsonPre":"FormattedText_jsonPre__qOGmo","table":"FormattedText_table__ipMMS"};
|
|
1
|
+
var css_248z = ".FormattedText_bullet__vbqlq{display:inline-block;margin-bottom:2px;margin-left:var(--p-2);margin-right:2px}.FormattedText_bullet__vbqlq:before{background-color:var(--accent-color);border-radius:50%;content:\"\";display:block;height:6px;margin:0 4px;width:6px}.FormattedText_numberedIndex__rZ6Kt{font-feature-settings:\"tnum\";color:var(--accent-color);display:inline-block;font-variant-numeric:tabular-nums;margin-left:var(--p-2);margin-right:6px;min-width:1.25em}.FormattedText_quote__BBm-M{color:var(--accent-color-alpha-600);font-size:85%;margin:0 0 var(--p-1);padding:2px 0 2px calc(4px + var(--p-2));position:relative;white-space:pre-wrap}.FormattedText_quote__BBm-M:before{background-color:var(--accent-color);border-radius:2px;bottom:2px;content:\"\";left:2px;opacity:.3;position:absolute;top:2px;width:4px}.FormattedText_line__c2bmE{background-color:var(--accent-color-alpha-200);border-radius:2px;height:2px;margin:var(--p-2) 0;width:100%}.FormattedText_codeBlock__Xrbvn,.FormattedText_codeInline__jCbNL{background-color:var(--decent-color-alpha-400);border-radius:var(--p-2);box-shadow:0 1px 1px var(--accent-color-alpha-50),inset 0 1px 1px var(--decent-color-alpha-500);font-family:monospace;-webkit-user-select:text;-moz-user-select:text;user-select:text}.FormattedText_codeInline__jCbNL{border-radius:3px;display:inline;font-size:90%;padding:0 4px}.FormattedText_codeBlock__Xrbvn,.FormattedText_jsonPre__qOGmo{display:block;font-size:90%;margin:var(--p-2) 0;overflow-x:auto;padding:var(--p-2);white-space:pre-wrap}.FormattedText_jsonPre__qOGmo{background-color:var(--decent-color-alpha-300);border-radius:var(--p-2);box-shadow:0 1px 1px var(--accent-color-alpha-50),inset 0 1px 1px var(--accent-color-alpha-50);font-family:monospace;max-width:100%;-webkit-user-select:text;-moz-user-select:text;user-select:text}.FormattedText_table__ipMMS{margin-bottom:10px}";
|
|
2
|
+
var S = {"bullet":"FormattedText_bullet__vbqlq","numberedIndex":"FormattedText_numberedIndex__rZ6Kt","quote":"FormattedText_quote__BBm-M","line":"FormattedText_line__c2bmE","codeInline":"FormattedText_codeInline__jCbNL","codeBlock":"FormattedText_codeBlock__Xrbvn","jsonPre":"FormattedText_jsonPre__qOGmo","table":"FormattedText_table__ipMMS"};
|
|
3
3
|
if (typeof document !== 'undefined') {
|
|
4
4
|
var __uilibHead = document.head || document.getElementsByTagName('head')[0];
|
|
5
5
|
if (__uilibHead) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Icon } from '../Icon/Icon.js';
|
|
5
|
+
import S from './Img.styl.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `<img>` that never shows the browser's broken-image glyph or its alt text:
|
|
9
|
+
* when loading fails it renders `fallback` (a broken-image icon by default).
|
|
10
|
+
*/
|
|
11
|
+
function Img({ src, alt = '', fallback, fallbackClassName, className, onError, ...rest }) {
|
|
12
|
+
const [failedSrc, setFailedSrc] = useState(null);
|
|
13
|
+
// Keyed by src, so a new src gets a fresh attempt.
|
|
14
|
+
const isFailed = !src || failedSrc === src;
|
|
15
|
+
if (isFailed) {
|
|
16
|
+
if (fallback !== undefined)
|
|
17
|
+
return jsx(Fragment, { children: fallback });
|
|
18
|
+
return (jsx("span", { className: cn(S.fallback, className, fallbackClassName), role: "img", "aria-label": alt || undefined, children: jsx(Icon, { type: "brokenImage", className: S.fallbackIcon }) }));
|
|
19
|
+
}
|
|
20
|
+
return (jsx("img", { ...rest, src: src, alt: alt, className: className, onError: event => {
|
|
21
|
+
setFailedSrc(src);
|
|
22
|
+
onError?.(event);
|
|
23
|
+
} }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { Img };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var css_248z = ".Img_fallback__crzoQ{align-items:center;color:currentColor;display:inline-flex;justify-content:center}.Img_fallbackIcon__oyxkV{height:50%;max-height:48px;max-width:48px;opacity:.4;width:50%}";
|
|
2
|
+
var S = {"fallback":"Img_fallback__crzoQ","fallbackIcon":"Img_fallbackIcon__oyxkV"};
|
|
3
|
+
if (typeof document !== 'undefined') {
|
|
4
|
+
var __uilibHead = document.head || document.getElementsByTagName('head')[0];
|
|
5
|
+
if (__uilibHead) {
|
|
6
|
+
var __uilibStyle = document.createElement('style');
|
|
7
|
+
__uilibStyle.setAttribute('data-uilib-css', '');
|
|
8
|
+
__uilibStyle.appendChild(document.createTextNode(css_248z));
|
|
9
|
+
var __uilibLink = __uilibHead.querySelector('link[rel="stylesheet"]');
|
|
10
|
+
var __uilibInline = __uilibHead.querySelector('style:not([data-uilib-css])');
|
|
11
|
+
var __uilibAnchor = __uilibLink || __uilibInline;
|
|
12
|
+
if (__uilibLink && __uilibInline && (__uilibLink.compareDocumentPosition(__uilibInline) & 4) === 0) {
|
|
13
|
+
__uilibAnchor = __uilibInline;
|
|
14
|
+
}
|
|
15
|
+
if (__uilibAnchor) __uilibHead.insertBefore(__uilibStyle, __uilibAnchor);
|
|
16
|
+
else __uilibHead.appendChild(__uilibStyle);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { S as default };
|
|
@@ -66,6 +66,7 @@ import '../VideoPlayer/VideoPlayer.styl.js';
|
|
|
66
66
|
import '../Gallery/Gallery.styl.js';
|
|
67
67
|
import '../GridLayout/GridLayout.styl.js';
|
|
68
68
|
import '../Heading/Heading.js';
|
|
69
|
+
import '../Img/Img.styl.js';
|
|
69
70
|
import '../LightBox/LightBox.styl.js';
|
|
70
71
|
import '../NestedMenu/NestedMenu.js';
|
|
71
72
|
import '../Notifications/store.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder text for the composer's empty paragraphs. Only a wholly empty
|
|
3
|
+
* composer shows it: a quote or list with an empty line after it already is
|
|
4
|
+
* content, so it keeps no hint.
|
|
5
|
+
*/
|
|
6
|
+
function promptComposerPlaceholder(text) {
|
|
7
|
+
return ({ editor }) => (editor.isEmpty ? text : '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { promptComposerPlaceholder };
|
|
@@ -3,6 +3,7 @@ import { Placeholder } from '@tiptap/extensions';
|
|
|
3
3
|
import { useEditor } from '@tiptap/react';
|
|
4
4
|
import StarterKit from '@tiptap/starter-kit';
|
|
5
5
|
import S from './PromptComposer.styl.js';
|
|
6
|
+
import { promptComposerPlaceholder } from './promptComposerPlaceholder.js';
|
|
6
7
|
import { createPromptBlocksExtension, isInPromptBlock } from '../../tiptap/prompt-blocks/createPromptBlocksExtension.js';
|
|
7
8
|
import { promptBlocksDoc } from '../../tiptap/prompt-blocks/promptBlocksText.js';
|
|
8
9
|
import { createLinkHighlightExtension } from '../../tiptap/link-highlight/createLinkHighlightExtension.js';
|
|
@@ -110,7 +111,7 @@ function usePromptComposerEditor({ disabled, placeholder, slashCommandItems, sla
|
|
|
110
111
|
}),
|
|
111
112
|
NonInclusiveLink,
|
|
112
113
|
Placeholder.configure({
|
|
113
|
-
placeholder: placeholderText,
|
|
114
|
+
placeholder: promptComposerPlaceholder(placeholderText),
|
|
114
115
|
showOnlyWhenEditable: true,
|
|
115
116
|
emptyNodeClass: S.promptComposerEmptyNode,
|
|
116
117
|
}),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { FormattedTextProps } from './FormattedText.types';
|
|
3
3
|
export declare function hrefForFormattedLink(url: string): string;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function findJsonEndExclusive(content: string, start: number): number;
|
|
5
|
+
export declare function FormattedText({ text, className, onButtonClick, quotes, injectors, breaks, }: FormattedTextProps): React.JSX.Element;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export type InjectionResult = {
|
|
3
|
+
elem: React.ReactNode;
|
|
4
|
+
index: number;
|
|
5
|
+
length: number;
|
|
6
|
+
} | null;
|
|
7
|
+
export type Injector = (content: string) => InjectionResult;
|
|
1
8
|
export type FormattedTextProps = {
|
|
2
9
|
text: string;
|
|
3
10
|
className?: string;
|
|
@@ -5,4 +12,7 @@ export type FormattedTextProps = {
|
|
|
5
12
|
text: string;
|
|
6
13
|
[name: string]: string;
|
|
7
14
|
}) => void;
|
|
15
|
+
quotes?: boolean;
|
|
16
|
+
injectors?: Injector[];
|
|
17
|
+
breaks?: boolean;
|
|
8
18
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { FormattedText } from './FormattedText';
|
|
2
|
-
export type { FormattedTextProps } from './FormattedText.types';
|
|
1
|
+
export { FormattedText, findJsonEndExclusive } from './FormattedText';
|
|
2
|
+
export type { FormattedTextProps, InjectionResult, Injector, } from './FormattedText.types';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as T from './Img.types';
|
|
2
|
+
export type ImgProps = T.Props;
|
|
3
|
+
/**
|
|
4
|
+
* `<img>` that never shows the browser's broken-image glyph or its alt text:
|
|
5
|
+
* when loading fails it renders `fallback` (a broken-image icon by default).
|
|
6
|
+
*/
|
|
7
|
+
export declare function Img({ src, alt, fallback, fallbackClassName, className, onError, ...rest }: T.Props): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImgHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type Props = ImgHTMLAttributes<HTMLImageElement> & {
|
|
3
|
+
/**
|
|
4
|
+
* Rendered instead of the image when it fails to load. Defaults to a
|
|
5
|
+
* broken-image icon. Pass `null` to render nothing.
|
|
6
|
+
*/
|
|
7
|
+
fallback?: ReactNode;
|
|
8
|
+
/** Class for the default fallback wrapper. */
|
|
9
|
+
fallbackClassName?: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/core';
|
|
2
|
+
/**
|
|
3
|
+
* Placeholder text for the composer's empty paragraphs. Only a wholly empty
|
|
4
|
+
* composer shows it: a quote or list with an empty line after it already is
|
|
5
|
+
* content, so it keeps no hint.
|
|
6
|
+
*/
|
|
7
|
+
export declare function promptComposerPlaceholder(text: string): ({ editor }: {
|
|
8
|
+
editor: Editor;
|
|
9
|
+
}) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -24,6 +24,7 @@ export * from './Gallery/Gallery';
|
|
|
24
24
|
export * from './GridLayout/GridLayout';
|
|
25
25
|
export * from './Heading/Heading';
|
|
26
26
|
export * from './Icon/Icon';
|
|
27
|
+
export * from './Img/Img';
|
|
27
28
|
export * from './Input/Input';
|
|
28
29
|
export * from './InputFile/InputFile';
|
|
29
30
|
export * from './Label/Label';
|