vue-pointing-bubble 0.1.0 → 0.4.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"),P={target:null,placement:"auto",targetAnchor:"center",tailLength:45,tailBaseWidth:24,padding:8,shadow:!0,transitionDuration:200},T="pointing-bubble-styles",q=`: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(e=document){if(e.getElementById(T))return;const t=e.createElement("style");t.id=T,t.textContent=q,e.head.prepend(t)}function D(e){return typeof e=="object"&&e!==null&&typeof e.x=="number"&&typeof e.y=="number"&&e.nodeType!==1}function O(e){return typeof Element>"u"||e==null?null:typeof e=="string"?document.querySelector(e):e instanceof Element?e:null}function A(e,t){return!e||!t?!1:Object.keys(e).every(n=>e[n]===t[n])}function X(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e),a=parseFloat(n.width)||0,l=parseFloat(n.height)||0;return{left:t.left,top:t.top,width:a,height:l,scaleX:a?t.width/a:1,scaleY:l?t.height/l:1,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}function Z(e,t,n,a){const l=getComputedStyle(e),{scaleX:i,scaleY:d}=n,r=e.getBoundingClientRect(),f=t.getBoundingClientRect();return{x:a?(r.left-f.left)/i:0,y:a?(r.top-f.top)/d:0,width:r.width/i,height:r.height/d,fill:l.backgroundColor,stroke:l.borderTopColor,strokeWidth:parseFloat(l.borderTopWidth)||0,radius:l.borderTopLeftRadius,zIndex:l.zIndex}}function F(e,t,n){const a=e.x-n;let l=a,i=t.width?t.width-e.x-n:1/0;return t.viewportWidth&&(l=Math.min(l,t.left/t.scaleX+a),i=Math.min(i,(t.viewportWidth-t.left)/t.scaleX-e.x-n)),{left:Math.max(0,l),right:Math.max(0,i)}}function j(e){return e.left>e.right?"left":"right"}function N(e){const{tip:t,box:n,frame:a,placement:l,targetRect:i,room:d,tailLength:r}=e;if(!n.width||!n.height)return null;const{width:f,height:p}=n,{x:b,y}=t,k=l==="manual",o=e.targetAnchor==="edge"?i:null;if(k){if(o){const v=n.x+f/2,w=n.y+p/2;if(!(v>=o.left&&v<=o.right&&w>=o.top&&w<=o.bottom)){const x=Math.min(o.right,Math.max(o.left,v)),g=Math.min(o.bottom,Math.max(o.top,w));return{placement:"manual",tx:x,ty:g,bx:n.x,by:n.y,w:f,h:p}}}return{placement:"manual",tx:b,ty:y,bx:n.x,by:n.y,w:f,h:p}}const E=v=>o?v==="top"?o.top:o.bottom:y;let B=l;if(l==="auto"){let v=y>(a.height||600)/2?"top":"bottom",w=j(d);const{scaleX:u,scaleY:x}=a;if(a.viewportHeight){const g=a.top+(E("top")-p-r)*x>=0,S=a.top+(E("bottom")+p+r)*x<=a.viewportHeight;v==="top"&&!g&&S?v="bottom":v==="bottom"&&!S&&g&&(v="top")}if(a.viewportWidth){const g=a.left+(b-f-r)*u>=0,S=a.left+(b+f+r)*u<=a.viewportWidth;w==="left"&&!g&&S?w="right":w==="right"&&!S&&g&&(w="left")}B=`${v}-${w}`}const[W,C]=B.split("-"),I=E(W);return{placement:B,tx:b,ty:I,bx:C==="left"?b-f-r:b+r,by:W==="top"?I-p-r:I+r,w:f,h:p}}const G=["tx","ty","bx","by"],V=["tx","ty"];function K(){return typeof matchMedia<"u"&&matchMedia("(prefers-reduced-motion: reduce)").matches}function Q(e,t,n,a){let l=0;const i=()=>{typeof cancelAnimationFrame<"u"&&cancelAnimationFrame(l)};if(!t||!e||n<=0||typeof requestAnimationFrame>"u"||K())return a(t),{cancel:i};const d=performance.now(),r=f=>{const p=Math.min(1,(f-d)/n),b=1-Math.pow(1-p,3),y={...t},k=t.placement==="manual"?V:G;for(const o of k)y[o]=e[o]+(t[o]-e[o])*b;a(y),p<1&&(l=requestAnimationFrame(r))};return l=requestAnimationFrame(r),{cancel:i}}function Y(e){const{geometry:t,strokeWidth:n,radius:a,tailBaseWidth:l}=e,{placement:i,tx:d,ty:r,bx:f,by:p,w:b,h:y}=t,k=parseFloat(a)*(a.endsWith("%")?Math.min(b,y)/100:1)||0,o=l/2,E=(s,c,h)=>c>h?(c+h)/2:Math.min(h,Math.max(c,s));let B=null,W=0;if(i==="manual"){const s=Math.max(f-d,d-(f+b),0),c=Math.max(p-r,r-(p+y),0);if(s>0&&s>=c)B=d<f?"left":"right",W=r;else if(c>0)B=r<p?"top":"bottom",W=d;else return null}else if(i!=="none"){const[s,c]=i.split("-");B=c==="left"?"right":"left",W=p+y*(s==="top"?.65:.35)}else return null;const C=1,I=B==="top"||B==="bottom",v={left:(s,c)=>[f-c,s],right:(s,c)=>[f+b+c,s],top:(s,c)=>[s,p-c],bottom:(s,c)=>[s,p+y+c]}[B],w={left:f-d,right:d-f-b,top:p-r,bottom:r-p-y}[B],u=I?E(W,f+k+o+n/2,f+b-k-o-n/2):E(W,p+k+o+n/2,p+y-k-o-n/2),x=Math.max(0,Math.min(n,w/2)),g=([s,c])=>`${s} ${c}`,S=(s,c)=>{const h=v(s,x),$=Math.hypot(d-h[0],r-h[1])||1,R=[h[0]+(d-h[0])*x/$,h[1]+(r-h[1])*x/$];return{inner:g(v(s,c)),outer:g(v(s,0)),ctrl:g(h),end:g(R)}},M=s=>{const c=S(u-o,s),h=S(u+o,s);return`M ${c.inner} L ${c.outer} Q ${c.ctrl} ${c.end} L ${d} ${r} L ${h.end} Q ${h.ctrl} ${h.outer} L ${h.inner}`};return{fill:`${M(-n-C)} Z`,stroke:n?M(-n):null}}function J(e){return{hasTarget:e,placement:"none",tip:null,slotTip:null,overlayStyle:{},boxStyle:{},rootIsBox:!e,tail:null,boxFill:"",boxStroke:"",boxStrokeWidth:0,boxZIndex:"auto"}}function z(e,t){return e===t?!0:!e||!t?!1:e.x===t.x&&e.y===t.y}function tt(e,t){return e.hasTarget===t.hasTarget&&e.placement===t.placement&&e.rootIsBox===t.rootIsBox&&e.boxFill===t.boxFill&&e.boxStroke===t.boxStroke&&e.boxStrokeWidth===t.boxStrokeWidth&&e.boxZIndex===t.boxZIndex&&z(e.tip,t.tip)&&z(e.slotTip,t.slotTip)&&A(e.overlayStyle,t.overlayStyle)&&A(e.boxStyle,t.boxStyle)&&e.tail?.fill===t.tail?.fill&&e.tail?.stroke===t.tail?.stroke}function U(e){H();let t={...P},n=null,a=null,l=null,i={left:0,top:0,width:0,height:0,scaleX:1,scaleY:1,viewportWidth:0,viewportHeight:0},d=null,r=null,f=null,p=null,b=J(!1);const y=()=>{const u=k();tt(b,u)||(b=u,e.onChange?.(b))};function k(){const u=t.target!=null,x=t.placement==="manual",g=u?d?.placement??"none":"none",S={position:"absolute",inset:"0",pointerEvents:"none",filter:t.shadow?_:void 0},M=l?.zIndex;M&&M!=="auto"&&(S.zIndex=M);const s={"--pb-padding":typeof t.padding=="number"?`${t.padding}px`:t.padding};if(t.maxWidth!=null&&(s["--pb-max-width"]=`${t.maxWidth}px`),!u)s["--pb-width"]="fit-content",t.shadow&&(s.filter=_);else if(Object.assign(s,{position:"absolute",pointerEvents:"auto",visibility:d?"visible":"hidden"}),x)s["--pb-width"]="fit-content";else if(Object.assign(s,{left:`${d?.bx??0}px`,top:`${d?.by??0}px`,margin:"0"}),t.maxWidth==null&&n){const h=F(n,i,t.tailLength),$=r?.placement??t.placement,R=$==="auto"?j(h):$.split("-")[1];s["--pb-max-width"]=`${h[R]}px`}const c=u&&d&&l?Y({geometry:d,strokeWidth:l.strokeWidth,radius:l.radius,tailBaseWidth:t.tailBaseWidth}):null;return{hasTarget:u,placement:g,tip:n,slotTip:r?{x:r.tx,y:r.ty}:n,overlayStyle:S,boxStyle:s,rootIsBox:!u,tail:c,boxFill:l?.fill??"",boxStroke:l?.stroke??"",boxStrokeWidth:l?.strokeWidth??0,boxZIndex:l?.zIndex??"auto"}}function o(){if(!n||!l){r=null,E(null);return}const u=F(n,i,t.tailLength);r=N({tip:n,box:l,frame:i,placement:t.placement,targetAnchor:t.targetAnchor,targetRect:a,room:u,tailLength:t.tailLength}),E(r)}function E(u){f?.cancel(),f=Q(d,u,t.transitionDuration,x=>{d=x,y()})}function B(){const u=e.getRootEl(),x=e.getBoxEl();if(!x||t.target==null||!u)return;const g=Z(x,u,i,t.placement==="manual");A(g,l)||(l=g)}function W(){const u=e.getRootEl();if(t.target==null||!u){n=null,a=null,l=null,r=null,d=null,y();return}i=X(u);const x=O(t.target);if(x){const g=x.getBoundingClientRect(),{scaleX:S,scaleY:M,left:s,top:c}=i,h={left:(g.left-s)/S,top:(g.top-c)/M,right:(g.right-s)/S,bottom:(g.bottom-c)/M};A(h,a)||(a=h),n={x:(h.left+h.right)/2,y:(h.top+h.bottom)/2}}else if(D(t.target))a=null,n={x:t.target.x,y:t.target.y};else{a=null,n=null,l=null,r=null,d=null,y();return}B(),o()}function C(){W()}typeof window<"u"&&(window.addEventListener("resize",C),window.addEventListener("scroll",C,!0));function I(){if(typeof ResizeObserver>"u")return;p?.disconnect(),p=new ResizeObserver(()=>W());const u=e.getRootEl(),x=e.getBoxEl();u&&p.observe(u),x&&p.observe(x)}const v=W;function w(){I(),v()}return{update:w,setConfig(u){t={...t,...u},w()},getConfig:()=>t,getState:()=>b,destroy(){f?.cancel(),p?.disconnect(),typeof window<"u"&&(window.removeEventListener("resize",C),window.removeEventListener("scroll",C,!0))}}}const et=["data-placement"],nt=["d","fill"],ot=["d","stroke","stroke-width"],L=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(e,{expose:t}){const n=e,a=m.ref(null),l=m.ref(null),i=m.ref({hasTarget:!1,placement:"none",tip:null,slotTip:null,overlayStyle:{},boxStyle:{},rootIsBox:!0,tail:null,boxFill:"",boxStroke:"",boxStrokeWidth:0,boxZIndex:"auto"});function d(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 r=U({getRootEl:()=>a.value,getBoxEl:()=>l.value,onChange:o=>{i.value=o}});function f(){r.setConfig({target:d(n.target),placement:n.placement,targetAnchor:n.targetAnchor,maxWidth:n.maxWidth,tailLength:n.tailLength,tailBaseWidth:n.tailBaseWidth,padding:n.padding,shadow:n.shadow,transitionDuration:n.transitionDuration})}m.watch(()=>[n.target,typeof n.target=="object"&&n.target&&"x"in n.target?n.target.x:null,typeof n.target=="object"&&n.target&&"y"in n.target?n.target.y:null,n.placement,n.targetAnchor,n.maxWidth,n.tailLength,n.tailBaseWidth,n.padding,n.shadow,n.transitionDuration],f,{flush:"post"}),m.onMounted(()=>{f()}),m.onBeforeUnmount(()=>{r.destroy()});function p(){r.update()}t({update:p});const b=m.computed(()=>i.value.rootIsBox?i.value.boxStyle:i.value.overlayStyle),y=m.computed(()=>i.value.rootIsBox?{display:"contents"}:i.value.boxStyle),k=m.computed(()=>({position:"absolute",inset:"0",width:"100%",height:"100%",overflow:"visible",zIndex:i.value.boxZIndex}));return(o,E)=>(m.openBlock(),m.createElementBlock("div",m.mergeProps({ref_key:"rootRef",ref:a,class:["pointing-bubble",{"pointing-bubble__inner":!e.target}],"data-placement":i.value.placement},e.target?{}:o.$attrs,{style:b.value}),[m.createElementVNode("div",m.mergeProps({ref_key:"boxRef",ref:l,class:{"pointing-bubble__inner":!!e.target}},e.target?o.$attrs:{},{style:y.value}),[m.renderSlot(o.$slots,"default",{placement:i.value.placement,tip:i.value.slotTip},()=>[E[0]||(E[0]=m.createElementVNode("p",{style:{margin:0,fontSize:"14px",fontWeight:600,color:"#1e293b"}}," Callout content ",-1))])],16),e.target&&i.value.tail?(m.openBlock(),m.createElementBlock("svg",{key:0,class:"pointing-bubble__svg",style:m.normalizeStyle(k.value)},[m.createElementVNode("path",{class:"pointing-bubble__path",d:i.value.tail.fill,fill:i.value.boxFill},null,8,nt),i.value.tail.stroke?(m.openBlock(),m.createElementBlock("path",{key:0,class:"pointing-bubble__path",d:i.value.tail.stroke,fill:"none",stroke:i.value.boxStroke,"stroke-width":i.value.boxStrokeWidth,"stroke-linejoin":"round","stroke-linecap":"butt"},null,8,ot)):m.createCommentVNode("",!0)],4)):m.createCommentVNode("",!0)],16,et))}}),lt={install(e,t={}){const n=t.name??"PointingBubble";e.component(n,L),e.component("Callout",L)}};exports.Callout=L;exports.DEFAULT_BUBBLE_CONFIG=P;exports.PointingBubble=L;exports.PointingBubblePlugin=lt;exports.buildTailPaths=Y;exports.computeGeometry=N;exports.createBubbleController=U;exports.default=L;exports.isPoint=D;exports.resolveElement=O;