vue-pointing-bubble 0.1.0 → 0.2.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.
@@ -0,0 +1,12 @@
1
+ import { Geometry, TailPaths } from './types';
2
+ export interface BuildTailOptions {
3
+ geometry: Geometry;
4
+ strokeWidth: number;
5
+ radius: string;
6
+ tailBaseWidth: number;
7
+ }
8
+ /**
9
+ * Build fill + stroke SVG path `d` for the tail from the box wall facing the tip.
10
+ * Fill extends 1px past the border inner edge to cover zoom snapping seams.
11
+ */
12
+ export declare function buildTailPaths(opts: BuildTailOptions): TailPaths | null;
@@ -0,0 +1,66 @@
1
+ export type Placement = 'auto' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'manual';
2
+ export type ResolvedPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'manual' | 'none';
3
+ export type TargetAnchor = 'center' | 'edge';
4
+ export interface Point {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ /** Core target: coords, DOM element, CSS selector, or null (box-only / in-flow). */
9
+ export type BubbleTarget = Point | Element | string | null;
10
+ export interface FrameRect {
11
+ left: number;
12
+ top: number;
13
+ width: number;
14
+ height: number;
15
+ scaleX: number;
16
+ scaleY: number;
17
+ viewportWidth: number;
18
+ viewportHeight: number;
19
+ }
20
+ export interface LocalRect {
21
+ left: number;
22
+ top: number;
23
+ right: number;
24
+ bottom: number;
25
+ }
26
+ export interface BoxMeasure {
27
+ x: number;
28
+ y: number;
29
+ width: number;
30
+ height: number;
31
+ fill: string;
32
+ stroke: string;
33
+ strokeWidth: number;
34
+ radius: string;
35
+ zIndex: string;
36
+ }
37
+ export interface Geometry {
38
+ placement: ResolvedPlacement;
39
+ tx: number;
40
+ ty: number;
41
+ bx: number;
42
+ by: number;
43
+ w: number;
44
+ h: number;
45
+ }
46
+ export interface Room {
47
+ left: number;
48
+ right: number;
49
+ }
50
+ export interface TailPaths {
51
+ fill: string;
52
+ stroke: string | null;
53
+ }
54
+ export interface BubbleConfig {
55
+ target: BubbleTarget;
56
+ placement: Placement;
57
+ targetAnchor: TargetAnchor;
58
+ maxWidth?: number;
59
+ tailLength: number;
60
+ tailBaseWidth: number;
61
+ padding: number | string;
62
+ shadow: boolean;
63
+ transitionDuration: number;
64
+ }
65
+ export declare const DEFAULT_BUBBLE_CONFIG: BubbleConfig;
66
+ export type StyleMap = Record<string, string | number | undefined>;
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const m=require("vue"),T={target:null,placement:"auto",targetAnchor:"center",tailLength:45,tailBaseWidth:24,padding:8,shadow:!0,transitionDuration:200},I="pointing-bubble-styles",U=`:where(.pointing-bubble__inner) {
2
+ box-sizing: border-box;
3
+ width: var(--pb-width, max-content);
4
+ max-width: var(--pb-max-width, none);
5
+ padding: var(--pb-padding);
6
+ background: white;
7
+ border: 2px solid gray;
8
+ border-radius: 8px;
9
+ }`,_="drop-shadow(0 6px 8px rgba(0, 0, 0, 0.25))";function H(n=document){if(n.getElementById(I))return;const e=n.createElement("style");e.id=I,e.textContent=U,n.head.prepend(e)}function P(n){return typeof n=="object"&&n!==null&&typeof n.x=="number"&&typeof n.y=="number"&&n.nodeType!==1}function D(n){return typeof Element>"u"||n==null?null:typeof n=="string"?document.querySelector(n):n instanceof Element?n:null}function z(n,e){return!n||!e?!1:Object.keys(n).every(t=>n[t]===e[t])}function X(n){const e=n.getBoundingClientRect(),t=getComputedStyle(n),a=parseFloat(t.width)||0,l=parseFloat(t.height)||0;return{left:e.left,top:e.top,width:a,height:l,scaleX:a?e.width/a:1,scaleY:l?e.height/l:1,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}function G(n,e,t,a){const l=getComputedStyle(n),{scaleX:r,scaleY:u}=t,i=n.getBoundingClientRect(),d=e.getBoundingClientRect();return{x:a?(i.left-d.left)/r:0,y:a?(i.top-d.top)/u:0,width:i.width/r,height:i.height/u,fill:l.backgroundColor,stroke:l.borderTopColor,strokeWidth:parseFloat(l.borderTopWidth)||0,radius:l.borderTopLeftRadius,zIndex:l.zIndex}}function F(n,e,t){const a=n.x-t;let l=a,r=e.width?e.width-n.x-t:1/0;return e.viewportWidth&&(l=Math.min(l,e.left/e.scaleX+a),r=Math.min(r,(e.viewportWidth-e.left)/e.scaleX-n.x-t)),{left:Math.max(0,l),right:Math.max(0,r)}}function O(n){return n.left>n.right?"left":"right"}function j(n){const{tip:e,box:t,frame:a,placement:l,targetRect:r,room:u,tailLength:i}=n;if(!t.width||!t.height)return null;const{width:d,height:f}=t,{x,y}=e,E=l==="manual",o=n.targetAnchor==="edge"?r:null;if(E){if(o){const v=t.x+d/2,w=t.y+f/2;if(!(v>=o.left&&v<=o.right&&w>=o.top&&w<=o.bottom)){const g=Math.min(o.right,Math.max(o.left,v)),b=Math.min(o.bottom,Math.max(o.top,w));return{placement:"manual",tx:g,ty:b,bx:t.x,by:t.y,w:d,h:f}}}return{placement:"manual",tx:x,ty:y,bx:t.x,by:t.y,w:d,h:f}}const k=v=>o?v==="top"?o.top:o.bottom:y;let S=l;if(l==="auto"){let v=y>(a.height||600)/2?"top":"bottom",w=O(u);const{scaleX:p,scaleY:g}=a;if(a.viewportHeight){const b=a.top+(k("top")-f-i)*g>=0,B=a.top+(k("bottom")+f+i)*g<=a.viewportHeight;v==="top"&&!b&&B?v="bottom":v==="bottom"&&!B&&b&&(v="top")}if(a.viewportWidth){const b=a.left+(x-d-i)*p>=0,B=a.left+(x+d+i)*p<=a.viewportWidth;w==="left"&&!b&&B?w="right":w==="right"&&!B&&b&&(w="left")}S=`${v}-${w}`}const[W,C]=S.split("-"),$=k(W);return{placement:S,tx:x,ty:$,bx:C==="left"?x-d-i:x+i,by:W==="top"?$-f-i:$+i,w:d,h:f}}const q=["tx","ty","bx","by"],V=["tx","ty"];function Z(){return typeof matchMedia<"u"&&matchMedia("(prefers-reduced-motion: reduce)").matches}function K(n,e,t,a){let l=0;const r=()=>{typeof cancelAnimationFrame<"u"&&cancelAnimationFrame(l)};if(!e||!n||t<=0||typeof requestAnimationFrame>"u"||Z())return a(e),{cancel:r};const u=performance.now(),i=d=>{const f=Math.min(1,(d-u)/t),x=1-Math.pow(1-f,3),y={...e},E=e.placement==="manual"?V:q;for(const o of E)y[o]=n[o]+(e[o]-n[o])*x;a(y),f<1&&(l=requestAnimationFrame(i))};return l=requestAnimationFrame(i),{cancel:r}}function N(n){const{geometry:e,strokeWidth:t,radius:a,tailBaseWidth:l}=n,{placement:r,tx:u,ty:i,bx:d,by:f,w:x,h:y}=e,E=parseFloat(a)*(a.endsWith("%")?Math.min(x,y)/100:1)||0,o=l/2,k=(s,c,h)=>c>h?(c+h)/2:Math.min(h,Math.max(c,s));let S=null,W=0;if(r==="manual"){const s=Math.max(d-u,u-(d+x),0),c=Math.max(f-i,i-(f+y),0);if(s>0&&s>=c)S=u<d?"left":"right",W=i;else if(c>0)S=i<f?"top":"bottom",W=u;else return null}else if(r!=="none"){const[s,c]=r.split("-");S=c==="left"?"right":"left",W=f+y*(s==="top"?.65:.35)}else return null;const C=1,$=S==="top"||S==="bottom",v={left:(s,c)=>[d-c,s],right:(s,c)=>[d+x+c,s],top:(s,c)=>[s,f-c],bottom:(s,c)=>[s,f+y+c]}[S],w={left:d-u,right:u-d-x,top:f-i,bottom:i-f-y}[S],p=$?k(W,d+E+o+t/2,d+x-E-o-t/2):k(W,f+E+o+t/2,f+y-E-o-t/2),g=Math.max(0,Math.min(t,w/2)),b=([s,c])=>`${s} ${c}`,B=(s,c)=>{const h=v(s,g),L=Math.hypot(u-h[0],i-h[1])||1,R=[h[0]+(u-h[0])*g/L,h[1]+(i-h[1])*g/L];return{inner:b(v(s,c)),outer:b(v(s,0)),ctrl:b(h),end:b(R)}},M=s=>{const c=B(p-o,s),h=B(p+o,s);return`M ${c.inner} L ${c.outer} Q ${c.ctrl} ${c.end} L ${u} ${i} L ${h.end} Q ${h.ctrl} ${h.outer} L ${h.inner}`};return{fill:`${M(-t-C)} Z`,stroke:t?M(-t):null}}function Q(n){return{hasTarget:n,placement:"none",tip:null,slotTip:null,overlayStyle:{},boxStyle:{},rootIsBox:!n,tail:null,boxFill:"",boxStroke:"",boxStrokeWidth:0,boxZIndex:"auto"}}function Y(n){H();let e={...T},t=null,a=null,l=null,r={left:0,top:0,width:0,height:0,scaleX:1,scaleY:1,viewportWidth:0,viewportHeight:0},u=null,i=null,d=null,f=null,x=Q(!1);const y=()=>{x=E(),n.onChange?.(x)};function E(){const p=e.target!=null,g=e.placement==="manual",b=p?u?.placement??"none":"none",B={position:"absolute",inset:"0",pointerEvents:"none",filter:e.shadow?_:void 0},M=l?.zIndex;M&&M!=="auto"&&(B.zIndex=M);const s={"--pb-padding":typeof e.padding=="number"?`${e.padding}px`:e.padding};if(e.maxWidth!=null&&(s["--pb-max-width"]=`${e.maxWidth}px`),!p)s["--pb-width"]="fit-content",e.shadow&&(s.filter=_);else if(Object.assign(s,{position:"absolute",pointerEvents:"auto",visibility:u?"visible":"hidden"}),g)s["--pb-width"]="fit-content";else if(Object.assign(s,{left:`${u?.bx??0}px`,top:`${u?.by??0}px`,margin:"0"}),e.maxWidth==null&&t){const h=F(t,r,e.tailLength),L=i?.placement??e.placement,R=L==="auto"?O(h):L.split("-")[1];s["--pb-max-width"]=`${h[R]}px`}const c=p&&u&&l?N({geometry:u,strokeWidth:l.strokeWidth,radius:l.radius,tailBaseWidth:e.tailBaseWidth}):null;return{hasTarget:p,placement:b,tip:t,slotTip:i?{x:i.tx,y:i.ty}:t,overlayStyle:B,boxStyle:s,rootIsBox:!p,tail:c,boxFill:l?.fill??"",boxStroke:l?.stroke??"",boxStrokeWidth:l?.strokeWidth??0,boxZIndex:l?.zIndex??"auto"}}function o(){if(!t||!l){i=null,k(null);return}const p=F(t,r,e.tailLength);i=j({tip:t,box:l,frame:r,placement:e.placement,targetAnchor:e.targetAnchor,targetRect:a,room:p,tailLength:e.tailLength}),k(i)}function k(p){d?.cancel(),d=K(u,p,e.transitionDuration,g=>{u=g,y()})}function S(){const p=n.getRootEl(),g=n.getBoxEl();if(!g||e.target==null||!p)return;const b=G(g,p,r,e.placement==="manual");z(b,l)||(l=b)}function W(){const p=n.getRootEl();if(e.target==null||!p){t=null,a=null,l=null,i=null,u=null,y();return}r=X(p);const g=D(e.target);if(g){const b=g.getBoundingClientRect(),{scaleX:B,scaleY:M,left:s,top:c}=r,h={left:(b.left-s)/B,top:(b.top-c)/M,right:(b.right-s)/B,bottom:(b.bottom-c)/M};z(h,a)||(a=h),t={x:(h.left+h.right)/2,y:(h.top+h.bottom)/2}}else if(P(e.target))a=null,t={x:e.target.x,y:e.target.y};else{a=null,t=null,l=null,i=null,u=null,y();return}S(),o()}function C(){W()}typeof window<"u"&&(window.addEventListener("resize",C),window.addEventListener("scroll",C,!0));function $(){if(typeof ResizeObserver>"u")return;f?.disconnect(),f=new ResizeObserver(()=>W());const p=n.getRootEl(),g=n.getBoxEl();p&&f.observe(p),g&&f.observe(g)}const v=W;function w(){$(),v()}return{update:w,setConfig(p){e={...e,...p},w()},getConfig:()=>e,getState:()=>x,destroy(){d?.cancel(),f?.disconnect(),typeof window<"u"&&(window.removeEventListener("resize",C),window.removeEventListener("scroll",C,!0))}}}const J=["data-placement"],tt=["d","fill"],et=["d","stroke","stroke-width"],A=m.defineComponent({name:"PointingBubble",inheritAttrs:!1,__name:"PointingBubble",props:{target:{default:null},placement:{default:"auto"},targetAnchor:{default:"center"},maxWidth:{default:void 0},tailLength:{default:45},tailBaseWidth:{default:24},padding:{default:8},shadow:{type:Boolean,default:!0},transitionDuration:{default:200}},setup(n,{expose:e}){const t=n,a=m.ref(null),l=m.ref(null),r=m.ref({hasTarget:!1,placement:"none",tip:null,slotTip:null,overlayStyle:{},boxStyle:{},rootIsBox:!0,tail:null,boxFill:"",boxStroke:"",boxStrokeWidth:0,boxZIndex:"auto"});function u(o){return o==null?null:typeof o=="string"||typeof Element<"u"&&o instanceof Element?o:typeof o=="object"&&"$el"in o&&o.$el instanceof Element?o.$el:typeof o=="object"&&typeof o.x=="number"&&typeof o.y=="number"?o:null}const i=Y({getRootEl:()=>a.value,getBoxEl:()=>l.value,onChange:o=>{r.value=o}});function d(){i.setConfig({target:u(t.target),placement:t.placement,targetAnchor:t.targetAnchor,maxWidth:t.maxWidth,tailLength:t.tailLength,tailBaseWidth:t.tailBaseWidth,padding:t.padding,shadow:t.shadow,transitionDuration:t.transitionDuration})}m.watch(()=>[t.target,typeof t.target=="object"&&t.target&&"x"in t.target?t.target.x:null,typeof t.target=="object"&&t.target&&"y"in t.target?t.target.y:null,t.placement,t.targetAnchor,t.maxWidth,t.tailLength,t.tailBaseWidth,t.padding,t.shadow,t.transitionDuration],d,{flush:"post"}),m.onMounted(()=>{d()}),m.onUpdated(()=>{i.update()}),m.onBeforeUnmount(()=>{i.destroy()});function f(){i.update()}e({update:f});const x=m.computed(()=>r.value.rootIsBox?r.value.boxStyle:r.value.overlayStyle),y=m.computed(()=>r.value.rootIsBox?{display:"contents"}:r.value.boxStyle),E=m.computed(()=>({position:"absolute",inset:"0",width:"100%",height:"100%",overflow:"visible",zIndex:r.value.boxZIndex}));return(o,k)=>(m.openBlock(),m.createElementBlock("div",m.mergeProps({ref_key:"rootRef",ref:a,class:["pointing-bubble",{"pointing-bubble__inner":!n.target}],"data-placement":r.value.placement},n.target?{}:o.$attrs,{style:x.value}),[m.createElementVNode("div",m.mergeProps({ref_key:"boxRef",ref:l,class:{"pointing-bubble__inner":!!n.target}},n.target?o.$attrs:{},{style:y.value}),[m.renderSlot(o.$slots,"default",{placement:r.value.placement,tip:r.value.slotTip},()=>[k[0]||(k[0]=m.createElementVNode("p",{style:{margin:0,fontSize:"14px",fontWeight:600,color:"#1e293b"}}," Callout content ",-1))])],16),n.target&&r.value.tail?(m.openBlock(),m.createElementBlock("svg",{key:0,class:"pointing-bubble__svg",style:m.normalizeStyle(E.value)},[m.createElementVNode("path",{class:"pointing-bubble__path",d:r.value.tail.fill,fill:r.value.boxFill},null,8,tt),r.value.tail.stroke?(m.openBlock(),m.createElementBlock("path",{key:0,class:"pointing-bubble__path",d:r.value.tail.stroke,fill:"none",stroke:r.value.boxStroke,"stroke-width":r.value.boxStrokeWidth,"stroke-linejoin":"round","stroke-linecap":"butt"},null,8,et)):m.createCommentVNode("",!0)],4)):m.createCommentVNode("",!0)],16,J))}}),nt={install(n,e={}){const t=e.name??"PointingBubble";n.component(t,A),n.component("Callout",A)}};exports.Callout=A;exports.DEFAULT_BUBBLE_CONFIG=T;exports.PointingBubble=A;exports.PointingBubblePlugin=nt;exports.buildTailPaths=N;exports.computeGeometry=j;exports.createBubbleController=Y;exports.default=A;exports.isPoint=P;exports.resolveElement=D;