@phuong-tran-redoc/document-engine-angular 0.1.4 → 0.1.5

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/index.d.ts CHANGED
@@ -746,9 +746,14 @@ declare class TiptapFloatingMenuDirective implements OnInit, OnDestroy {
746
746
  *
747
747
  * Usage:
748
748
  * <button #trigger>Open</button>
749
- * <div [popover]="trigger" [isOpen]="isOpen">
749
+ * <div [documentEnginePopover]="trigger" [isOpen]="isOpen">
750
750
  * Content
751
751
  * </div>
752
+ *
753
+ * The host is pinned `position: fixed`, so floating-ui is asked for viewport-
754
+ * relative coordinates (`strategy: 'fixed'`). Omitting the strategy yields
755
+ * offset-parent-relative numbers, which are off by the page's scroll offset
756
+ * once written onto a fixed element.
752
757
  */
753
758
  declare class PopoverDirective implements OnChanges, AfterViewInit, OnDestroy {
754
759
  private elRef;
@@ -756,7 +761,16 @@ declare class PopoverDirective implements OnChanges, AfterViewInit, OnDestroy {
756
761
  /**
757
762
  * The trigger element reference (ElementRef or HTMLElement)
758
763
  */
759
- popover: ElementRef<HTMLElement> | HTMLElement;
764
+ documentEnginePopover?: ElementRef<HTMLElement> | HTMLElement;
765
+ /**
766
+ * The trigger element reference (ElementRef or HTMLElement)
767
+ *
768
+ * @deprecated Use `documentEnginePopover`. `[popover]` collides with the
769
+ * native HTML popover attribute and will be removed in the next major.
770
+ */
771
+ popover?: ElementRef<HTMLElement> | HTMLElement;
772
+ /** The trigger, whichever input supplied it. */
773
+ private get trigger();
760
774
  /**
761
775
  * Whether the popover is open
762
776
  */
@@ -784,9 +798,18 @@ declare class PopoverDirective implements OnChanges, AfterViewInit, OnDestroy {
784
798
  private updateVisibility;
785
799
  private setupFloating;
786
800
  private _setupFloatingInternal;
801
+ /**
802
+ * Position `dropdownEl` against `triggerEl`.
803
+ *
804
+ * `strategy: 'fixed'` is required: the host binding pins this element to
805
+ * `position: fixed`, so the coordinates must be viewport-relative. Without it
806
+ * floating-ui returns offset-parent-relative numbers and the panel lands the
807
+ * page's scroll offset away from its trigger.
808
+ */
809
+ private reposition;
787
810
  private cleanup;
788
811
  static ɵfac: i0.ɵɵFactoryDeclaration<PopoverDirective, never>;
789
- static ɵdir: i0.ɵɵDirectiveDeclaration<PopoverDirective, "[popover]", never, { "popover": { "alias": "popover"; "required": false; }; "isOpen": { "alias": "isOpen"; "required": false; }; "placement": { "alias": "placement"; "required": false; }; "offset": { "alias": "offset"; "required": false; }; "padding": { "alias": "padding"; "required": false; }; }, {}, never, never, true, never>;
812
+ static ɵdir: i0.ɵɵDirectiveDeclaration<PopoverDirective, "[documentEnginePopover], [popover]", never, { "documentEnginePopover": { "alias": "documentEnginePopover"; "required": false; }; "popover": { "alias": "popover"; "required": false; }; "isOpen": { "alias": "isOpen"; "required": false; }; "placement": { "alias": "placement"; "required": false; }; "offset": { "alias": "offset"; "required": false; }; "padding": { "alias": "padding"; "required": false; }; }, {}, never, never, true, never>;
790
813
  }
791
814
 
792
815
  declare const DocumentEngineKit: Extension<DocumentEngineConfig, any>;
@@ -1277,6 +1300,7 @@ declare class CheckboxComponent implements ControlValueAccessor {
1277
1300
  checked: boolean;
1278
1301
  checkedChange: EventEmitter<boolean>;
1279
1302
  hostClass: boolean;
1303
+ private readonly cdr;
1280
1304
  private onChange;
1281
1305
  private onTouched;
1282
1306
  onCheckboxChange(event: Event): void;
@@ -1711,6 +1735,8 @@ declare class ToolbarBubbleMenuComponent implements AfterViewInit, OnDestroy {
1711
1735
  private currentViewRef;
1712
1736
  private currentViewId;
1713
1737
  private currentAttributes;
1738
+ private cleanupAutoUpdate?;
1739
+ private positionTimeout?;
1714
1740
  isVisible: boolean;
1715
1741
  ngAfterViewInit(): void;
1716
1742
  ngOnDestroy(): void;
@@ -1733,6 +1759,21 @@ declare class ToolbarBubbleMenuComponent implements AfterViewInit, OnDestroy {
1733
1759
  private handleNavigateTo;
1734
1760
  private handleGoBack;
1735
1761
  private findViewConfig;
1762
+ /**
1763
+ * Anchor the bubble to its trigger with floating-ui, matching
1764
+ * `PopoverDirective`.
1765
+ *
1766
+ * The hand-rolled version this replaces measured the trigger exactly once, so
1767
+ * scrolling left the panel welded to the viewport while its target slid away.
1768
+ * It also had no third case: a panel that fit neither below nor above got a
1769
+ * negative `top`, putting its own header off the top edge of the screen.
1770
+ * `flip` handles the second placement and `shift` clamps the panel into the
1771
+ * viewport instead, so there is no unhandled case left.
1772
+ *
1773
+ * `strategy: 'fixed'` matches the `position: fixed` written below — asking for
1774
+ * the default `'absolute'` coordinates would offset the panel by the page's
1775
+ * scroll amount.
1776
+ */
1736
1777
  private positionBubble;
1737
1778
  static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarBubbleMenuComponent, never>;
1738
1779
  static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarBubbleMenuComponent, "document-engine-toolbar-bubble-menu", never, { "editor": { "alias": "editor"; "required": false; }; "config": { "alias": "config"; "required": false; }; }, {}, never, never, true, never>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phuong-tran-redoc/document-engine-angular",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Realestatedoc (Redoc)",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "tslib": "^2.3.0",
37
37
  "@floating-ui/dom": "^1.7.4",
38
- "@phuong-tran-redoc/document-engine-core": "0.1.4"
38
+ "@phuong-tran-redoc/document-engine-core": "0.1.5"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "rxjs": "^7.5.0",
@@ -75,6 +75,9 @@
75
75
  "./styles": {
76
76
  "sass": "./src/lib/styles/index.scss"
77
77
  },
78
+ "./styles/tokens": {
79
+ "sass": "./src/lib/styles/tokens.scss"
80
+ },
78
81
  "./styles/editor-content": {
79
82
  "sass": "./src/lib/styles/editor-content.scss"
80
83
  },
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Floating-panel surface — the look every bubble menu / popover panel needs to
3
+ * be legible: an opaque background, a rule, a radius, an elevation and a
4
+ * stacking order.
5
+ *
6
+ * Why this is a mixin and not Tailwind utilities in a template:
7
+ * utilities like `bg-card`, `border-border` or `shadow-elevation-2` only exist
8
+ * if the HOST app's `tailwind.config.js` extends its theme with exactly those
9
+ * keys. The demo app happens to; a consumer with its own theme does not, and
10
+ * there the class names generate no rule at all — the panel then renders as
11
+ * transparent text floating over the page. A component library must not put
12
+ * that requirement on its host, so the panel carries its own surface.
13
+ *
14
+ * Every value resolves from a CSS custom property WITH a fallback, so:
15
+ * - a consumer that remaps the tokens gets its own theme, and
16
+ * - a consumer that remaps nothing still gets a usable, opaque panel.
17
+ *
18
+ * Tokens read here (all optional, all documented in docs/THEMING.md):
19
+ * --popover / --card panel background
20
+ * --popover-foreground / --card-foreground panel text
21
+ * --border panel rule
22
+ * --shadow-elevation-2 panel elevation
23
+ */
24
+
25
+ /* Light-mode floors, used only when the consumer defines nothing. */
26
+ $de-fallback-surface: hsl(0 0% 100%) !default;
27
+ $de-fallback-surface-foreground: hsl(222.2 84% 4.9%) !default;
28
+ $de-fallback-border: hsl(214.3 31.8% 91.4%) !default;
29
+
30
+ @mixin panel-surface($z-index: 30, $radius: 0.25rem) {
31
+ box-sizing: border-box;
32
+ z-index: $z-index;
33
+ border-radius: $radius;
34
+ border: 1px solid var(--border, #{$de-fallback-border});
35
+
36
+ /* `--popover` is the semantic token for a floating surface; `--card` is the
37
+ * broader surface token most consumers already define, so it is the second
38
+ * chance before the hardcoded floor. */
39
+ background-color: var(--popover, var(--card, #{$de-fallback-surface}));
40
+ color: var(--popover-foreground, var(--card-foreground, #{$de-fallback-surface-foreground}));
41
+
42
+ box-shadow: var(--shadow-elevation-2, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));
43
+ }
@@ -7,10 +7,31 @@
7
7
  color: var(--card-foreground, #000);
8
8
  box-shadow: none;
9
9
 
10
+ // Participate in a parent that has a real height (a fixed-height container, a
11
+ // flex/grid cell) instead of only ever sizing to content. `flex: 1 1 auto` is
12
+ // what actually claims that height: without it this box stays content-sized as
13
+ // a flex item of :host, and a fixed-height container is left with a band of
14
+ // dead, unclickable space under the editable.
15
+ //
16
+ // `max-height: 100%` is inert when the parent's height is indefinite (a
17
+ // percentage against an auto height resolves to none), so an unconstrained
18
+ // consumer still gets a box that simply grows.
19
+ flex: 1 1 auto;
20
+ min-height: 0;
21
+ max-height: 100%;
22
+
10
23
  &__content {
11
24
  padding: 1rem;
12
25
  border: 1px solid var(--border, #e5e7eb);
13
26
  background-color: var(--card, #fff);
27
+
28
+ // Take the leftover space so the editing surface below can stretch into it.
29
+ // Deliberately no `overflow` here: the panels rendered inside carry selects
30
+ // and colour popovers, and an overflow ancestor can clip them.
31
+ display: flex;
32
+ flex-direction: column;
33
+ flex: 1 1 auto;
34
+ min-height: 0;
14
35
  }
15
36
  }
16
37
 
@@ -18,3 +39,43 @@
18
39
  // document-engine-toolbar ~ .document-engine-document-editor__content {
19
40
  // border-top: 1px solid var(--border, #e5e7eb);
20
41
  // }
42
+
43
+ /**
44
+ * Editing-surface sizing.
45
+ *
46
+ * The library used to size the contenteditable to its content and nothing else,
47
+ * so an emptied editor collapsed to a single line. In a fixed-height container
48
+ * that left a ~28px editable inside a ~480px box that plainly looks like a text
49
+ * field, and clicking anywhere but that one line did nothing.
50
+ *
51
+ * Two hooks, one rule:
52
+ * - `--de-editor-min-height` is the floor (default `12rem`). Remap it, or set
53
+ * it to `0` to opt out entirely.
54
+ * - `flex: 1 1 auto` lets an ancestor with a real height drive the surface, so
55
+ * the editable fills a fixed-height container without any magic number.
56
+ *
57
+ * `.tiptap-editor` is the class `TiptapEditorDirective` puts on its host;
58
+ * `.tiptap` / `.ProseMirror` is the contenteditable Tiptap creates inside it.
59
+ */
60
+ .tiptap-editor {
61
+ display: flex;
62
+ flex-direction: column;
63
+ flex: 1 1 auto;
64
+ min-height: 0;
65
+
66
+ // The editing surface is the scroll container. Without this, `max-height: 100%`
67
+ // above caps the wrapper while nothing scrolls, so a long document paints
68
+ // straight through the bottom of a fixed-height container and over whatever
69
+ // follows it on the page.
70
+ //
71
+ // Safe to put overflow here: the floating panels are rendered inside
72
+ // `document-engine-toolbar`, a sibling of the content box, never a descendant
73
+ // of this element — so there is nothing of theirs for this to clip.
74
+ overflow-y: auto;
75
+
76
+ > .tiptap,
77
+ > .ProseMirror {
78
+ flex: 1 1 auto;
79
+ min-height: var(--de-editor-min-height, 12rem);
80
+ }
81
+ }
@@ -23,8 +23,16 @@
23
23
  *
24
24
  * All styles use class/element selectors and CSS-variable-driven values, so you
25
25
  * can also override them with your own CSS without importing these files.
26
+ *
27
+ * Theming: this barrel also ships zero-specificity DEFAULTS for every custom
28
+ * property the library reads (`_tokens.scss`, emitted on `:where(:root)`), so it
29
+ * looks right out of the box and any token you declare yourself wins without a
30
+ * specificity fight. The full list is in `docs/THEMING.md`.
26
31
  */
27
32
 
33
+ // Token defaults first, so they sit at the top of the emitted sheet.
34
+ @use 'tokens';
35
+
28
36
  @forward 'icon';
29
37
  @forward 'button';
30
38
  @forward 'toolbar';
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Default values for every CSS custom property the library's CSS reads.
3
+ *
4
+ * The library's stylesheets are token-driven: they paint with `var(--border)`,
5
+ * `var(--popover)`, `var(--foreground)` and friends. Historically the library
6
+ * *consumed* those tokens without ever defining or documenting them, so a
7
+ * consumer that had not happened to declare the same shadcn-style token set got
8
+ * panels with no background and text with no colour.
9
+ *
10
+ * This file closes that hole: it declares a light-mode default for every token,
11
+ * so importing the chrome barrel is enough to get a working look with zero
12
+ * theming. See `docs/THEMING.md` for the full contract.
13
+ *
14
+ * ## Why `:where(:root)`
15
+ *
16
+ * `:where()` contributes **zero specificity**, so ANY declaration a consumer
17
+ * writes — `:root { --border: … }`, `.dark { --border: … }`, an inline style,
18
+ * a `[data-theme]` block — beats these defaults regardless of source order.
19
+ * They are a floor, never a fight.
20
+ *
21
+ * ## Rescoping
22
+ *
23
+ * The defaults are also exposed as a mixin, so you can plant a fresh set on any
24
+ * container — e.g. an embedded editor that must ignore the surrounding theme:
25
+ *
26
+ * @use '@phuong-tran-redoc/document-engine-angular/styles/tokens' as de-tokens;
27
+ * .my-isolated-editor { @include de-tokens.token-defaults; }
28
+ */
29
+
30
+ @mixin token-defaults {
31
+ /* --- Surfaces --- */
32
+ --background: hsl(0 0% 100%);
33
+ --foreground: hsl(222.2 84% 4.9%);
34
+ --card: hsl(0 0% 100%);
35
+ --card-foreground: hsl(222.2 84% 4.9%);
36
+ --popover: hsl(0 0% 100%);
37
+ --popover-foreground: hsl(222.2 84% 4.9%);
38
+
39
+ /* --- Accents & state --- */
40
+ --primary: hsl(222.2 47.4% 11.2%);
41
+ --primary-foreground: hsl(210 40% 98%);
42
+ --secondary: hsl(210 40% 96.1%);
43
+ --secondary-foreground: hsl(222.2 47.4% 11.2%);
44
+ --muted: hsl(210 40% 96.1%);
45
+ --muted-foreground: hsl(215.4 16.3% 46.9%);
46
+ --accent: hsl(210 40% 96.1%);
47
+ --accent-foreground: hsl(222.2 47.4% 11.2%);
48
+ --destructive: hsl(0 84.2% 60.2%);
49
+ --destructive-foreground: hsl(210 40% 98%);
50
+
51
+ /* --- Lines --- */
52
+ --border: hsl(214.3 31.8% 91.4%);
53
+ --input: hsl(214.3 31.8% 91.4%);
54
+ --ring: hsl(222.2 84% 4.9%);
55
+
56
+ /* --- Elevation ---
57
+ * Consumed by panels, dropdowns and inputs. Undefined, `box-shadow: var(…)`
58
+ * resolves to nothing, which is how floating panels lost their elevation. */
59
+ --shadow-elevation-1: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
60
+ --shadow-elevation-2: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
61
+ --shadow-elevation-3: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
62
+
63
+ /* --- Editing surface sizing (see docs/THEMING.md#sizing) --- */
64
+ --de-editor-min-height: 12rem;
65
+ }
66
+
67
+ :where(:root) {
68
+ @include token-defaults;
69
+ }