@ng-native/components 0.13.0 → 0.15.0
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/keyboard-avoiding-view.js +3 -2
- package/dist/keyboard-avoiding-view.js.map +1 -1
- package/dist/markdown-blocks.d.ts +18 -1
- package/dist/markdown-blocks.js +112 -4
- package/dist/markdown-blocks.js.map +1 -1
- package/dist/markdown-columns.d.ts +24 -0
- package/dist/markdown-columns.js +58 -0
- package/dist/markdown-columns.js.map +1 -0
- package/dist/markdown-stream.d.ts +31 -0
- package/dist/markdown-stream.js +282 -0
- package/dist/markdown-stream.js.map +1 -0
- package/dist/markdown.d.ts +30 -3
- package/dist/markdown.js +131 -41
- package/dist/markdown.js.map +1 -1
- package/dist/modal.d.ts +8 -2
- package/dist/modal.js +8 -5
- package/dist/modal.js.map +1 -1
- package/dist/pressable.js +6 -6
- package/dist/pressable.js.map +1 -1
- package/dist/section-list.js +4 -4
- package/dist/section-list.js.map +1 -1
- package/dist/swipe-refresh-layout.js +1 -1
- package/dist/swipe-refresh-layout.js.map +1 -1
- package/dist/text-input.d.ts +6 -1
- package/dist/text-input.js +8 -2
- package/dist/text-input.js.map +1 -1
- package/dist/touchable-opacity.js +3 -2
- package/dist/touchable-opacity.js.map +1 -1
- package/dist/transforms.d.ts +6 -0
- package/dist/transforms.js +8 -0
- package/dist/transforms.js.map +1 -1
- package/dist/view-base.d.ts +1 -1
- package/dist/view-base.js +6 -4
- package/dist/view-base.js.map +1 -1
- package/dist/virtual-list.js +6 -5
- package/dist/virtual-list.js.map +1 -1
- package/dist/written-props.js +1 -0
- package/dist/written-props.js.map +1 -1
- package/dist/zero-size-warning.js +2 -2
- package/dist/zero-size-warning.js.map +1 -1
- package/package.json +3 -3
package/dist/markdown.js
CHANGED
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
* import { Markdown } from '@ng-native/components/markdown';
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
|
-
import { Component, Injector, computed, inject, input, linkedSignal, output } from '@angular/core';
|
|
12
|
+
import { Component, Injector, booleanAttribute, computed, inject, input, linkedSignal, output, } from '@angular/core';
|
|
13
13
|
import { DeepLinks } from '@ng-native/device';
|
|
14
14
|
import { nativePlatform } from '@ng-native/fabric';
|
|
15
15
|
import { lexer } from 'marked';
|
|
16
16
|
import { Image } from "./image.js";
|
|
17
|
-
import { markdownBlocks, opensOnPress, } from "./markdown-blocks.js";
|
|
17
|
+
import { blockKey, markdownBlocks, opensOnPress, shareBlocks, } from "./markdown-blocks.js";
|
|
18
|
+
import { MarkdownColumns } from "./markdown-columns.js";
|
|
19
|
+
import { MarkdownStream } from "./markdown-stream.js";
|
|
18
20
|
import { ScrollView } from "./scroll-view.js";
|
|
19
21
|
import { TemplateSlot } from "./template-slot.js";
|
|
20
22
|
import { Text } from "./text.js";
|
|
@@ -56,6 +58,15 @@ function imageSources(blocks, into = new Set()) {
|
|
|
56
58
|
* dark. `classes` replaces one by its element's name, so a global stylesheet or Tailwind styles it
|
|
57
59
|
* instead: `{ h1: 'text-3xl font-bold' }`. Put the default back beside your own to keep both.
|
|
58
60
|
*
|
|
61
|
+
* For a source that grows as it streams in, an answer arriving a few words at a time, set
|
|
62
|
+
* `streaming` until it is complete. The blocks before the last one are then kept as they are and
|
|
63
|
+
* only the last is lexed and converted again, unless a chunk defines or changes a link reference,
|
|
64
|
+
* which can change a block already kept, and the whole source is lexed again. Syntax still open at its end, a code span, an emphasis, a link,
|
|
65
|
+
* an image or a table row, is held back until it closes. Of a code fence only an unfinished
|
|
66
|
+
* opening line is held back, and its code then streams as it arrives. Clearing `streaming` draws
|
|
67
|
+
* the whole source as written, and `tokens` are always drawn as given. A block that did not
|
|
68
|
+
* change keeps its views, streaming or not.
|
|
69
|
+
*
|
|
59
70
|
* A pressed link is `(linkPress)`. An absolute target is then opened with `DeepLinks.open` unless
|
|
60
71
|
* a handler calls `preventDefault()`; a relative one is only reported, for the app to route.
|
|
61
72
|
*/
|
|
@@ -69,13 +80,36 @@ export class Markdown {
|
|
|
69
80
|
/** Tokens `marked.lexer` made already, drawn in place of `source`. */
|
|
70
81
|
tokens = input(/* @ts-ignore */
|
|
71
82
|
...(ngDevMode ? [undefined, { debugName: "tokens" }] : /* istanbul ignore next */ []));
|
|
83
|
+
/**
|
|
84
|
+
* Whether `source` is still arriving, a chunk at a time. While it is, only its last block is
|
|
85
|
+
* lexed again, unless a link reference changes, and syntax still open at its end is held back.
|
|
86
|
+
* See the class note.
|
|
87
|
+
*/
|
|
88
|
+
streaming = input(false, { ...(ngDevMode ? { debugName: "streaming" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
72
89
|
/** A class for an element, in place of its default. See the class note. */
|
|
73
90
|
classes = input({}, /* @ts-ignore */
|
|
74
91
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
75
92
|
/** A link was pressed. See the class note. */
|
|
76
93
|
linkPress = output();
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
stream = new MarkdownStream();
|
|
95
|
+
blockKey = blockKey;
|
|
96
|
+
/**
|
|
97
|
+
* The blocks each token the stream kept was drawn as. A kept token is the same object from one
|
|
98
|
+
* chunk to the next, so its blocks are not converted or compared again; a source lexed whole
|
|
99
|
+
* makes new tokens, and so new blocks.
|
|
100
|
+
*/
|
|
101
|
+
streamed = new WeakMap();
|
|
102
|
+
/** The document's blocks, with each one that did not change kept, so it is not drawn again. */
|
|
103
|
+
blocks = linkedSignal({ ...(ngDevMode ? { debugName: "blocks" } : /* istanbul ignore next */ {}), source: () => {
|
|
104
|
+
const tokens = this.tokens();
|
|
105
|
+
if (tokens)
|
|
106
|
+
return { tokens, streamed: false };
|
|
107
|
+
const streamed = this.streaming();
|
|
108
|
+
return { tokens: this.lex(this.source() ?? '', streamed), streamed };
|
|
109
|
+
},
|
|
110
|
+
computation: ({ tokens, streamed }, previous) => streamed
|
|
111
|
+
? this.streamedBlocks(tokens, previous?.value)
|
|
112
|
+
: shareBlocks(previous?.value, markdownBlocks(tokens)) });
|
|
79
113
|
classOf = computed(() => ({ ...DEFAULT_CLASSES, ...this.classes() }), /* @ts-ignore */
|
|
80
114
|
...(ngDevMode ? [{ debugName: "classOf" }] : /* istanbul ignore next */ []));
|
|
81
115
|
/**
|
|
@@ -93,6 +127,32 @@ export class Markdown {
|
|
|
93
127
|
kept.set(src, ratio);
|
|
94
128
|
return kept;
|
|
95
129
|
} });
|
|
130
|
+
lex(source, streaming) {
|
|
131
|
+
return streaming ? this.stream.lex(source) : lexer(source);
|
|
132
|
+
}
|
|
133
|
+
/** `tokens`' blocks, converting and sharing only those of tokens the stream had not kept. */
|
|
134
|
+
streamedBlocks(tokens, previous) {
|
|
135
|
+
const kept = [];
|
|
136
|
+
let at = 0;
|
|
137
|
+
for (; at < tokens.length; at++) {
|
|
138
|
+
const blocks = this.streamed.get(tokens[at]);
|
|
139
|
+
if (!blocks)
|
|
140
|
+
break;
|
|
141
|
+
kept.push(...blocks);
|
|
142
|
+
}
|
|
143
|
+
const rest = tokens.slice(at);
|
|
144
|
+
const converted = rest.map((token) => markdownBlocks([token]));
|
|
145
|
+
// The blocks drawn after the kept ones, when those are still the ones drawn first.
|
|
146
|
+
const drawn = !kept.length || previous?.[kept.length - 1] === kept.at(-1);
|
|
147
|
+
const was = drawn ? previous?.slice(kept.length) : undefined;
|
|
148
|
+
const shared = shareBlocks(was, converted.flat());
|
|
149
|
+
let from = 0;
|
|
150
|
+
rest.forEach((token, i) => {
|
|
151
|
+
const count = converted[i].length;
|
|
152
|
+
this.streamed.set(token, shared.slice(from, (from += count)));
|
|
153
|
+
});
|
|
154
|
+
return [...kept, ...shared];
|
|
155
|
+
}
|
|
96
156
|
classFor(element) {
|
|
97
157
|
return this.classOf()[element];
|
|
98
158
|
}
|
|
@@ -118,9 +178,9 @@ export class Markdown {
|
|
|
118
178
|
this.injector.get(DeepLinks).open(link.href);
|
|
119
179
|
}
|
|
120
180
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Markdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
121
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: Markdown, isStandalone: true, selector: "markdown", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, tokens: { classPropertyName: "tokens", publicName: "tokens", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkPress: "linkPress" }, host: { properties: { "class.md-ios": "platform === 'ios'", "class.md-android": "platform === 'android'" } }, ngImport: i0, template: `
|
|
181
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: Markdown, isStandalone: true, selector: "markdown", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, tokens: { classPropertyName: "tokens", publicName: "tokens", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkPress: "linkPress" }, host: { properties: { "class.md-ios": "platform === 'ios'", "class.md-android": "platform === 'android'" } }, ngImport: i0, template: `
|
|
122
182
|
<ng-template #blockTemplate let-list>
|
|
123
|
-
@for (block of list; track
|
|
183
|
+
@for (block of list; track blockKey(block)) {
|
|
124
184
|
@switch (block.kind) {
|
|
125
185
|
@case ('heading') {
|
|
126
186
|
<text [class]="classFor(block.element)" accessibilityRole="header"
|
|
@@ -171,24 +231,39 @@ export class Markdown {
|
|
|
171
231
|
<view [class]="classFor('hr')"></view>
|
|
172
232
|
}
|
|
173
233
|
@case ('table') {
|
|
174
|
-
<view
|
|
175
|
-
<view
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
</view>
|
|
182
|
-
@for (row of block.rows; track $index) {
|
|
234
|
+
<scroll-view horizontal>
|
|
235
|
+
<view
|
|
236
|
+
[class]="classFor('table')"
|
|
237
|
+
[markdownColumns]="block"
|
|
238
|
+
#columns="markdownColumns"
|
|
239
|
+
>
|
|
183
240
|
<view [class]="classFor('tr')">
|
|
184
|
-
@for (cell of
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
241
|
+
@for (cell of block.header; track $index; let column = $index) {
|
|
242
|
+
<view class="md-cell" [style.minWidth]="columns.width(column)">
|
|
243
|
+
<text
|
|
244
|
+
[class]="classFor('th')"
|
|
245
|
+
(layout)="columns.measure(0, column, $any($event))"
|
|
246
|
+
><ng-container *templateSlot="inlineTemplate; context: { $implicit: cell }"
|
|
247
|
+
/></text>
|
|
248
|
+
</view>
|
|
188
249
|
}
|
|
189
250
|
</view>
|
|
190
|
-
|
|
191
|
-
|
|
251
|
+
@for (row of block.rows; track $index; let r = $index) {
|
|
252
|
+
<view [class]="classFor('tr')">
|
|
253
|
+
@for (cell of row; track $index; let column = $index) {
|
|
254
|
+
<view class="md-cell md-cell-body" [style.minWidth]="columns.width(column)">
|
|
255
|
+
<text
|
|
256
|
+
[class]="classFor('td')"
|
|
257
|
+
(layout)="columns.measure(r + 1, column, $any($event))"
|
|
258
|
+
><ng-container
|
|
259
|
+
*templateSlot="inlineTemplate; context: { $implicit: cell }"
|
|
260
|
+
/></text>
|
|
261
|
+
</view>
|
|
262
|
+
}
|
|
263
|
+
</view>
|
|
264
|
+
}
|
|
265
|
+
</view>
|
|
266
|
+
</scroll-view>
|
|
192
267
|
}
|
|
193
268
|
}
|
|
194
269
|
}
|
|
@@ -222,13 +297,13 @@ export class Markdown {
|
|
|
222
297
|
</ng-template>
|
|
223
298
|
|
|
224
299
|
<ng-container *templateSlot="blockTemplate; context: { $implicit: blocks() }" />
|
|
225
|
-
`, isInline: true, styles: ["/* What a document looks like with no `classes` of the app's, in light and in dark. */\n\n:host {\n gap: 12px;\n color: #1c1c1e;\n}\n\n.md-h1,\n.md-h2,\n.md-h3,\n.md-h4,\n.md-h5,\n.md-h6 {\n font-weight: 700;\n}\n\n.md-h1 {\n font-size: 28px;\n line-height: 34px;\n}\n\n.md-h2 {\n font-size: 22px;\n line-height: 28px;\n}\n\n.md-h3 {\n font-size: 20px;\n line-height: 25px;\n}\n\n.md-h4 {\n font-size: 17px;\n line-height: 22px;\n}\n\n.md-h5 {\n font-size: 15px;\n line-height: 20px;\n}\n\n.md-h6 {\n font-size: 13px;\n line-height: 18px;\n color: #6c6c70;\n}\n\n.md-p,\n.md-marker,\n.md-th,\n.md-td {\n font-size: 16px;\n line-height: 23px;\n}\n\n.md-strong {\n font-weight: 700;\n}\n\n.md-em {\n font-style: italic;\n}\n\n.md-del {\n text-decoration-line: line-through;\n}\n\n.md-a {\n color: #0a66d8;\n text-decoration-line: underline;\n}\n\n.md-code {\n background-color: rgba(120, 120, 128, 0.16);\n}\n\n.md-code,\n.md-pre {\n font-family: Courier New;\n}\n\n:host(.md-ios) .md-code,\n:host(.md-ios) .md-pre {\n font-family: Menlo;\n}\n\n:host(.md-android) .md-code,\n:host(.md-android) .md-pre {\n font-family: monospace;\n}\n\n.md-blockquote {\n gap: 8px;\n padding-left: 12px;\n border-left-width: 3px;\n border-left-color: #d1d1d6;\n color: #6c6c70;\n}\n\n.md-ul,\n.md-ol {\n gap: 4px;\n}\n\n.md-li {\n flex-direction: row;\n gap: 8px;\n}\n\n.md-marker {\n min-width: 16px;\n}\n\n.md-li-body {\n flex: 1;\n gap: 4px;\n}\n\n.md-pre {\n flex-grow: 0;\n border-radius: 8px;\n background-color: #f2f2f7;\n}\n\n.md-pre-text {\n padding: 12px;\n font-size: 14px;\n line-height: 20px;\n}\n\n.md-hr {\n height: 1px;\n background-color: #d1d1d6;\n}\n\n.md-img {\n width: 100%;\n height: 200px;\n border-radius: 8px;\n}\n\n.md-table {\n border-width: 1px;\n border-color: #d1d1d6;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.md-tr {\n flex-direction: row;\n}\n\n.md-th,\n.md-td {\n
|
|
300
|
+
`, isInline: true, styles: ["/* What a document looks like with no `classes` of the app's, in light and in dark. */\n\n:host {\n gap: 12px;\n color: #1c1c1e;\n}\n\n.md-h1,\n.md-h2,\n.md-h3,\n.md-h4,\n.md-h5,\n.md-h6 {\n font-weight: 700;\n}\n\n.md-h1 {\n font-size: 28px;\n line-height: 34px;\n}\n\n.md-h2 {\n font-size: 22px;\n line-height: 28px;\n}\n\n.md-h3 {\n font-size: 20px;\n line-height: 25px;\n}\n\n.md-h4 {\n font-size: 17px;\n line-height: 22px;\n}\n\n.md-h5 {\n font-size: 15px;\n line-height: 20px;\n}\n\n.md-h6 {\n font-size: 13px;\n line-height: 18px;\n color: #6c6c70;\n}\n\n.md-p,\n.md-marker,\n.md-th,\n.md-td {\n font-size: 16px;\n line-height: 23px;\n}\n\n.md-strong {\n font-weight: 700;\n}\n\n.md-em {\n font-style: italic;\n}\n\n.md-del {\n text-decoration-line: line-through;\n}\n\n.md-a {\n color: #0a66d8;\n text-decoration-line: underline;\n}\n\n.md-code {\n background-color: rgba(120, 120, 128, 0.16);\n}\n\n.md-code,\n.md-pre {\n font-family: Courier New;\n}\n\n:host(.md-ios) .md-code,\n:host(.md-ios) .md-pre {\n font-family: Menlo;\n}\n\n:host(.md-android) .md-code,\n:host(.md-android) .md-pre {\n font-family: monospace;\n}\n\n.md-blockquote {\n gap: 8px;\n padding-left: 12px;\n border-left-width: 3px;\n border-left-color: #d1d1d6;\n color: #6c6c70;\n}\n\n.md-ul,\n.md-ol {\n gap: 4px;\n}\n\n.md-li {\n flex-direction: row;\n gap: 8px;\n}\n\n.md-marker {\n min-width: 16px;\n}\n\n.md-li-body {\n flex: 1;\n gap: 4px;\n}\n\n.md-pre {\n flex-grow: 0;\n border-radius: 8px;\n background-color: #f2f2f7;\n}\n\n.md-pre-text {\n padding: 12px;\n font-size: 14px;\n line-height: 20px;\n}\n\n.md-hr {\n height: 1px;\n background-color: #d1d1d6;\n}\n\n.md-img {\n width: 100%;\n height: 200px;\n border-radius: 8px;\n}\n\n.md-table {\n border-width: 1px;\n border-color: #d1d1d6;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.md-tr {\n flex-direction: row;\n}\n\n/* The text hugs its content inside the column's least width, so it can report a narrower one. */\n.md-cell {\n align-items: flex-start;\n}\n\n.md-th,\n.md-td {\n max-width: 280px;\n padding: 6px 8px;\n}\n\n.md-cell-body {\n border-top-width: 1px;\n border-top-color: #d1d1d6;\n}\n\n.md-th {\n font-weight: 600;\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n color: #f2f2f7;\n }\n\n .md-h6,\n .md-blockquote {\n color: #98989f;\n }\n\n .md-a {\n color: #4c9bff;\n }\n\n .md-code {\n background-color: rgba(120, 120, 128, 0.32);\n }\n\n .md-pre {\n background-color: #1c1c1e;\n }\n\n .md-blockquote,\n .md-table,\n .md-cell-body {\n border-color: #38383a;\n }\n\n .md-hr {\n background-color: #38383a;\n }\n}\n"], dependencies: [{ kind: "directive", type: Image, selector: "image", inputs: ["source", "src", "srcSet", "defaultSource", "loadingIndicatorSource", "resizeMode", "resizeMethod", "resizeMultiplier", "blurRadius", "capInsets", "tintColor", "fadeDuration", "progressiveRenderingEnabled", "alt", "crossOrigin", "referrerPolicy"] }, { kind: "directive", type: MarkdownColumns, selector: "[markdownColumns]", inputs: ["markdownColumns"], exportAs: ["markdownColumns"] }, { kind: "component", type: ScrollView, selector: "scroll-view", inputs: ["contentContainerStyle", "contentContainerClass", "horizontal", "contentOffset", "maintainVisibleContentPosition", "keyboardShouldPersistTaps", "nestedScrollEnabled", "stickyHeaderIndices"], outputs: ["contentSizeChange"], exportAs: ["scrollView"] }, { kind: "directive", type: TemplateSlot, selector: "[templateSlot]", inputs: ["templateSlot", "templateSlotContext"] }, { kind: "directive", type: Text, selector: "text", inputs: ["numberOfLines", "ellipsizeMode", "selectable", "selectionColor", "allowFontScaling", "maxFontSizeMultiplier", "dynamicTypeRamp", "adjustsFontSizeToFit", "minimumFontScale", "lineBreakStrategyIOS", "textBreakStrategy", "dataDetectorType", "android_hyphenationFrequency", "pressable", "suppressHighlighting"], exportAs: ["text"] }, { kind: "directive", type: View, selector: "view" }] });
|
|
226
301
|
}
|
|
227
302
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Markdown, decorators: [{
|
|
228
303
|
type: Component,
|
|
229
|
-
args: [{ selector: 'markdown', imports: [Image, ScrollView, TemplateSlot, Text, View], template: `
|
|
304
|
+
args: [{ selector: 'markdown', imports: [Image, MarkdownColumns, ScrollView, TemplateSlot, Text, View], template: `
|
|
230
305
|
<ng-template #blockTemplate let-list>
|
|
231
|
-
@for (block of list; track
|
|
306
|
+
@for (block of list; track blockKey(block)) {
|
|
232
307
|
@switch (block.kind) {
|
|
233
308
|
@case ('heading') {
|
|
234
309
|
<text [class]="classFor(block.element)" accessibilityRole="header"
|
|
@@ -279,24 +354,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
279
354
|
<view [class]="classFor('hr')"></view>
|
|
280
355
|
}
|
|
281
356
|
@case ('table') {
|
|
282
|
-
<view
|
|
283
|
-
<view
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
</view>
|
|
290
|
-
@for (row of block.rows; track $index) {
|
|
357
|
+
<scroll-view horizontal>
|
|
358
|
+
<view
|
|
359
|
+
[class]="classFor('table')"
|
|
360
|
+
[markdownColumns]="block"
|
|
361
|
+
#columns="markdownColumns"
|
|
362
|
+
>
|
|
291
363
|
<view [class]="classFor('tr')">
|
|
292
|
-
@for (cell of
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
364
|
+
@for (cell of block.header; track $index; let column = $index) {
|
|
365
|
+
<view class="md-cell" [style.minWidth]="columns.width(column)">
|
|
366
|
+
<text
|
|
367
|
+
[class]="classFor('th')"
|
|
368
|
+
(layout)="columns.measure(0, column, $any($event))"
|
|
369
|
+
><ng-container *templateSlot="inlineTemplate; context: { $implicit: cell }"
|
|
370
|
+
/></text>
|
|
371
|
+
</view>
|
|
296
372
|
}
|
|
297
373
|
</view>
|
|
298
|
-
|
|
299
|
-
|
|
374
|
+
@for (row of block.rows; track $index; let r = $index) {
|
|
375
|
+
<view [class]="classFor('tr')">
|
|
376
|
+
@for (cell of row; track $index; let column = $index) {
|
|
377
|
+
<view class="md-cell md-cell-body" [style.minWidth]="columns.width(column)">
|
|
378
|
+
<text
|
|
379
|
+
[class]="classFor('td')"
|
|
380
|
+
(layout)="columns.measure(r + 1, column, $any($event))"
|
|
381
|
+
><ng-container
|
|
382
|
+
*templateSlot="inlineTemplate; context: { $implicit: cell }"
|
|
383
|
+
/></text>
|
|
384
|
+
</view>
|
|
385
|
+
}
|
|
386
|
+
</view>
|
|
387
|
+
}
|
|
388
|
+
</view>
|
|
389
|
+
</scroll-view>
|
|
300
390
|
}
|
|
301
391
|
}
|
|
302
392
|
}
|
|
@@ -333,6 +423,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
333
423
|
`, host: {
|
|
334
424
|
'[class.md-ios]': "platform === 'ios'",
|
|
335
425
|
'[class.md-android]': "platform === 'android'",
|
|
336
|
-
}, styles: ["/* What a document looks like with no `classes` of the app's, in light and in dark. */\n\n:host {\n gap: 12px;\n color: #1c1c1e;\n}\n\n.md-h1,\n.md-h2,\n.md-h3,\n.md-h4,\n.md-h5,\n.md-h6 {\n font-weight: 700;\n}\n\n.md-h1 {\n font-size: 28px;\n line-height: 34px;\n}\n\n.md-h2 {\n font-size: 22px;\n line-height: 28px;\n}\n\n.md-h3 {\n font-size: 20px;\n line-height: 25px;\n}\n\n.md-h4 {\n font-size: 17px;\n line-height: 22px;\n}\n\n.md-h5 {\n font-size: 15px;\n line-height: 20px;\n}\n\n.md-h6 {\n font-size: 13px;\n line-height: 18px;\n color: #6c6c70;\n}\n\n.md-p,\n.md-marker,\n.md-th,\n.md-td {\n font-size: 16px;\n line-height: 23px;\n}\n\n.md-strong {\n font-weight: 700;\n}\n\n.md-em {\n font-style: italic;\n}\n\n.md-del {\n text-decoration-line: line-through;\n}\n\n.md-a {\n color: #0a66d8;\n text-decoration-line: underline;\n}\n\n.md-code {\n background-color: rgba(120, 120, 128, 0.16);\n}\n\n.md-code,\n.md-pre {\n font-family: Courier New;\n}\n\n:host(.md-ios) .md-code,\n:host(.md-ios) .md-pre {\n font-family: Menlo;\n}\n\n:host(.md-android) .md-code,\n:host(.md-android) .md-pre {\n font-family: monospace;\n}\n\n.md-blockquote {\n gap: 8px;\n padding-left: 12px;\n border-left-width: 3px;\n border-left-color: #d1d1d6;\n color: #6c6c70;\n}\n\n.md-ul,\n.md-ol {\n gap: 4px;\n}\n\n.md-li {\n flex-direction: row;\n gap: 8px;\n}\n\n.md-marker {\n min-width: 16px;\n}\n\n.md-li-body {\n flex: 1;\n gap: 4px;\n}\n\n.md-pre {\n flex-grow: 0;\n border-radius: 8px;\n background-color: #f2f2f7;\n}\n\n.md-pre-text {\n padding: 12px;\n font-size: 14px;\n line-height: 20px;\n}\n\n.md-hr {\n height: 1px;\n background-color: #d1d1d6;\n}\n\n.md-img {\n width: 100%;\n height: 200px;\n border-radius: 8px;\n}\n\n.md-table {\n border-width: 1px;\n border-color: #d1d1d6;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.md-tr {\n flex-direction: row;\n}\n\n.md-th,\n.md-td {\n
|
|
337
|
-
}], propDecorators: { source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], tokens: [{ type: i0.Input, args: [{ isSignal: true, alias: "tokens", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], linkPress: [{ type: i0.Output, args: ["linkPress"] }] } });
|
|
426
|
+
}, styles: ["/* What a document looks like with no `classes` of the app's, in light and in dark. */\n\n:host {\n gap: 12px;\n color: #1c1c1e;\n}\n\n.md-h1,\n.md-h2,\n.md-h3,\n.md-h4,\n.md-h5,\n.md-h6 {\n font-weight: 700;\n}\n\n.md-h1 {\n font-size: 28px;\n line-height: 34px;\n}\n\n.md-h2 {\n font-size: 22px;\n line-height: 28px;\n}\n\n.md-h3 {\n font-size: 20px;\n line-height: 25px;\n}\n\n.md-h4 {\n font-size: 17px;\n line-height: 22px;\n}\n\n.md-h5 {\n font-size: 15px;\n line-height: 20px;\n}\n\n.md-h6 {\n font-size: 13px;\n line-height: 18px;\n color: #6c6c70;\n}\n\n.md-p,\n.md-marker,\n.md-th,\n.md-td {\n font-size: 16px;\n line-height: 23px;\n}\n\n.md-strong {\n font-weight: 700;\n}\n\n.md-em {\n font-style: italic;\n}\n\n.md-del {\n text-decoration-line: line-through;\n}\n\n.md-a {\n color: #0a66d8;\n text-decoration-line: underline;\n}\n\n.md-code {\n background-color: rgba(120, 120, 128, 0.16);\n}\n\n.md-code,\n.md-pre {\n font-family: Courier New;\n}\n\n:host(.md-ios) .md-code,\n:host(.md-ios) .md-pre {\n font-family: Menlo;\n}\n\n:host(.md-android) .md-code,\n:host(.md-android) .md-pre {\n font-family: monospace;\n}\n\n.md-blockquote {\n gap: 8px;\n padding-left: 12px;\n border-left-width: 3px;\n border-left-color: #d1d1d6;\n color: #6c6c70;\n}\n\n.md-ul,\n.md-ol {\n gap: 4px;\n}\n\n.md-li {\n flex-direction: row;\n gap: 8px;\n}\n\n.md-marker {\n min-width: 16px;\n}\n\n.md-li-body {\n flex: 1;\n gap: 4px;\n}\n\n.md-pre {\n flex-grow: 0;\n border-radius: 8px;\n background-color: #f2f2f7;\n}\n\n.md-pre-text {\n padding: 12px;\n font-size: 14px;\n line-height: 20px;\n}\n\n.md-hr {\n height: 1px;\n background-color: #d1d1d6;\n}\n\n.md-img {\n width: 100%;\n height: 200px;\n border-radius: 8px;\n}\n\n.md-table {\n border-width: 1px;\n border-color: #d1d1d6;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.md-tr {\n flex-direction: row;\n}\n\n/* The text hugs its content inside the column's least width, so it can report a narrower one. */\n.md-cell {\n align-items: flex-start;\n}\n\n.md-th,\n.md-td {\n max-width: 280px;\n padding: 6px 8px;\n}\n\n.md-cell-body {\n border-top-width: 1px;\n border-top-color: #d1d1d6;\n}\n\n.md-th {\n font-weight: 600;\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n color: #f2f2f7;\n }\n\n .md-h6,\n .md-blockquote {\n color: #98989f;\n }\n\n .md-a {\n color: #4c9bff;\n }\n\n .md-code {\n background-color: rgba(120, 120, 128, 0.32);\n }\n\n .md-pre {\n background-color: #1c1c1e;\n }\n\n .md-blockquote,\n .md-table,\n .md-cell-body {\n border-color: #38383a;\n }\n\n .md-hr {\n background-color: #38383a;\n }\n}\n"] }]
|
|
427
|
+
}], propDecorators: { source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], tokens: [{ type: i0.Input, args: [{ isSignal: true, alias: "tokens", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], linkPress: [{ type: i0.Output, args: ["linkPress"] }] } });
|
|
338
428
|
//# sourceMappingURL=markdown.js.map
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Markdown, drawn as native views and text.\n *\n * Its own entry point because it parses with `marked`, an optional peer dependency an app installs\n * only when it renders Markdown: the barrel never imports it, so an app that does not is not asked\n * for it.\n *\n * ```ts\n * import { Markdown } from '@ng-native/components/markdown';\n * ```\n */\nimport { Component, Injector, computed, inject, input, linkedSignal, output } from '@angular/core';\nimport { DeepLinks } from '@ng-native/device';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { lexer, type Token } from 'marked';\nimport type { ImageLoadEvent } from './events.ts';\nimport { Image } from './image.ts';\nimport {\n markdownBlocks,\n opensOnPress,\n type MarkdownBlock,\n type MarkdownElement,\n type MarkdownInline,\n} from './markdown-blocks.ts';\nimport { ScrollView } from './scroll-view.ts';\nimport { TemplateSlot } from './template-slot.ts';\nimport { Text } from './text.ts';\nimport { View } from './view.ts';\n\nexport type { MarkdownElement } from './markdown-blocks.ts';\n\n/** Classes for the elements a document is drawn with, each one in place of the default. */\nexport type MarkdownClasses = Partial<Readonly<Record<MarkdownElement, string>>>;\n\n/** `(linkPress)`: a link was pressed. */\nexport interface MarkdownLinkPress {\n /** The target as written, after its entities are decoded and its scheme was allowed. */\n readonly href: string;\n readonly title: string | null;\n /** Keep the link from being opened, to handle it in the app instead. */\n preventDefault(): void;\n}\n\nconst ELEMENTS: readonly MarkdownElement[] = [\n ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'ul', 'ol', 'li', 'marker'],\n ...['pre', 'code', 'strong', 'em', 'del', 'a', 'hr', 'img', 'table', 'tr', 'th', 'td'],\n] as MarkdownElement[];\n\nconst DEFAULT_CLASSES = Object.fromEntries(\n ELEMENTS.map((element) => [element, `md-${element}`]),\n) as Readonly<Record<MarkdownElement, string>>;\n\ntype MarkdownLink = Extract<MarkdownInline, { kind: 'link' }>;\n\n/** Every image address in `blocks`, however deep in quotes and lists. */\nfunction imageSources(blocks: readonly MarkdownBlock[], into = new Set<string>()): Set<string> {\n for (const block of blocks) {\n if (block.kind === 'image') into.add(block.src);\n else if (block.kind === 'blockquote') imageSources(block.children, into);\n else if (block.kind === 'list') {\n for (const item of block.items) imageSources(item.children, into);\n }\n }\n return into;\n}\n\n/**\n * A Markdown document as `<text>`, `<view>`, `<image>` and `<scroll-view>`: headings, paragraphs,\n * emphasis, inline code, links, quotes, ordered, unordered and task lists, code blocks, rules,\n * tables and images.\n *\n * `source` is parsed with `marked.lexer`; `tokens` takes tokens lexed already, and wins when both\n * are set. A paragraph is one `<text>` with a nested `<text>` for each emphasis, code span and\n * link, so it wraps as one run; an image is lifted out to a block of its own.\n *\n * Nothing in the document is interpreted. Raw HTML is drawn as the text it is written as, a link\n * is followed only to `http`, `https`, `mailto`, `tel` or a relative target, and an image is\n * loaded only over `http` or `https`; anything else is drawn as its text.\n *\n * Each element carries a default class, `md-h1`, `md-p`, `md-a` and so on, styled in light and\n * dark. `classes` replaces one by its element's name, so a global stylesheet or Tailwind styles it\n * instead: `{ h1: 'text-3xl font-bold' }`. Put the default back beside your own to keep both.\n *\n * A pressed link is `(linkPress)`. An absolute target is then opened with `DeepLinks.open` unless\n * a handler calls `preventDefault()`; a relative one is only reported, for the app to route.\n */\n@Component({\n selector: 'markdown',\n imports: [Image, ScrollView, TemplateSlot, Text, View],\n template: `\n <ng-template #blockTemplate let-list>\n @for (block of list; track $index) {\n @switch (block.kind) {\n @case ('heading') {\n <text [class]=\"classFor(block.element)\" accessibilityRole=\"header\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: block.children }\"\n /></text>\n }\n @case ('paragraph') {\n <text [class]=\"classFor('p')\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: block.children }\"\n /></text>\n }\n @case ('image') {\n <image\n [src]=\"block.src\"\n [alt]=\"block.alt || undefined\"\n resizeMode=\"contain\"\n [class]=\"classFor('img')\"\n [style.aspectRatio]=\"ratios().get(block.src) ?? null\"\n [style.height]=\"ratios().has(block.src) ? 'auto' : null\"\n (load)=\"sizeImage(block.src, $any($event))\"\n />\n }\n @case ('blockquote') {\n <view [class]=\"classFor('blockquote')\">\n <ng-container *templateSlot=\"blockTemplate; context: { $implicit: block.children }\" />\n </view>\n }\n @case ('list') {\n <view [class]=\"classFor(block.element)\">\n @for (item of block.items; track $index) {\n <view [class]=\"classFor('li')\">\n <text [class]=\"classFor('marker')\">{{ item.marker }}</text>\n <view class=\"md-li-body\">\n <ng-container\n *templateSlot=\"blockTemplate; context: { $implicit: item.children }\"\n />\n </view>\n </view>\n }\n </view>\n }\n @case ('code') {\n <scroll-view horizontal [class]=\"classFor('pre')\">\n <text selectable class=\"md-pre-text\">{{ block.text }}</text>\n </scroll-view>\n }\n @case ('hr') {\n <view [class]=\"classFor('hr')\"></view>\n }\n @case ('table') {\n <view [class]=\"classFor('table')\">\n <view [class]=\"classFor('tr')\">\n @for (cell of block.header; track $index) {\n <text [class]=\"classFor('th')\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: cell }\"\n /></text>\n }\n </view>\n @for (row of block.rows; track $index) {\n <view [class]=\"classFor('tr')\">\n @for (cell of row; track $index) {\n <text [class]=\"classFor('td')\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: cell }\"\n /></text>\n }\n </view>\n }\n </view>\n }\n }\n }\n </ng-template>\n\n <ng-template #inlineTemplate let-runs>\n @for (run of runs; track $index) {\n @switch (run.kind) {\n @case ('text') {\n <ng-container>{{ run.text }}</ng-container>\n }\n @case ('code') {\n <text [class]=\"classFor('code')\">{{ run.text }}</text>\n }\n @case ('link') {\n <text\n [class]=\"classFor('a')\"\n pressable\n [accessibilityHint]=\"run.title ?? undefined\"\n (press)=\"followLink(run)\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: run.children }\"\n /></text>\n }\n @default {\n <text [class]=\"classFor(run.kind)\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: run.children }\"\n /></text>\n }\n }\n }\n </ng-template>\n\n <ng-container *templateSlot=\"blockTemplate; context: { $implicit: blocks() }\" />\n `,\n styleUrl: './markdown.css',\n host: {\n '[class.md-ios]': \"platform === 'ios'\",\n '[class.md-android]': \"platform === 'android'\",\n },\n})\nexport class Markdown {\n private readonly injector = inject(Injector);\n /** Which monospace font code is drawn in, as the platform names it. */\n protected readonly platform = nativePlatform();\n\n /** The Markdown to draw. */\n readonly source = input<string>();\n /** Tokens `marked.lexer` made already, drawn in place of `source`. */\n readonly tokens = input<readonly Token[]>();\n /** A class for an element, in place of its default. See the class note. */\n readonly classes = input<MarkdownClasses>({});\n /** A link was pressed. See the class note. */\n readonly linkPress = output<MarkdownLinkPress>();\n\n protected readonly blocks = computed(() =>\n markdownBlocks(this.tokens() ?? lexer(this.source() ?? '')),\n );\n\n private readonly classOf = computed(() => ({ ...DEFAULT_CLASSES, ...this.classes() }));\n\n /**\n * Each image's width over its height, once it has loaded, so it is drawn at the page's width and\n * its own shape. An image the document no longer has is forgotten when the document changes.\n */\n protected readonly ratios = linkedSignal<readonly MarkdownBlock[], ReadonlyMap<string, number>>({\n source: this.blocks,\n computation: (blocks, previous) => {\n const kept = new Map<string, number>();\n if (!previous) return kept;\n const present = imageSources(blocks);\n for (const [src, ratio] of previous.value) if (present.has(src)) kept.set(src, ratio);\n return kept;\n },\n });\n\n protected classFor(element: MarkdownElement): string {\n return this.classOf()[element];\n }\n\n protected sizeImage(src: string, event: Partial<ImageLoadEvent>): void {\n const source = event.nativeEvent?.source;\n if (!source) return;\n const { width, height } = source;\n if (!(width > 0 && height > 0)) return;\n this.ratios.update((ratios) => new Map(ratios).set(src, width / height));\n }\n\n protected followLink(link: MarkdownLink): void {\n let prevented = false;\n this.linkPress.emit({\n href: link.href,\n title: link.title,\n preventDefault: () => {\n prevented = true;\n },\n });\n if (!prevented && opensOnPress(link.href)) this.injector.get(DeepLinks).open(link.href);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Markdown, drawn as native views and text.\n *\n * Its own entry point because it parses with `marked`, an optional peer dependency an app installs\n * only when it renders Markdown: the barrel never imports it, so an app that does not is not asked\n * for it.\n *\n * ```ts\n * import { Markdown } from '@ng-native/components/markdown';\n * ```\n */\nimport {\n Component,\n Injector,\n booleanAttribute,\n computed,\n inject,\n input,\n linkedSignal,\n output,\n} from '@angular/core';\nimport { DeepLinks } from '@ng-native/device';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { lexer, type Token } from 'marked';\nimport type { ImageLoadEvent } from './events.ts';\nimport { Image } from './image.ts';\nimport {\n blockKey,\n markdownBlocks,\n opensOnPress,\n shareBlocks,\n type MarkdownBlock,\n type MarkdownElement,\n type MarkdownInline,\n} from './markdown-blocks.ts';\nimport { MarkdownColumns } from './markdown-columns.ts';\nimport { MarkdownStream } from './markdown-stream.ts';\nimport { ScrollView } from './scroll-view.ts';\nimport { TemplateSlot } from './template-slot.ts';\nimport { Text } from './text.ts';\nimport { View } from './view.ts';\n\nexport type { MarkdownElement } from './markdown-blocks.ts';\n\n/** Classes for the elements a document is drawn with, each one in place of the default. */\nexport type MarkdownClasses = Partial<Readonly<Record<MarkdownElement, string>>>;\n\n/** `(linkPress)`: a link was pressed. */\nexport interface MarkdownLinkPress {\n /** The target as written, after its entities are decoded and its scheme was allowed. */\n readonly href: string;\n readonly title: string | null;\n /** Keep the link from being opened, to handle it in the app instead. */\n preventDefault(): void;\n}\n\nconst ELEMENTS: readonly MarkdownElement[] = [\n ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'ul', 'ol', 'li', 'marker'],\n ...['pre', 'code', 'strong', 'em', 'del', 'a', 'hr', 'img', 'table', 'tr', 'th', 'td'],\n] as MarkdownElement[];\n\nconst DEFAULT_CLASSES = Object.fromEntries(\n ELEMENTS.map((element) => [element, `md-${element}`]),\n) as Readonly<Record<MarkdownElement, string>>;\n\ntype MarkdownLink = Extract<MarkdownInline, { kind: 'link' }>;\n\n/** Every image address in `blocks`, however deep in quotes and lists. */\nfunction imageSources(blocks: readonly MarkdownBlock[], into = new Set<string>()): Set<string> {\n for (const block of blocks) {\n if (block.kind === 'image') into.add(block.src);\n else if (block.kind === 'blockquote') imageSources(block.children, into);\n else if (block.kind === 'list') {\n for (const item of block.items) imageSources(item.children, into);\n }\n }\n return into;\n}\n\n/**\n * A Markdown document as `<text>`, `<view>`, `<image>` and `<scroll-view>`: headings, paragraphs,\n * emphasis, inline code, links, quotes, ordered, unordered and task lists, code blocks, rules,\n * tables and images.\n *\n * `source` is parsed with `marked.lexer`; `tokens` takes tokens lexed already, and wins when both\n * are set. A paragraph is one `<text>` with a nested `<text>` for each emphasis, code span and\n * link, so it wraps as one run; an image is lifted out to a block of its own.\n *\n * Nothing in the document is interpreted. Raw HTML is drawn as the text it is written as, a link\n * is followed only to `http`, `https`, `mailto`, `tel` or a relative target, and an image is\n * loaded only over `http` or `https`; anything else is drawn as its text.\n *\n * Each element carries a default class, `md-h1`, `md-p`, `md-a` and so on, styled in light and\n * dark. `classes` replaces one by its element's name, so a global stylesheet or Tailwind styles it\n * instead: `{ h1: 'text-3xl font-bold' }`. Put the default back beside your own to keep both.\n *\n * For a source that grows as it streams in, an answer arriving a few words at a time, set\n * `streaming` until it is complete. The blocks before the last one are then kept as they are and\n * only the last is lexed and converted again, unless a chunk defines or changes a link reference,\n * which can change a block already kept, and the whole source is lexed again. Syntax still open at its end, a code span, an emphasis, a link,\n * an image or a table row, is held back until it closes. Of a code fence only an unfinished\n * opening line is held back, and its code then streams as it arrives. Clearing `streaming` draws\n * the whole source as written, and `tokens` are always drawn as given. A block that did not\n * change keeps its views, streaming or not.\n *\n * A pressed link is `(linkPress)`. An absolute target is then opened with `DeepLinks.open` unless\n * a handler calls `preventDefault()`; a relative one is only reported, for the app to route.\n */\n@Component({\n selector: 'markdown',\n imports: [Image, MarkdownColumns, ScrollView, TemplateSlot, Text, View],\n template: `\n <ng-template #blockTemplate let-list>\n @for (block of list; track blockKey(block)) {\n @switch (block.kind) {\n @case ('heading') {\n <text [class]=\"classFor(block.element)\" accessibilityRole=\"header\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: block.children }\"\n /></text>\n }\n @case ('paragraph') {\n <text [class]=\"classFor('p')\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: block.children }\"\n /></text>\n }\n @case ('image') {\n <image\n [src]=\"block.src\"\n [alt]=\"block.alt || undefined\"\n resizeMode=\"contain\"\n [class]=\"classFor('img')\"\n [style.aspectRatio]=\"ratios().get(block.src) ?? null\"\n [style.height]=\"ratios().has(block.src) ? 'auto' : null\"\n (load)=\"sizeImage(block.src, $any($event))\"\n />\n }\n @case ('blockquote') {\n <view [class]=\"classFor('blockquote')\">\n <ng-container *templateSlot=\"blockTemplate; context: { $implicit: block.children }\" />\n </view>\n }\n @case ('list') {\n <view [class]=\"classFor(block.element)\">\n @for (item of block.items; track $index) {\n <view [class]=\"classFor('li')\">\n <text [class]=\"classFor('marker')\">{{ item.marker }}</text>\n <view class=\"md-li-body\">\n <ng-container\n *templateSlot=\"blockTemplate; context: { $implicit: item.children }\"\n />\n </view>\n </view>\n }\n </view>\n }\n @case ('code') {\n <scroll-view horizontal [class]=\"classFor('pre')\">\n <text selectable class=\"md-pre-text\">{{ block.text }}</text>\n </scroll-view>\n }\n @case ('hr') {\n <view [class]=\"classFor('hr')\"></view>\n }\n @case ('table') {\n <scroll-view horizontal>\n <view\n [class]=\"classFor('table')\"\n [markdownColumns]=\"block\"\n #columns=\"markdownColumns\"\n >\n <view [class]=\"classFor('tr')\">\n @for (cell of block.header; track $index; let column = $index) {\n <view class=\"md-cell\" [style.minWidth]=\"columns.width(column)\">\n <text\n [class]=\"classFor('th')\"\n (layout)=\"columns.measure(0, column, $any($event))\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: cell }\"\n /></text>\n </view>\n }\n </view>\n @for (row of block.rows; track $index; let r = $index) {\n <view [class]=\"classFor('tr')\">\n @for (cell of row; track $index; let column = $index) {\n <view class=\"md-cell md-cell-body\" [style.minWidth]=\"columns.width(column)\">\n <text\n [class]=\"classFor('td')\"\n (layout)=\"columns.measure(r + 1, column, $any($event))\"\n ><ng-container\n *templateSlot=\"inlineTemplate; context: { $implicit: cell }\"\n /></text>\n </view>\n }\n </view>\n }\n </view>\n </scroll-view>\n }\n }\n }\n </ng-template>\n\n <ng-template #inlineTemplate let-runs>\n @for (run of runs; track $index) {\n @switch (run.kind) {\n @case ('text') {\n <ng-container>{{ run.text }}</ng-container>\n }\n @case ('code') {\n <text [class]=\"classFor('code')\">{{ run.text }}</text>\n }\n @case ('link') {\n <text\n [class]=\"classFor('a')\"\n pressable\n [accessibilityHint]=\"run.title ?? undefined\"\n (press)=\"followLink(run)\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: run.children }\"\n /></text>\n }\n @default {\n <text [class]=\"classFor(run.kind)\"\n ><ng-container *templateSlot=\"inlineTemplate; context: { $implicit: run.children }\"\n /></text>\n }\n }\n }\n </ng-template>\n\n <ng-container *templateSlot=\"blockTemplate; context: { $implicit: blocks() }\" />\n `,\n styleUrl: './markdown.css',\n host: {\n '[class.md-ios]': \"platform === 'ios'\",\n '[class.md-android]': \"platform === 'android'\",\n },\n})\nexport class Markdown {\n private readonly injector = inject(Injector);\n /** Which monospace font code is drawn in, as the platform names it. */\n protected readonly platform = nativePlatform();\n\n /** The Markdown to draw. */\n readonly source = input<string>();\n /** Tokens `marked.lexer` made already, drawn in place of `source`. */\n readonly tokens = input<readonly Token[]>();\n /**\n * Whether `source` is still arriving, a chunk at a time. While it is, only its last block is\n * lexed again, unless a link reference changes, and syntax still open at its end is held back.\n * See the class note.\n */\n readonly streaming = input(false, { transform: booleanAttribute });\n /** A class for an element, in place of its default. See the class note. */\n readonly classes = input<MarkdownClasses>({});\n /** A link was pressed. See the class note. */\n readonly linkPress = output<MarkdownLinkPress>();\n\n private readonly stream = new MarkdownStream();\n protected readonly blockKey = blockKey;\n\n /**\n * The blocks each token the stream kept was drawn as. A kept token is the same object from one\n * chunk to the next, so its blocks are not converted or compared again; a source lexed whole\n * makes new tokens, and so new blocks.\n */\n private readonly streamed = new WeakMap<Token, readonly MarkdownBlock[]>();\n\n /** The document's blocks, with each one that did not change kept, so it is not drawn again. */\n protected readonly blocks = linkedSignal({\n source: () => {\n const tokens = this.tokens();\n if (tokens) return { tokens, streamed: false };\n const streamed = this.streaming();\n return { tokens: this.lex(this.source() ?? '', streamed), streamed };\n },\n computation: ({ tokens, streamed }, previous?: { value: readonly MarkdownBlock[] }) =>\n streamed\n ? this.streamedBlocks(tokens, previous?.value)\n : shareBlocks(previous?.value, markdownBlocks(tokens)),\n });\n\n private readonly classOf = computed(() => ({ ...DEFAULT_CLASSES, ...this.classes() }));\n\n /**\n * Each image's width over its height, once it has loaded, so it is drawn at the page's width and\n * its own shape. An image the document no longer has is forgotten when the document changes.\n */\n protected readonly ratios = linkedSignal<readonly MarkdownBlock[], ReadonlyMap<string, number>>({\n source: this.blocks,\n computation: (blocks, previous) => {\n const kept = new Map<string, number>();\n if (!previous) return kept;\n const present = imageSources(blocks);\n for (const [src, ratio] of previous.value) if (present.has(src)) kept.set(src, ratio);\n return kept;\n },\n });\n\n private lex(source: string, streaming: boolean): readonly Token[] {\n return streaming ? this.stream.lex(source) : lexer(source);\n }\n\n /** `tokens`' blocks, converting and sharing only those of tokens the stream had not kept. */\n private streamedBlocks(\n tokens: readonly Token[],\n previous: readonly MarkdownBlock[] | undefined,\n ): readonly MarkdownBlock[] {\n const kept: MarkdownBlock[] = [];\n let at = 0;\n for (; at < tokens.length; at++) {\n const blocks = this.streamed.get(tokens[at]!);\n if (!blocks) break;\n kept.push(...blocks);\n }\n const rest = tokens.slice(at);\n const converted = rest.map((token) => markdownBlocks([token]));\n // The blocks drawn after the kept ones, when those are still the ones drawn first.\n const drawn = !kept.length || previous?.[kept.length - 1] === kept.at(-1);\n const was = drawn ? previous?.slice(kept.length) : undefined;\n const shared = shareBlocks(was, converted.flat());\n let from = 0;\n rest.forEach((token, i) => {\n const count = converted[i]!.length;\n this.streamed.set(token, shared.slice(from, (from += count)));\n });\n return [...kept, ...shared];\n }\n\n protected classFor(element: MarkdownElement): string {\n return this.classOf()[element];\n }\n\n protected sizeImage(src: string, event: Partial<ImageLoadEvent>): void {\n const source = event.nativeEvent?.source;\n if (!source) return;\n const { width, height } = source;\n if (!(width > 0 && height > 0)) return;\n this.ratios.update((ratios) => new Map(ratios).set(src, width / height));\n }\n\n protected followLink(link: MarkdownLink): void {\n let prevented = false;\n this.linkPress.emit({\n href: link.href,\n title: link.title,\n preventDefault: () => {\n prevented = true;\n },\n });\n if (!prevented && opensOnPress(link.href)) this.injector.get(DeepLinks).open(link.href);\n }\n}\n"]}
|
package/dist/modal.d.ts
CHANGED
|
@@ -36,9 +36,15 @@ export declare class Modal extends ViewBase {
|
|
|
36
36
|
readonly supportedOrientations: import("@angular/core").InputSignal<readonly ModalOrientation[] | undefined>;
|
|
37
37
|
/** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */
|
|
38
38
|
readonly allowSwipeDismissal: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where
|
|
41
|
+
* React Native ignores `false`.
|
|
42
|
+
*/
|
|
40
43
|
readonly statusBarTranslucent: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
41
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),
|
|
46
|
+
* where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.
|
|
47
|
+
*/
|
|
42
48
|
readonly navigationBarTranslucent: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
43
49
|
/** Android: give the modal's window a hardware-accelerated surface. */
|
|
44
50
|
readonly hardwareAccelerated: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
package/dist/modal.js
CHANGED
|
@@ -43,9 +43,15 @@ export class Modal extends ViewBase {
|
|
|
43
43
|
...(ngDevMode ? [undefined, { debugName: "supportedOrientations" }] : /* istanbul ignore next */ []));
|
|
44
44
|
/** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */
|
|
45
45
|
allowSwipeDismissal = input(undefined, { ...(ngDevMode ? { debugName: "allowSwipeDismissal" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where
|
|
48
|
+
* React Native ignores `false`.
|
|
49
|
+
*/
|
|
47
50
|
statusBarTranslucent = input(undefined, { ...(ngDevMode ? { debugName: "statusBarTranslucent" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
48
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),
|
|
53
|
+
* where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.
|
|
54
|
+
*/
|
|
49
55
|
navigationBarTranslucent = input(undefined, { ...(ngDevMode ? { debugName: "navigationBarTranslucent" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
50
56
|
/** Android: give the modal's window a hardware-accelerated surface. */
|
|
51
57
|
hardwareAccelerated = input(undefined, { ...(ngDevMode ? { debugName: "hardwareAccelerated" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
@@ -72,9 +78,6 @@ export class Modal extends ViewBase {
|
|
|
72
78
|
if (this.transparent() && style && style !== 'overFullScreen') {
|
|
73
79
|
console.warn(`[angular-native] <modal> is transparent, which needs presentationStyle="overFullScreen" on iOS; '${style}' will not be see-through.`);
|
|
74
80
|
}
|
|
75
|
-
if (this.navigationBarTranslucent() && !this.statusBarTranslucent()) {
|
|
76
|
-
console.warn('[angular-native] <modal> navigationBarTranslucent has no effect without statusBarTranslucent.');
|
|
77
|
-
}
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
package/dist/modal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.js","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,GACN,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;;AAM1C;;;;;;;;;;;;;GAaG;AAiBH,MAAM,OAAO,KAAM,SAAQ,QAAQ;IACjC;;;;;OAKG;IACM,OAAO,GAAG,KAAK,CAAC,IAAI,+EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAChE,wEAAwE;IAC/D,WAAW,GAAG,KAAK,CAAC,KAAK,mFAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IACrE,0EAA0E;IACjE,aAAa,GAAG,KAAK;iGAAU,CAAC;IACzC,4DAA4D;IACnD,aAAa,GAAG,KAAK;iGAA6B,CAAC;IAC5D,+FAA+F;IACtF,iBAAiB,GAAG,KAAK;qGAA0B,CAAC;IAC7D,6EAA6E;IACpE,qBAAqB,GAAG,KAAK;yGAA+B,CAAC;IACtE,sFAAsF;IAC7E,mBAAmB,GAAG,KAAK,CAAC,SAAS,2FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAChF
|
|
1
|
+
{"version":3,"file":"modal.js","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n DestroyRef,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { Direction } from '@ng-native/device';\nimport { HostEngine } from '@ng-native/fabric';\nimport { optionalBoolean } from './transforms.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\n\nexport type ModalPresentationStyle = 'fullScreen' | 'pageSheet' | 'formSheet' | 'overFullScreen';\nexport type ModalOrientation =\n 'portrait' | 'portrait-upside-down' | 'landscape' | 'landscape-left' | 'landscape-right';\n\n/**\n * Content presented over everything else. Commits as `ModalHostView`.\n *\n * RN's `Modal.js` gives the native host `{position: 'absolute'}` (the engine's default for it)\n * and puts the children inside a full-bleed view carrying the backdrop colour. Without the\n * container the content has no backdrop and no layout box, so a modal renders as nothing.\n *\n * Events are element events: `(requestClose)` for the Android back button and an iOS swipe,\n * `(show)`, `(dismiss)`, and `(orientationChange)`.\n *\n * The container is pinned to the leading edge with `[side]: 0`, the same as RN's own Modal.js -\n * `left` in a left-to-right layout, `right` in a right-to-left one - so the backdrop starts from\n * the edge content actually begins at rather than always the physical left.\n */\n@Component({\n selector: 'modal',\n imports: [View],\n template: `<view [style]=\"containerStyle()\" collapsable=\"false\"><ng-content /></view>`,\n host: {\n '[visible]': 'visible()',\n '[transparent]': 'transparent()',\n '[animationType]': 'animationType()',\n '[presentationStyle]': 'resolvedPresentationStyle()',\n '[supportedOrientations]': 'supportedOrientations()',\n '[allowSwipeDismissal]': 'allowSwipeDismissal()',\n '[statusBarTranslucent]': 'statusBarTranslucent()',\n '[navigationBarTranslucent]': 'navigationBarTranslucent()',\n '[hardwareAccelerated]': 'hardwareAccelerated()',\n },\n})\nexport class Modal extends ViewBase {\n /**\n * Whether the modal is showing. Defaults to true, as in RN. While false the engine leaves the\n * native host out of the commit, as RN's Modal returns null when hidden, because the host\n * covers the screen and takes every touch whether or not it presents; on iOS a modal that was\n * showing stays until native reports its dismissal.\n */\n readonly visible = input(true, { transform: booleanAttribute });\n /** See through to the screen behind; the container gets no backdrop. */\n readonly transparent = input(false, { transform: booleanAttribute });\n /** The container's background when not transparent. Defaults to white. */\n readonly backdropColor = input<string>();\n /** How the modal appears and leaves. Defaults to `none`. */\n readonly animationType = input<'none' | 'slide' | 'fade'>();\n /** iOS: full screen, a sheet, or over the current content. `overFullScreen` if transparent. */\n readonly presentationStyle = input<ModalPresentationStyle>();\n /** iOS: which orientations the modal may rotate to. Defaults to portrait. */\n readonly supportedOrientations = input<readonly ModalOrientation[]>();\n /** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */\n readonly allowSwipeDismissal = input(undefined, { transform: optionalBoolean });\n /**\n * Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where\n * React Native ignores `false`.\n */\n readonly statusBarTranslucent = input(undefined, { transform: optionalBoolean });\n /**\n * Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),\n * where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.\n */\n readonly navigationBarTranslucent = input(undefined, { transform: optionalBoolean });\n /** Android: give the modal's window a hardware-accelerated surface. */\n readonly hardwareAccelerated = input(undefined, { transform: optionalBoolean });\n\n private readonly direction = inject(Direction);\n\n protected readonly resolvedPresentationStyle = computed(\n () => this.presentationStyle() ?? (this.transparent() ? 'overFullScreen' : undefined),\n );\n\n protected readonly containerStyle = computed(() => ({\n [this.direction.rtl() ? 'right' : 'left']: 0,\n top: 0,\n flex: 1,\n backgroundColor: this.transparent() ? 'transparent' : (this.backdropColor() ?? 'white'),\n }));\n\n constructor() {\n super();\n // RN's host claims any touch that reaches it, so nothing inside the modal can hand a\n // gesture to the screen underneath.\n const stop = inject(HostEngine).setResponder(this.node, {\n onStartShouldSetResponder: () => true,\n });\n inject(DestroyRef).onDestroy(stop);\n\n effect(() => {\n const style = this.presentationStyle();\n if (this.transparent() && style && style !== 'overFullScreen') {\n console.warn(\n `[angular-native] <modal> is transparent, which needs presentationStyle=\"overFullScreen\" on iOS; '${style}' will not be see-through.`,\n );\n }\n });\n }\n}\n"]}
|
package/dist/pressable.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* press rectangle, delays and a long-press timer around them. This is that machine, on the same
|
|
6
6
|
* responder negotiation, so a button and an enclosing scroll view agree on who owns a drag.
|
|
7
7
|
*/
|
|
8
|
-
import { Directive,
|
|
8
|
+
import { Directive, computed, forwardRef, input, output, signal, booleanAttribute, } from '@angular/core';
|
|
9
9
|
import { nativePlatform } from '@ng-native/fabric';
|
|
10
|
-
import { optionalBoolean } from "./transforms.js";
|
|
10
|
+
import { numberOr, optionalBoolean } from "./transforms.js";
|
|
11
11
|
import { ViewBase } from "./view-base.js";
|
|
12
12
|
import * as i0 from "@angular/core";
|
|
13
13
|
/**
|
|
@@ -67,13 +67,13 @@ export class TouchableBase extends ViewBase {
|
|
|
67
67
|
pressRetentionOffset = input(/* @ts-ignore */
|
|
68
68
|
...(ngDevMode ? [undefined, { debugName: "pressRetentionOffset" }] : /* istanbul ignore next */ []));
|
|
69
69
|
/** Milliseconds a touch must be held before `(longPress)` fires. */
|
|
70
|
-
delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, { ...(ngDevMode ? { debugName: "delayLongPress" } : /* istanbul ignore next */ {}), transform:
|
|
70
|
+
delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, { ...(ngDevMode ? { debugName: "delayLongPress" } : /* istanbul ignore next */ {}), transform: numberOr(DEFAULT_LONG_PRESS_DELAY) });
|
|
71
71
|
/** Milliseconds between the touch landing and `(pressIn)`, so a scroll can start first. */
|
|
72
|
-
delayPressIn = input(0, { ...(ngDevMode ? { debugName: "delayPressIn" } : /* istanbul ignore next */ {}), transform:
|
|
72
|
+
delayPressIn = input(0, { ...(ngDevMode ? { debugName: "delayPressIn" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
73
73
|
/** Milliseconds between the touch lifting and `(pressOut)`. */
|
|
74
|
-
delayPressOut = input(0, { ...(ngDevMode ? { debugName: "delayPressOut" } : /* istanbul ignore next */ {}), transform:
|
|
74
|
+
delayPressOut = input(0, { ...(ngDevMode ? { debugName: "delayPressOut" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
75
75
|
/** The pressed state lasts at least this long, so a quick tap still shows it. */
|
|
76
|
-
minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, { ...(ngDevMode ? { debugName: "minPressDuration" } : /* istanbul ignore next */ {}), transform:
|
|
76
|
+
minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, { ...(ngDevMode ? { debugName: "minPressDuration" } : /* istanbul ignore next */ {}), transform: numberOr(DEFAULT_MIN_PRESS_DURATION) });
|
|
77
77
|
/** Whether a scrolling ancestor may take the gesture away mid-press. */
|
|
78
78
|
cancelable = input(true, { ...(ngDevMode ? { debugName: "cancelable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
79
79
|
/** Android: a native ripple on press. */
|