@bigplay/tree-view 0.1.0 → 0.2.1
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/BracketCard.vue.d.ts +24 -0
- package/dist/TreeView.vue.d.ts +9 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +214 -138
- package/dist/index.js.map +1 -1
- package/dist/useTreeLayout.d.ts +1 -1
- package/dist/useVisibleRect.d.ts +23 -0
- package/package.json +2 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BracketCardMatch, BracketCardRow, BracketCardTeam } from '@bigplay/tree-view-core';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** Матч: нужны только команды. */
|
|
4
|
+
match: BracketCardMatch | null | undefined;
|
|
5
|
+
/** Сколько строк рисовать. У финала — одна. */
|
|
6
|
+
rows?: number;
|
|
7
|
+
/** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */
|
|
8
|
+
myTeamHint?: string;
|
|
9
|
+
/** Текст в ячейке счёта при техническом поражении. */
|
|
10
|
+
techDefeatLabel?: string;
|
|
11
|
+
/** Подсказка по наведению на техническое поражение. */
|
|
12
|
+
techDefeatHint?: string;
|
|
13
|
+
};
|
|
14
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
15
|
+
select: (team: BracketCardTeam, row: BracketCardRow) => any;
|
|
16
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
17
|
+
onSelect?: ((team: BracketCardTeam, row: BracketCardRow) => any) | undefined;
|
|
18
|
+
}>, {
|
|
19
|
+
rows: number;
|
|
20
|
+
myTeamHint: string;
|
|
21
|
+
techDefeatLabel: string;
|
|
22
|
+
techDefeatHint: string;
|
|
23
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
24
|
+
export default _default;
|
package/dist/TreeView.vue.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
8
8
|
/** Данные: объект, массив объектов или плоский список (вместе с `getParentId`). */
|
|
9
9
|
data: T | T[] | null | undefined;
|
|
10
10
|
/** Размеры и направление. Можно передать только часть полей. */
|
|
11
|
-
options?: Partial<TreeViewOptions
|
|
11
|
+
options?: Partial<TreeViewOptions<T>>;
|
|
12
12
|
/** Как достать id узла. По умолчанию — поле `id`. */
|
|
13
13
|
getId?: Accessors<T>["getId"];
|
|
14
14
|
/** Как достать детей. По умолчанию — поле `children`. */
|
|
@@ -35,6 +35,14 @@ declare const _default: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>
|
|
|
35
35
|
scaleLimits?: ScaleLimits;
|
|
36
36
|
/** Дополнительный класс на обёртке узла — строкой или функцией. */
|
|
37
37
|
nodeClass?: string | ((node: LayoutNode<T>) => string);
|
|
38
|
+
/**
|
|
39
|
+
* Рисовать только то, что видно на экране. На больших сетках это
|
|
40
|
+
* главная экономия: в DOM попадает несколько десятков карточек
|
|
41
|
+
* вместо тысячи. На маленьких деревьях ничего не меняет.
|
|
42
|
+
*/
|
|
43
|
+
virtualize?: boolean;
|
|
44
|
+
/** Запас вокруг видимой области в пикселях. */
|
|
45
|
+
overscan?: number;
|
|
38
46
|
} & Partial<{}>> & import('vue').PublicProps;
|
|
39
47
|
expose(exposed: import('vue').ShallowUnwrapRef<{
|
|
40
48
|
fit: (padding?: number) => void;
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),d=require("@bigplay/tree-view-core"),I={class:"tree-view-card"},$={class:"tree-view-card__label"},O=["aria-label"],m=e.defineComponent({__name:"TreeNodeCard",props:{label:{},hasChildren:{type:Boolean},collapsed:{type:Boolean}},emits:["toggle"],setup(n,{emit:r}){const a=r;return(o,s)=>(e.openBlock(),e.createElementBlock("div",I,[e.createElementVNode("span",$,e.toDisplayString(n.label),1),n.hasChildren?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"tree-view-card__toggle","data-tv-no-pan":"","aria-label":n.collapsed?"Развернуть":"Свернуть",onClick:s[0]||(s[0]=e.withModifiers(u=>a("toggle"),["stop"]))},e.toDisplayString(n.collapsed?"+":"−"),9,O)):e.createCommentVNode("",!0)]))}});function b(n){const r=e.ref({...d.IDENTITY_TRANSFORM}),a=new d.PanZoomController({pannable:n.pannable,zoomable:n.zoomable,limits:n.limits,onChange:o=>{r.value=o}});return e.watch(n.element,(o,s,u)=>{o&&(a.attach(o),u(()=>a.detach()))},{immediate:!0}),e.onBeforeUnmount(()=>a.detach()),{transform:r,fit:(o,s)=>a.fit(o,s),zoomIn:()=>a.zoomIn(),zoomOut:()=>a.zoomOut(),reset:()=>a.reset(),setTransform:o=>a.set(o)}}function _(n){const r=e.ref(new Set),a=e.computed(()=>d.toTree(n.data(),n.accessors())),o=e.computed(()=>d.applyCollapsed(a.value,r.value)),s=e.computed(()=>d.layoutTree(o.value,n.options()));function u(v){return r.value.has(v)}function i(v){r.value=d.toggleCollapsed(r.value,v)}function h(){r.value=new Set}return{layout:s,isCollapsed:u,toggle:i,expandAll:h}}const N=["width","height"],P=["d"],L=["onClick"],y=e.defineComponent({__name:"TreeView",props:{data:{},options:{default:void 0},getId:{type:Function,default:void 0},getChildren:{type:Function,default:void 0},getParentId:{type:Function,default:void 0},collapsible:{type:Boolean,default:!1},pannable:{type:Boolean,default:!0},zoomable:{type:Boolean,default:!0},fitOnMount:{type:Boolean,default:!0},size:{default:"fill"},scaleLimits:{default:void 0},nodeClass:{type:[String,Function],default:void 0}},emits:["node-click","toggle","transform"],setup(n,{expose:r,emit:a}){const o=n,s=a,u=e.ref(null),{layout:i,isCollapsed:h,toggle:v,expandAll:C}=_({data:()=>o.data,options:()=>o.options,accessors:()=>({getId:o.getId,getChildren:o.getChildren,getParentId:o.getParentId})}),c=b({element:u,pannable:()=>o.pannable,zoomable:()=>o.zoomable,limits:()=>o.scaleLimits??d.DEFAULT_SCALE_LIMITS}),x=e.computed(()=>({width:`${i.value.width}px`,height:`${i.value.height}px`,transform:d.toCssTransform(c.transform.value)})),T=e.computed(()=>{if(o.size!=="content")return;const{scale:t}=c.transform.value;return{width:`${i.value.width*t}px`,height:`${i.value.height*t}px`,overflow:"visible"}});function z(t){return{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`}}function B(t){return typeof o.nodeClass=="function"?o.nodeClass(t):o.nodeClass}function S(t){const p=t.data;return p?.name??p?.title??p?.label??t.id}function f(t){v(t.id),s("toggle",t,h(t.id))}function E(t,p){s("node-click",t,p),o.collapsible&&t.hasChildren&&f(t)}function g(t){c.fit({width:i.value.width,height:i.value.height},t)}return e.onMounted(()=>{o.fitOnMount&&e.nextTick(()=>g())}),e.watch(()=>c.transform.value,t=>s("transform",t)),r({fit:g,zoomIn:c.zoomIn,zoomOut:c.zoomOut,resetTransform:c.reset,setTransform:c.setTransform,expandAll:C,toggle:v,layout:i,transform:c.transform}),(t,p)=>(e.openBlock(),e.createElementBlock("div",{ref_key:"rootElement",ref:u,class:e.normalizeClass(["tree-view",{"tree-view--pannable":n.pannable,"tree-view--content":n.size==="content"}]),style:e.normalizeStyle(T.value)},[e.createElementVNode("div",{class:"tree-view__canvas",style:e.normalizeStyle(x.value)},[(e.openBlock(),e.createElementBlock("svg",{class:"tree-view__links",width:e.unref(i).width,height:e.unref(i).height},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(i).links,l=>e.renderSlot(t.$slots,"link",{link:l},()=>[e.createElementVNode("path",{class:"tree-view__link",d:l.path},null,8,P)],void 0,l.id)),128))],8,N)),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(i).nodes,l=>(e.openBlock(),e.createElementBlock("div",{key:l.id,class:e.normalizeClass(["tree-view__node",B(l)]),style:e.normalizeStyle(z(l)),onClick:w=>E(l,w)},[e.renderSlot(t.$slots,"node",{node:l,data:l.data,depth:l.depth,collapsed:l.collapsed,hasChildren:l.hasChildren,toggle:()=>f(l)},()=>[e.createVNode(m,{label:S(l),"has-children":l.hasChildren,collapsed:l.collapsed,onToggle:w=>f(l)},null,8,["label","has-children","collapsed","onToggle"])])],14,L))),128))],4)],6))}}),V=".tree-view{--tv-link-color: #cbd5e1;--tv-link-width: 2px;--tv-node-bg: #ffffff;--tv-node-color: #0f172a;--tv-node-border: 1px solid #e2e8f0;--tv-node-radius: 10px;--tv-node-shadow: 0 1px 2px rgb(15 23 42 / 8%);--tv-node-padding: 8px 12px;--tv-accent: #2563eb;position:relative;width:100%;height:100%;overflow:hidden;touch-action:none}.tree-view--pannable{cursor:grab}.tree-view--content{width:auto;height:auto;overflow:visible}.tree-view__canvas{position:absolute;top:0;left:0;transform-origin:0 0}.tree-view__links{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.tree-view__link{fill:none;stroke:var(--tv-link-color);stroke-width:var(--tv-link-width)}.tree-view__node{position:absolute;box-sizing:border-box;display:flex;align-items:stretch;justify-content:stretch}.tree-view-card{box-sizing:border-box;display:flex;gap:8px;align-items:center;justify-content:space-between;width:100%;height:100%;padding:var(--tv-node-padding);overflow:hidden;color:var(--tv-node-color);background:var(--tv-node-bg);border:var(--tv-node-border);border-radius:var(--tv-node-radius);box-shadow:var(--tv-node-shadow)}.tree-view-card__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-view-card__toggle{flex:none;width:20px;height:20px;padding:0;font:inherit;line-height:1;color:var(--tv-accent);cursor:pointer;background:none;border:none}";d.injectStyles(V);const k={install(n){n.component("TreeView",y),n.component("TreeNodeCard",m)}};Object.defineProperty(exports,"DEFAULT_OPTIONS",{enumerable:!0,get:()=>d.DEFAULT_OPTIONS});exports.TreeNodeCard=m;exports.TreeView=y;exports.TreeViewPlugin=k;exports.default=k;exports.usePanZoom=b;exports.useTreeLayout=_;
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),c=require("@bigplay/tree-view-core"),O={class:"tree-view-card"},D={class:"tree-view-card__label"},I=["aria-label"],k=e.defineComponent({__name:"TreeNodeCard",props:{label:{},hasChildren:{type:Boolean},collapsed:{type:Boolean}},emits:["toggle"],setup(o,{emit:d}){const r=d;return(t,a)=>(e.openBlock(),e.createElementBlock("div",O,[e.createElementVNode("span",D,e.toDisplayString(o.label),1),o.hasChildren?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"tree-view-card__toggle","data-tv-no-pan":"","aria-label":o.collapsed?"Развернуть":"Свернуть",onClick:a[0]||(a[0]=e.withModifiers(u=>r("toggle"),["stop"]))},e.toDisplayString(o.collapsed?"+":"−"),9,I)):e.createCommentVNode("",!0)]))}});function x(o){const d=e.ref({...c.IDENTITY_TRANSFORM}),r=new c.PanZoomController({pannable:o.pannable,zoomable:o.zoomable,limits:o.limits,onChange:t=>{d.value=t}});return e.watch(o.element,(t,a,u)=>{t&&(r.attach(t),u(()=>r.detach()))},{immediate:!0}),e.onBeforeUnmount(()=>r.detach()),{transform:d,fit:(t,a)=>r.fit(t,a),zoomIn:()=>r.zoomIn(),zoomOut:()=>r.zoomOut(),reset:()=>r.reset(),setTransform:t=>r.set(t)}}function C(o){const d=e.ref(new Set),r=e.computed(()=>c.toTree(o.data(),o.accessors())),t=e.computed(()=>c.applyCollapsed(r.value,d.value)),a=e.computed(()=>c.layoutTree(t.value,o.options()));function u(p){return d.value.has(p)}function m(p){d.value=c.toggleCollapsed(d.value,p)}function l(){d.value=new Set}return{layout:a,isCollapsed:u,toggle:m,expandAll:l}}function M(o,d,r){const t=e.ref(r()?c.estimateVisibleRect():null);function a(){t.value=r()?c.measureVisibleRect(o.value,d()):null}return e.onMounted(()=>{a(),window.addEventListener("scroll",a,{passive:!0,capture:!0}),window.addEventListener("resize",a,{passive:!0})}),e.onBeforeUnmount(()=>{window.removeEventListener("scroll",a,{capture:!0}),window.removeEventListener("resize",a)}),{rect:t,update:a}}const P=["width","height"],F=["d"],j=["onClick"],B=e.defineComponent({__name:"TreeView",props:{data:{},options:{default:void 0},getId:{type:Function,default:void 0},getChildren:{type:Function,default:void 0},getParentId:{type:Function,default:void 0},collapsible:{type:Boolean,default:!1},pannable:{type:Boolean,default:!0},zoomable:{type:Boolean,default:!0},fitOnMount:{type:Boolean,default:!0},size:{default:"fill"},scaleLimits:{default:void 0},nodeClass:{type:[String,Function],default:void 0},virtualize:{type:Boolean,default:!0},overscan:{default:c.DEFAULT_OVERSCAN}},emits:["node-click","toggle","transform"],setup(o,{expose:d,emit:r}){const t=o,a=r,u=e.ref(null),m=e.ref(null),{layout:l,isCollapsed:p,toggle:s,expandAll:b}=C({data:()=>t.data,options:()=>t.options,accessors:()=>({getId:t.getId,getChildren:t.getChildren,getParentId:t.getParentId})}),v=x({element:u,pannable:()=>t.pannable,zoomable:()=>t.zoomable,limits:()=>t.scaleLimits??c.DEFAULT_SCALE_LIMITS}),h=M(m,()=>v.transform.value.scale,()=>t.virtualize),w=e.computed(()=>t.virtualize?c.visibleLayout(l.value,h.rect.value,t.overscan):l.value),E=e.computed(()=>({width:`${l.value.width}px`,height:`${l.value.height}px`,transform:c.toCssTransform(v.transform.value)})),S=e.computed(()=>{if(t.size!=="content")return;const{scale:n}=v.transform.value;return{width:`${l.value.width*n}px`,height:`${l.value.height*n}px`,overflow:"visible"}});function $(n){return{left:`${n.x}px`,top:`${n.y}px`,width:`${n.width}px`,height:`${n.height}px`}}function L(n){return typeof t.nodeClass=="function"?t.nodeClass(n):t.nodeClass}function N(n){const f=n.data;return f?.name??f?.title??f?.label??n.id}function g(n){s(n.id),a("toggle",n,p(n.id))}function V(n,f){a("node-click",n,f),t.collapsible&&n.hasChildren&&g(n)}function _(n){v.fit({width:l.value.width,height:l.value.height},n)}return e.onMounted(()=>{t.fitOnMount&&e.nextTick(()=>_())}),e.watch(()=>v.transform.value,n=>{h.update(),a("transform",n)}),e.watch(l,()=>{e.nextTick(()=>h.update())}),d({fit:_,zoomIn:v.zoomIn,zoomOut:v.zoomOut,resetTransform:v.reset,setTransform:v.setTransform,expandAll:b,toggle:s,layout:l,transform:v.transform}),(n,f)=>(e.openBlock(),e.createElementBlock("div",{ref_key:"rootElement",ref:u,class:e.normalizeClass(["tree-view",{"tree-view--pannable":o.pannable,"tree-view--content":o.size==="content"}]),style:e.normalizeStyle(S.value)},[e.createElementVNode("div",{ref_key:"canvasElement",ref:m,class:"tree-view__canvas",style:e.normalizeStyle(E.value)},[(e.openBlock(),e.createElementBlock("svg",{class:"tree-view__links",width:e.unref(l).width,height:e.unref(l).height},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(w.value.links,i=>e.renderSlot(n.$slots,"link",{link:i},()=>[e.createElementVNode("path",{class:"tree-view__link",d:i.path},null,8,F)],void 0,i.id)),128))],8,P)),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(w.value.nodes,i=>(e.openBlock(),e.createElementBlock("div",{key:i.id,class:e.normalizeClass(["tree-view__node",L(i)]),style:e.normalizeStyle($(i)),onClick:y=>V(i,y)},[e.renderSlot(n.$slots,"node",{node:i,data:i.data,depth:i.depth,collapsed:i.collapsed,hasChildren:i.hasChildren,toggle:()=>g(i)},()=>[e.createVNode(k,{label:N(i),"has-children":i.hasChildren,collapsed:i.collapsed,onToggle:y=>g(i)},null,8,["label","has-children","collapsed","onToggle"])])],14,j))),128))],4)],6))}}),A={key:0,class:"tv-bracket__hint"},H=["title","onClick","onMouseenter"],R={key:0,class:"tv-bracket__tooltip"},U={class:"tv-bracket__name"},Z={class:"tv-bracket__score"},T=e.defineComponent({__name:"BracketCard",props:{match:{},rows:{default:void 0},myTeamHint:{default:"Нажми для перехода в матч 👇"},techDefeatLabel:{default:"ТП"},techDefeatHint:{default:"Техническое поражение"}},emits:["select"],setup(o,{emit:d}){const r=o,t=d,a=e.ref(null),u=e.computed(()=>c.buildBracketCard(r.match,{rows:r.rows,techDefeatLabel:r.techDefeatLabel}));function m(l){l.team&&t("select",l.team,l)}return(l,p)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["tv-bracket",{"tv-bracket--my-team":u.value.hasMyTeam}])},[u.value.hasMyTeam&&o.myTeamHint?(e.openBlock(),e.createElementBlock("div",A,e.toDisplayString(o.myTeamHint),1)):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value.rows,s=>(e.openBlock(),e.createElementBlock("div",{key:s.key,class:e.normalizeClass(["tv-bracket__row",{"tv-bracket__row--winner":s.isWinner,"tv-bracket__row--loser":s.isLoser&&!s.isEmpty,"tv-bracket__row--my":s.isMyTeam,"tv-bracket__row--empty":s.isEmpty}]),title:s.isMyTeam?"Моя команда":void 0,onClick:b=>m(s),onMouseenter:b=>a.value=s.key,onMouseleave:p[0]||(p[0]=b=>a.value=null)},[s.team?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[s.isTechDefeat&&a.value===s.key?(e.openBlock(),e.createElementBlock("span",R,e.toDisplayString(o.techDefeatHint),1)):e.createCommentVNode("",!0),e.createElementVNode("span",U,e.toDisplayString(s.team.name),1),e.createElementVNode("span",Z,e.toDisplayString(s.score),1)],64)):e.createCommentVNode("",!0)],42,H))),128))],2))}}),q=".tree-view{--tv-link-color: #cbd5e1;--tv-link-width: 2px;--tv-node-bg: #ffffff;--tv-node-color: #0f172a;--tv-node-border: 1px solid #e2e8f0;--tv-node-radius: 10px;--tv-node-shadow: 0 1px 2px rgb(15 23 42 / 8%);--tv-node-padding: 8px 12px;--tv-accent: #2563eb;position:relative;width:100%;height:100%;overflow:hidden;touch-action:none}.tree-view--pannable{cursor:grab}.tree-view--content{width:auto;height:auto;overflow:visible}.tree-view__canvas{position:absolute;top:0;left:0;transform-origin:0 0}.tree-view__links{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.tree-view__link{fill:none;stroke:var(--tv-link-color);stroke-width:var(--tv-link-width)}.tree-view__node{position:absolute;box-sizing:border-box;display:flex;align-items:stretch;justify-content:stretch}.tree-view-card{box-sizing:border-box;display:flex;gap:8px;align-items:center;justify-content:space-between;width:100%;height:100%;padding:var(--tv-node-padding);overflow:hidden;color:var(--tv-node-color);background:var(--tv-node-bg);border:var(--tv-node-border);border-radius:var(--tv-node-radius);box-shadow:var(--tv-node-shadow)}.tree-view-card__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-view-card__toggle{flex:none;width:20px;height:20px;padding:0;font:inherit;line-height:1;color:var(--tv-accent);cursor:pointer;background:none;border:none}.tv-bracket{--tv-bracket-winner-bg: #0427c4;--tv-bracket-winner-color: #ffffff;--tv-bracket-loser-bg: #e5e5e5;--tv-bracket-loser-color: #7d7d8d;--tv-bracket-my-bg: #00146c;--tv-bracket-my-color: #ffffff;--tv-bracket-radius: 6px;position:relative;box-sizing:border-box;display:flex;flex-direction:column;width:100%;height:100%;font-size:12px}.tv-bracket--my-team{border-radius:var(--tv-bracket-radius);outline:2px solid var(--tv-bracket-my-bg)}.tv-bracket__hint{position:absolute;right:0;bottom:100%;left:0;padding:3px 8px;overflow:hidden;font-size:11px;font-weight:600;line-height:1.2;color:var(--tv-bracket-my-color);text-align:center;text-overflow:ellipsis;white-space:nowrap;background:var(--tv-bracket-my-bg);border-radius:var(--tv-bracket-radius) var(--tv-bracket-radius) 0 0}.tv-bracket__row{position:relative;box-sizing:border-box;display:flex;flex:1 1 0;align-items:center;justify-content:space-between;min-height:0;padding:4px 8px;cursor:pointer;transition:all 75ms}.tv-bracket__row--winner{font-weight:700;color:var(--tv-bracket-winner-color);background:var(--tv-bracket-winner-bg)}.tv-bracket__row--loser{color:var(--tv-bracket-loser-color);background:var(--tv-bracket-loser-bg)}.tv-bracket__row--my{color:var(--tv-bracket-my-color);background:var(--tv-bracket-my-bg)}.tv-bracket__row--empty{cursor:default;background:var(--tv-bracket-loser-bg)}.tv-bracket__name{max-width:120px;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.tv-bracket__score{display:flex;flex:none;align-items:center;justify-content:center;width:20px;height:20px;font-weight:700;font-variant-numeric:tabular-nums}.tv-bracket__tooltip{position:absolute;top:-26px;left:6px;z-index:5000;padding:4px 8px;font-weight:400;color:var(--tv-bracket-loser-color);white-space:nowrap;background:#fff;border:1px solid #d1d5db;border-radius:4px;box-shadow:0 4px 10px #0f172a26}";c.injectStyles(q);const z={install(o){o.component("TreeView",B),o.component("TreeNodeCard",k),o.component("BracketCard",T)}};Object.defineProperty(exports,"DEFAULT_OPTIONS",{enumerable:!0,get:()=>c.DEFAULT_OPTIONS});Object.defineProperty(exports,"bracketCardHeight",{enumerable:!0,get:()=>c.bracketCardHeight});Object.defineProperty(exports,"buildBracketCard",{enumerable:!0,get:()=>c.buildBracketCard});exports.BracketCard=T;exports.TreeNodeCard=k;exports.TreeView=B;exports.TreeViewPlugin=z;exports.default=z;exports.usePanZoom=x;exports.useTreeLayout=C;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/TreeNodeCard.vue","../src/usePanZoom.ts","../src/useTreeLayout.ts","../src/TreeView.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Карточка узла по умолчанию.\n * Нужна только чтобы «из коробки» что-то отображалось —\n * в реальном проекте обычно используют слот #node и рисуют свой блок.\n */\ndefineProps<{\n label: string\n hasChildren?: boolean\n collapsed?: boolean\n}>()\n\nconst emit = defineEmits<{ (e: 'toggle'): void }>()\n</script>\n\n<template>\n <div class=\"tree-view-card\">\n <span class=\"tree-view-card__label\">{{ label }}</span>\n\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"tree-view-card__toggle\"\n data-tv-no-pan\n :aria-label=\"collapsed ? 'Развернуть' : 'Свернуть'\"\n @click.stop=\"emit('toggle')\"\n >\n {{ collapsed ? '+' : '−' }}\n </button>\n </div>\n</template>\n","import { onBeforeUnmount, ref, watch, type Ref } from 'vue'\nimport {\n IDENTITY_TRANSFORM,\n PanZoomController,\n type ScaleLimits,\n type Size,\n type Transform,\n} from '@bigplay/tree-view-core'\n\nexport interface UsePanZoomParams {\n /** Элемент, который слушаем: по нему тянут и крутят колесо. */\n element: Ref<HTMLElement | null>\n pannable: () => boolean\n zoomable: () => boolean\n limits: () => ScaleLimits\n}\n\n/**\n * Тонкая обёртка над `PanZoomController` из core:\n * переносит его состояние в реактивный `ref` и следит за жизненным циклом.\n */\nexport function usePanZoom(params: UsePanZoomParams) {\n const transform = ref<Transform>({ ...IDENTITY_TRANSFORM })\n\n const controller = new PanZoomController({\n pannable: params.pannable,\n zoomable: params.zoomable,\n limits: params.limits,\n onChange: (next) => {\n transform.value = next\n },\n })\n\n watch(\n params.element,\n (element, _previous, onCleanup) => {\n if (!element) return\n\n controller.attach(element)\n onCleanup(() => controller.detach())\n },\n { immediate: true },\n )\n\n onBeforeUnmount(() => controller.detach())\n\n return {\n transform,\n fit: (content: Size, padding?: number) => controller.fit(content, padding),\n zoomIn: () => controller.zoomIn(),\n zoomOut: () => controller.zoomOut(),\n reset: () => controller.reset(),\n setTransform: (next: Transform) => controller.set(next),\n }\n}\n","import { computed, ref, type ComputedRef } from 'vue'\nimport {\n applyCollapsed,\n layoutTree,\n toggleCollapsed,\n toTree,\n type Accessors,\n type Layout,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/** Функции-геттеры, чтобы композабл видел свежие значения пропов. */\nexport interface UseTreeLayoutParams<T> {\n data: () => T | T[] | null | undefined\n options: () => Partial<TreeViewOptions> | undefined\n accessors: () => Accessors<T>\n}\n\nexport interface UseTreeLayoutResult<T> {\n /** Готовая раскладка: узлы с координатами и линии. */\n layout: ComputedRef<Layout<T>>\n /** Свёрнут ли узел с таким id. */\n isCollapsed: (id: string) => boolean\n /** Свернуть/развернуть узел. */\n toggle: (id: string) => void\n /** Развернуть всё дерево. */\n expandAll: () => void\n}\n\n/**\n * Вся «логика» компонента: данные → дерево → раскладка.\n * Каждый шаг — отдельная чистая функция из core, здесь только реактивность.\n */\nexport function useTreeLayout<T>(params: UseTreeLayoutParams<T>): UseTreeLayoutResult<T> {\n const collapsedIds = ref(new Set<string>())\n\n const tree = computed(() => toTree(params.data(), params.accessors()))\n const visibleTree = computed(() => applyCollapsed(tree.value, collapsedIds.value))\n const layout = computed(() => layoutTree(visibleTree.value, params.options()))\n\n function isCollapsed(id: string): boolean {\n return collapsedIds.value.has(id)\n }\n\n function toggle(id: string): void {\n // Новое множество вместо мутации — иначе Vue не заметит изменения.\n collapsedIds.value = toggleCollapsed(collapsedIds.value, id)\n }\n\n function expandAll(): void {\n collapsedIds.value = new Set()\n }\n\n return { layout, isCollapsed, toggle, expandAll }\n}\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Компонент-обёртка. Вся математика — в пакете core, здесь только\n * реактивность, разметка и события.\n */\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\nimport {\n DEFAULT_SCALE_LIMITS,\n toCssTransform,\n type Accessors,\n type LayoutNode,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport { usePanZoom } from './usePanZoom'\nimport { useTreeLayout } from './useTreeLayout'\n\nconst props = withDefaults(\n defineProps<{\n /** Данные: объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: T | T[] | null | undefined\n /** Размеры и направление. Можно передать только часть полей. */\n options?: Partial<TreeViewOptions>\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId?: Accessors<T>['getId']\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren?: Accessors<T>['getChildren']\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId?: Accessors<T>['getParentId']\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible?: boolean\n /** Разрешить перетаскивание холста. */\n pannable?: boolean\n /** Разрешить зум колесом и «щипком». */\n zoomable?: boolean\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount?: boolean\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается,\n * дерево двигают мышью;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n * Нужен, когда сетка стоит внутри страницы со своим скроллом.\n */\n size?: 'fill' | 'content'\n /** Пределы масштаба. */\n scaleLimits?: ScaleLimits\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass?: string | ((node: LayoutNode<T>) => string)\n }>(),\n {\n options: undefined,\n getId: undefined,\n getChildren: undefined,\n getParentId: undefined,\n collapsible: false,\n pannable: true,\n zoomable: true,\n fitOnMount: true,\n size: 'fill',\n scaleLimits: undefined,\n nodeClass: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'node-click', node: LayoutNode<T>, event: MouseEvent): void\n (e: 'toggle', node: LayoutNode<T>, collapsed: boolean): void\n (e: 'transform', transform: Transform): void\n}>()\n\nconst rootElement = ref<HTMLElement | null>(null)\n\nconst { layout, isCollapsed, toggle, expandAll } = useTreeLayout<T>({\n data: () => props.data,\n options: () => props.options,\n accessors: () => ({\n getId: props.getId,\n getChildren: props.getChildren,\n getParentId: props.getParentId,\n }),\n})\n\nconst panZoom = usePanZoom({\n element: rootElement,\n pannable: () => props.pannable,\n zoomable: () => props.zoomable,\n limits: () => props.scaleLimits ?? DEFAULT_SCALE_LIMITS,\n})\n\nconst canvasStyle = computed(() => ({\n width: `${layout.value.width}px`,\n height: `${layout.value.height}px`,\n transform: toCssTransform(panZoom.transform.value),\n}))\n\nconst rootStyle = computed(() => {\n if (props.size !== 'content') return undefined\n\n const { scale } = panZoom.transform.value\n return {\n width: `${layout.value.width * scale}px`,\n height: `${layout.value.height * scale}px`,\n overflow: 'visible',\n }\n})\n\nfunction nodeStyle(node: LayoutNode<T>) {\n return {\n left: `${node.x}px`,\n top: `${node.y}px`,\n width: `${node.width}px`,\n height: `${node.height}px`,\n }\n}\n\nfunction nodeClassOf(node: LayoutNode<T>): string | undefined {\n return typeof props.nodeClass === 'function' ? props.nodeClass(node) : props.nodeClass\n}\n\n/** Подпись карточки по умолчанию: name → title → label → id. */\nfunction labelOf(node: LayoutNode<T>): string {\n const data = node.data as { name?: string; title?: string; label?: string } | null\n return data?.name ?? data?.title ?? data?.label ?? node.id\n}\n\nfunction toggleNode(node: LayoutNode<T>): void {\n toggle(node.id)\n emit('toggle', node, isCollapsed(node.id))\n}\n\nfunction onNodeClick(node: LayoutNode<T>, event: MouseEvent): void {\n emit('node-click', node, event)\n if (props.collapsible && node.hasChildren) toggleNode(node)\n}\n\n/** Вписывает дерево в видимую область. */\nfunction fit(padding?: number): void {\n panZoom.fit({ width: layout.value.width, height: layout.value.height }, padding)\n}\n\nonMounted(() => {\n if (props.fitOnMount) void nextTick(() => fit())\n})\n\nwatch(\n () => panZoom.transform.value,\n (transform) => emit('transform', transform),\n)\n\ndefineExpose({\n fit,\n zoomIn: panZoom.zoomIn,\n zoomOut: panZoom.zoomOut,\n resetTransform: panZoom.reset,\n setTransform: panZoom.setTransform,\n expandAll,\n toggle,\n layout,\n transform: panZoom.transform,\n})\n</script>\n\n<template>\n <div\n ref=\"rootElement\"\n class=\"tree-view\"\n :class=\"{ 'tree-view--pannable': pannable, 'tree-view--content': size === 'content' }\"\n :style=\"rootStyle\"\n >\n <div class=\"tree-view__canvas\" :style=\"canvasStyle\">\n <svg class=\"tree-view__links\" :width=\"layout.width\" :height=\"layout.height\">\n <template v-for=\"link in layout.links\" :key=\"link.id\">\n <slot name=\"link\" :link=\"link\">\n <path class=\"tree-view__link\" :d=\"link.path\" />\n </slot>\n </template>\n </svg>\n\n <div\n v-for=\"node in layout.nodes\"\n :key=\"node.id\"\n class=\"tree-view__node\"\n :class=\"nodeClassOf(node)\"\n :style=\"nodeStyle(node)\"\n @click=\"onNodeClick(node, $event)\"\n >\n <!--\n Главная точка кастомизации: сюда подставляют свою карточку.\n Доступно всё, что может понадобиться для отрисовки и управления.\n -->\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"node.data\"\n :depth=\"node.depth\"\n :collapsed=\"node.collapsed\"\n :has-children=\"node.hasChildren\"\n :toggle=\"() => toggleNode(node)\"\n >\n <TreeNodeCard\n :label=\"labelOf(node)\"\n :has-children=\"node.hasChildren\"\n :collapsed=\"node.collapsed\"\n @toggle=\"toggleNode(node)\"\n />\n </slot>\n </div>\n </div>\n </div>\n</template>\n","import type { App, Plugin } from 'vue'\nimport TreeView from './TreeView.vue'\nimport TreeNodeCard from './TreeNodeCard.vue'\n// Стили общие для Vue 2 и Vue 3 — лежат в core, чтобы не расходились.\n// Они попадают прямо в бандл и подключаются сами: отдельный импорт в проекте не нужен.\nimport { injectStyles } from '@bigplay/tree-view-core'\nimport styles from '@bigplay/tree-view-core/style.css?inline'\n\ninjectStyles(styles)\n\nexport { TreeView, TreeNodeCard }\nexport { useTreeLayout } from './useTreeLayout'\nexport { usePanZoom } from './usePanZoom'\n\n// Типы из core переэкспортируем, чтобы их не приходилось ставить отдельно.\nexport type {\n Accessors,\n Direction,\n Layout,\n LayoutLink,\n LayoutNode,\n LinkStyle,\n ScaleLimits,\n Transform,\n TreeNode,\n TreeViewOptions,\n} from '@bigplay/tree-view-core'\nexport { DEFAULT_OPTIONS } from '@bigplay/tree-view-core'\n\n/** Плагин: `app.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin: Plugin = {\n install(app: App) {\n app.component('TreeView', TreeView)\n app.component('TreeNodeCard', TreeNodeCard)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["emit","__emit","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","__props","_hoisted_3","usePanZoom","params","transform","ref","IDENTITY_TRANSFORM","controller","PanZoomController","next","watch","element","_previous","onCleanup","onBeforeUnmount","content","padding","useTreeLayout","collapsedIds","tree","computed","toTree","visibleTree","applyCollapsed","layout","layoutTree","isCollapsed","id","toggle","toggleCollapsed","expandAll","props","rootElement","panZoom","DEFAULT_SCALE_LIMITS","canvasStyle","toCssTransform","rootStyle","scale","nodeStyle","node","nodeClassOf","labelOf","data","toggleNode","onNodeClick","event","fit","onMounted","nextTick","__expose","_normalizeClass","_unref","_Fragment","_renderList","link","_renderSlot","_ctx","_normalizeStyle","$event","_createVNode","TreeNodeCard","injectStyles","styles","TreeViewPlugin","app","TreeView"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/TreeNodeCard.vue","../src/usePanZoom.ts","../src/useTreeLayout.ts","../src/useVisibleRect.ts","../src/TreeView.vue","../src/BracketCard.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Карточка узла по умолчанию.\n * Нужна только чтобы «из коробки» что-то отображалось —\n * в реальном проекте обычно используют слот #node и рисуют свой блок.\n */\ndefineProps<{\n label: string\n hasChildren?: boolean\n collapsed?: boolean\n}>()\n\nconst emit = defineEmits<{ (e: 'toggle'): void }>()\n</script>\n\n<template>\n <div class=\"tree-view-card\">\n <span class=\"tree-view-card__label\">{{ label }}</span>\n\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"tree-view-card__toggle\"\n data-tv-no-pan\n :aria-label=\"collapsed ? 'Развернуть' : 'Свернуть'\"\n @click.stop=\"emit('toggle')\"\n >\n {{ collapsed ? '+' : '−' }}\n </button>\n </div>\n</template>\n","import { onBeforeUnmount, ref, watch, type Ref } from 'vue'\nimport {\n IDENTITY_TRANSFORM,\n PanZoomController,\n type ScaleLimits,\n type Size,\n type Transform,\n} from '@bigplay/tree-view-core'\n\nexport interface UsePanZoomParams {\n /** Элемент, который слушаем: по нему тянут и крутят колесо. */\n element: Ref<HTMLElement | null>\n pannable: () => boolean\n zoomable: () => boolean\n limits: () => ScaleLimits\n}\n\n/**\n * Тонкая обёртка над `PanZoomController` из core:\n * переносит его состояние в реактивный `ref` и следит за жизненным циклом.\n */\nexport function usePanZoom(params: UsePanZoomParams) {\n const transform = ref<Transform>({ ...IDENTITY_TRANSFORM })\n\n const controller = new PanZoomController({\n pannable: params.pannable,\n zoomable: params.zoomable,\n limits: params.limits,\n onChange: (next) => {\n transform.value = next\n },\n })\n\n watch(\n params.element,\n (element, _previous, onCleanup) => {\n if (!element) return\n\n controller.attach(element)\n onCleanup(() => controller.detach())\n },\n { immediate: true },\n )\n\n onBeforeUnmount(() => controller.detach())\n\n return {\n transform,\n fit: (content: Size, padding?: number) => controller.fit(content, padding),\n zoomIn: () => controller.zoomIn(),\n zoomOut: () => controller.zoomOut(),\n reset: () => controller.reset(),\n setTransform: (next: Transform) => controller.set(next),\n }\n}\n","import { computed, ref, type ComputedRef } from 'vue'\nimport {\n applyCollapsed,\n layoutTree,\n toggleCollapsed,\n toTree,\n type Accessors,\n type Layout,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/** Функции-геттеры, чтобы композабл видел свежие значения пропов. */\nexport interface UseTreeLayoutParams<T> {\n data: () => T | T[] | null | undefined\n options: () => Partial<TreeViewOptions<T>> | undefined\n accessors: () => Accessors<T>\n}\n\nexport interface UseTreeLayoutResult<T> {\n /** Готовая раскладка: узлы с координатами и линии. */\n layout: ComputedRef<Layout<T>>\n /** Свёрнут ли узел с таким id. */\n isCollapsed: (id: string) => boolean\n /** Свернуть/развернуть узел. */\n toggle: (id: string) => void\n /** Развернуть всё дерево. */\n expandAll: () => void\n}\n\n/**\n * Вся «логика» компонента: данные → дерево → раскладка.\n * Каждый шаг — отдельная чистая функция из core, здесь только реактивность.\n */\nexport function useTreeLayout<T>(params: UseTreeLayoutParams<T>): UseTreeLayoutResult<T> {\n const collapsedIds = ref(new Set<string>())\n\n const tree = computed(() => toTree(params.data(), params.accessors()))\n const visibleTree = computed(() => applyCollapsed(tree.value, collapsedIds.value))\n const layout = computed(() => layoutTree(visibleTree.value, params.options()))\n\n function isCollapsed(id: string): boolean {\n return collapsedIds.value.has(id)\n }\n\n function toggle(id: string): void {\n // Новое множество вместо мутации — иначе Vue не заметит изменения.\n collapsedIds.value = toggleCollapsed(collapsedIds.value, id)\n }\n\n function expandAll(): void {\n collapsedIds.value = new Set()\n }\n\n return { layout, isCollapsed, toggle, expandAll }\n}\n","import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'\nimport { estimateVisibleRect, measureVisibleRect, type Rect } from '@bigplay/tree-view-core'\n\n/**\n * Следит за тем, какая часть холста сейчас видна.\n *\n * Пересчитывается там же, где это меняется: при перетаскивании и зуме,\n * при скролле страницы и при изменении размеров окна. Отдельный троттлинг\n * не нужен — и те, и другие события браузер отдаёт не чаще кадра.\n */\nexport function useVisibleRect(canvas: Ref<HTMLElement | null>, scale: () => number, enabled: () => boolean) {\n // До первого измерения берём размер окна — иначе первый кадр нарисует всё дерево.\n const rect = ref<Rect | null>(enabled() ? estimateVisibleRect() : null)\n\n function update(): void {\n rect.value = enabled() ? measureVisibleRect(canvas.value, scale()) : null\n }\n\n onMounted(() => {\n update()\n window.addEventListener('scroll', update, { passive: true, capture: true })\n window.addEventListener('resize', update, { passive: true })\n })\n\n onBeforeUnmount(() => {\n window.removeEventListener('scroll', update, { capture: true })\n window.removeEventListener('resize', update)\n })\n\n return { rect, update }\n}\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Компонент-обёртка. Вся математика — в пакете core, здесь только\n * реактивность, разметка и события.\n */\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\nimport {\n DEFAULT_OVERSCAN,\n DEFAULT_SCALE_LIMITS,\n toCssTransform,\n visibleLayout,\n type Accessors,\n type LayoutNode,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport { usePanZoom } from './usePanZoom'\nimport { useTreeLayout } from './useTreeLayout'\nimport { useVisibleRect } from './useVisibleRect'\n\nconst props = withDefaults(\n defineProps<{\n /** Данные: объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: T | T[] | null | undefined\n /** Размеры и направление. Можно передать только часть полей. */\n options?: Partial<TreeViewOptions<T>>\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId?: Accessors<T>['getId']\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren?: Accessors<T>['getChildren']\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId?: Accessors<T>['getParentId']\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible?: boolean\n /** Разрешить перетаскивание холста. */\n pannable?: boolean\n /** Разрешить зум колесом и «щипком». */\n zoomable?: boolean\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount?: boolean\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается,\n * дерево двигают мышью;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n * Нужен, когда сетка стоит внутри страницы со своим скроллом.\n */\n size?: 'fill' | 'content'\n /** Пределы масштаба. */\n scaleLimits?: ScaleLimits\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass?: string | ((node: LayoutNode<T>) => string)\n /**\n * Рисовать только то, что видно на экране. На больших сетках это\n * главная экономия: в DOM попадает несколько десятков карточек\n * вместо тысячи. На маленьких деревьях ничего не меняет.\n */\n virtualize?: boolean\n /** Запас вокруг видимой области в пикселях. */\n overscan?: number\n }>(),\n {\n options: undefined,\n getId: undefined,\n getChildren: undefined,\n getParentId: undefined,\n collapsible: false,\n pannable: true,\n zoomable: true,\n fitOnMount: true,\n size: 'fill',\n scaleLimits: undefined,\n nodeClass: undefined,\n virtualize: true,\n overscan: DEFAULT_OVERSCAN,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'node-click', node: LayoutNode<T>, event: MouseEvent): void\n (e: 'toggle', node: LayoutNode<T>, collapsed: boolean): void\n (e: 'transform', transform: Transform): void\n}>()\n\nconst rootElement = ref<HTMLElement | null>(null)\nconst canvasElement = ref<HTMLElement | null>(null)\n\nconst { layout, isCollapsed, toggle, expandAll } = useTreeLayout<T>({\n data: () => props.data,\n options: () => props.options,\n accessors: () => ({\n getId: props.getId,\n getChildren: props.getChildren,\n getParentId: props.getParentId,\n }),\n})\n\nconst panZoom = usePanZoom({\n element: rootElement,\n pannable: () => props.pannable,\n zoomable: () => props.zoomable,\n limits: () => props.scaleLimits ?? DEFAULT_SCALE_LIMITS,\n})\n\nconst visible = useVisibleRect(\n canvasElement,\n () => panZoom.transform.value.scale,\n () => props.virtualize,\n)\n\n/** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\nconst drawn = computed(() =>\n props.virtualize ? visibleLayout(layout.value, visible.rect.value, props.overscan) : layout.value,\n)\n\nconst canvasStyle = computed(() => ({\n width: `${layout.value.width}px`,\n height: `${layout.value.height}px`,\n transform: toCssTransform(panZoom.transform.value),\n}))\n\nconst rootStyle = computed(() => {\n if (props.size !== 'content') return undefined\n\n const { scale } = panZoom.transform.value\n return {\n width: `${layout.value.width * scale}px`,\n height: `${layout.value.height * scale}px`,\n overflow: 'visible',\n }\n})\n\nfunction nodeStyle(node: LayoutNode<T>) {\n return {\n left: `${node.x}px`,\n top: `${node.y}px`,\n width: `${node.width}px`,\n height: `${node.height}px`,\n }\n}\n\nfunction nodeClassOf(node: LayoutNode<T>): string | undefined {\n return typeof props.nodeClass === 'function' ? props.nodeClass(node) : props.nodeClass\n}\n\n/** Подпись карточки по умолчанию: name → title → label → id. */\nfunction labelOf(node: LayoutNode<T>): string {\n const data = node.data as { name?: string; title?: string; label?: string } | null\n return data?.name ?? data?.title ?? data?.label ?? node.id\n}\n\nfunction toggleNode(node: LayoutNode<T>): void {\n toggle(node.id)\n emit('toggle', node, isCollapsed(node.id))\n}\n\nfunction onNodeClick(node: LayoutNode<T>, event: MouseEvent): void {\n emit('node-click', node, event)\n if (props.collapsible && node.hasChildren) toggleNode(node)\n}\n\n/** Вписывает дерево в видимую область. */\nfunction fit(padding?: number): void {\n panZoom.fit({ width: layout.value.width, height: layout.value.height }, padding)\n}\n\nonMounted(() => {\n if (props.fitOnMount) void nextTick(() => fit())\n})\n\nwatch(\n () => panZoom.transform.value,\n (transform) => {\n // Холст поехал — видимая часть стала другой.\n visible.update()\n emit('transform', transform)\n },\n)\n\n// Новые данные — и пересчитывать видимую часть надо уже по ним.\nwatch(layout, () => void nextTick(() => visible.update()))\n\ndefineExpose({\n fit,\n zoomIn: panZoom.zoomIn,\n zoomOut: panZoom.zoomOut,\n resetTransform: panZoom.reset,\n setTransform: panZoom.setTransform,\n expandAll,\n toggle,\n layout,\n transform: panZoom.transform,\n})\n</script>\n\n<template>\n <div\n ref=\"rootElement\"\n class=\"tree-view\"\n :class=\"{ 'tree-view--pannable': pannable, 'tree-view--content': size === 'content' }\"\n :style=\"rootStyle\"\n >\n <div ref=\"canvasElement\" class=\"tree-view__canvas\" :style=\"canvasStyle\">\n <svg class=\"tree-view__links\" :width=\"layout.width\" :height=\"layout.height\">\n <template v-for=\"link in drawn.links\" :key=\"link.id\">\n <slot name=\"link\" :link=\"link\">\n <path class=\"tree-view__link\" :d=\"link.path\" />\n </slot>\n </template>\n </svg>\n\n <div\n v-for=\"node in drawn.nodes\"\n :key=\"node.id\"\n class=\"tree-view__node\"\n :class=\"nodeClassOf(node)\"\n :style=\"nodeStyle(node)\"\n @click=\"onNodeClick(node, $event)\"\n >\n <!--\n Главная точка кастомизации: сюда подставляют свою карточку.\n Доступно всё, что может понадобиться для отрисовки и управления.\n -->\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"node.data\"\n :depth=\"node.depth\"\n :collapsed=\"node.collapsed\"\n :has-children=\"node.hasChildren\"\n :toggle=\"() => toggleNode(node)\"\n >\n <TreeNodeCard\n :label=\"labelOf(node)\"\n :has-children=\"node.hasChildren\"\n :collapsed=\"node.collapsed\"\n @toggle=\"toggleNode(node)\"\n />\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Карточка матча турнирной сетки — та самая, что на сайте:\n * победитель синий, проигравший серый, команда пользователя в рамке\n * с подсказкой, техническое поражение показывает «ТП» и объяснение по наведению.\n *\n * Какие строки рисовать — решает `buildBracketCard` из core, поэтому\n * версия для Vue 2 выглядит точно так же.\n */\nimport { computed, ref } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n type BracketCardTeam,\n} from '@bigplay/tree-view-core'\n\nconst props = withDefaults(\n defineProps<{\n /** Матч: нужны только команды. */\n match: BracketCardMatch | null | undefined\n /** Сколько строк рисовать. У финала — одна. */\n rows?: number\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint?: string\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel?: string\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint?: string\n }>(),\n {\n rows: undefined,\n myTeamHint: 'Нажми для перехода в матч 👇',\n techDefeatLabel: 'ТП',\n techDefeatHint: 'Техническое поражение',\n },\n)\n\nconst emit = defineEmits<{ (e: 'select', team: BracketCardTeam, row: BracketCardRow): void }>()\n\nconst hoveredKey = ref<string | null>(null)\n\nconst card = computed(() =>\n buildBracketCard(props.match, { rows: props.rows, techDefeatLabel: props.techDefeatLabel }),\n)\n\nfunction onRowClick(row: BracketCardRow): void {\n if (row.team) emit('select', row.team, row)\n}\n</script>\n\n<template>\n <div class=\"tv-bracket\" :class=\"{ 'tv-bracket--my-team': card.hasMyTeam }\">\n <div v-if=\"card.hasMyTeam && myTeamHint\" class=\"tv-bracket__hint\">{{ myTeamHint }}</div>\n\n <div\n v-for=\"row in card.rows\"\n :key=\"row.key\"\n class=\"tv-bracket__row\"\n :class=\"{\n 'tv-bracket__row--winner': row.isWinner,\n 'tv-bracket__row--loser': row.isLoser && !row.isEmpty,\n 'tv-bracket__row--my': row.isMyTeam,\n 'tv-bracket__row--empty': row.isEmpty,\n }\"\n :title=\"row.isMyTeam ? 'Моя команда' : undefined\"\n @click=\"onRowClick(row)\"\n @mouseenter=\"hoveredKey = row.key\"\n @mouseleave=\"hoveredKey = null\"\n >\n <template v-if=\"row.team\">\n <span v-if=\"row.isTechDefeat && hoveredKey === row.key\" class=\"tv-bracket__tooltip\">\n {{ techDefeatHint }}\n </span>\n\n <span class=\"tv-bracket__name\">{{ row.team.name }}</span>\n <span class=\"tv-bracket__score\">{{ row.score }}</span>\n </template>\n </div>\n </div>\n</template>\n","import type { App, Plugin } from 'vue'\nimport TreeView from './TreeView.vue'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport BracketCard from './BracketCard.vue'\n// Стили общие для Vue 2 и Vue 3 — лежат в core, чтобы не расходились.\n// Они попадают прямо в бандл и подключаются сами: отдельный импорт в проекте не нужен.\nimport { injectStyles } from '@bigplay/tree-view-core'\nimport styles from '@bigplay/tree-view-core/style.css?inline'\n\ninjectStyles(styles)\n\nexport { TreeView, TreeNodeCard, BracketCard }\nexport { useTreeLayout } from './useTreeLayout'\nexport { usePanZoom } from './usePanZoom'\n\n// Типы из core переэкспортируем, чтобы их не приходилось ставить отдельно.\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n Direction,\n Layout,\n LayoutLink,\n LayoutNode,\n LinkStyle,\n ScaleLimits,\n Transform,\n TreeNode,\n TreeViewOptions,\n} from '@bigplay/tree-view-core'\nexport { DEFAULT_OPTIONS, buildBracketCard, bracketCardHeight } from '@bigplay/tree-view-core'\n\n/** Плагин: `app.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin: Plugin = {\n install(app: App) {\n app.component('TreeView', TreeView)\n app.component('TreeNodeCard', TreeNodeCard)\n app.component('BracketCard', BracketCard)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["emit","__emit","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","__props","_hoisted_3","usePanZoom","params","transform","ref","IDENTITY_TRANSFORM","controller","PanZoomController","next","watch","element","_previous","onCleanup","onBeforeUnmount","content","padding","useTreeLayout","collapsedIds","tree","computed","toTree","visibleTree","applyCollapsed","layout","layoutTree","isCollapsed","id","toggle","toggleCollapsed","expandAll","useVisibleRect","canvas","scale","enabled","rect","estimateVisibleRect","update","measureVisibleRect","onMounted","props","rootElement","canvasElement","panZoom","DEFAULT_SCALE_LIMITS","visible","drawn","visibleLayout","canvasStyle","toCssTransform","rootStyle","nodeStyle","node","nodeClassOf","labelOf","data","toggleNode","onNodeClick","event","fit","nextTick","__expose","_normalizeClass","_unref","_Fragment","_renderList","link","_renderSlot","_ctx","_normalizeStyle","$event","_createVNode","TreeNodeCard","hoveredKey","card","buildBracketCard","onRowClick","row","_hoisted_4","_hoisted_5","injectStyles","styles","TreeViewPlugin","app","TreeView","BracketCard"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Plugin } from 'vue';
|
|
2
2
|
import { default as TreeView } from './TreeView.vue';
|
|
3
3
|
import { default as TreeNodeCard } from './TreeNodeCard.vue';
|
|
4
|
-
|
|
4
|
+
import { default as BracketCard } from './BracketCard.vue';
|
|
5
|
+
export { TreeView, TreeNodeCard, BracketCard };
|
|
5
6
|
export { useTreeLayout } from './useTreeLayout';
|
|
6
7
|
export { usePanZoom } from './usePanZoom';
|
|
7
|
-
export type { Accessors, Direction, Layout, LayoutLink, LayoutNode, LinkStyle, ScaleLimits, Transform, TreeNode, TreeViewOptions, } from '@bigplay/tree-view-core';
|
|
8
|
-
export { DEFAULT_OPTIONS } from '@bigplay/tree-view-core';
|
|
8
|
+
export type { Accessors, BracketCardMatch, BracketCardModel, BracketCardOptions, BracketCardRow, BracketCardTeam, Direction, Layout, LayoutLink, LayoutNode, LinkStyle, ScaleLimits, Transform, TreeNode, TreeViewOptions, } from '@bigplay/tree-view-core';
|
|
9
|
+
export { DEFAULT_OPTIONS, buildBracketCard, bracketCardHeight } from '@bigplay/tree-view-core';
|
|
9
10
|
/** Плагин: `app.use(TreeViewPlugin)` регистрирует компоненты глобально. */
|
|
10
11
|
export declare const TreeViewPlugin: Plugin;
|
|
11
12
|
export default TreeViewPlugin;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { IDENTITY_TRANSFORM as
|
|
3
|
-
import { DEFAULT_OPTIONS as
|
|
4
|
-
const
|
|
1
|
+
import { defineComponent as L, openBlock as c, createElementBlock as d, createElementVNode as w, toDisplayString as m, withModifiers as Z, createCommentVNode as _, ref as g, watch as $, onBeforeUnmount as D, computed as p, onMounted as V, nextTick as M, normalizeStyle as z, normalizeClass as y, unref as N, Fragment as x, renderList as E, renderSlot as O, createVNode as K } from "vue";
|
|
2
|
+
import { IDENTITY_TRANSFORM as W, PanZoomController as Y, toTree as q, applyCollapsed as G, layoutTree as J, toggleCollapsed as Q, estimateVisibleRect as X, measureVisibleRect as ee, DEFAULT_OVERSCAN as te, visibleLayout as oe, toCssTransform as ae, DEFAULT_SCALE_LIMITS as ne, buildBracketCard as re, injectStyles as ie } from "@bigplay/tree-view-core";
|
|
3
|
+
import { DEFAULT_OPTIONS as Le, bracketCardHeight as Ie, buildBracketCard as Be } from "@bigplay/tree-view-core";
|
|
4
|
+
const le = { class: "tree-view-card" }, se = { class: "tree-view-card__label" }, ce = ["aria-label"], F = /* @__PURE__ */ L({
|
|
5
5
|
__name: "TreeNodeCard",
|
|
6
6
|
props: {
|
|
7
7
|
label: {},
|
|
@@ -9,59 +9,70 @@ const X = { class: "tree-view-card" }, ee = { class: "tree-view-card__label" },
|
|
|
9
9
|
collapsed: { type: Boolean }
|
|
10
10
|
},
|
|
11
11
|
emits: ["toggle"],
|
|
12
|
-
setup(
|
|
13
|
-
const
|
|
14
|
-
return (
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
setup(t, { emit: s }) {
|
|
13
|
+
const n = s;
|
|
14
|
+
return (e, a) => (c(), d("div", le, [
|
|
15
|
+
w("span", se, m(t.label), 1),
|
|
16
|
+
t.hasChildren ? (c(), d("button", {
|
|
17
17
|
key: 0,
|
|
18
18
|
type: "button",
|
|
19
19
|
class: "tree-view-card__toggle",
|
|
20
20
|
"data-tv-no-pan": "",
|
|
21
|
-
"aria-label":
|
|
22
|
-
onClick:
|
|
23
|
-
},
|
|
21
|
+
"aria-label": t.collapsed ? "Развернуть" : "Свернуть",
|
|
22
|
+
onClick: a[0] || (a[0] = Z((v) => n("toggle"), ["stop"]))
|
|
23
|
+
}, m(t.collapsed ? "+" : "−"), 9, ce)) : _("", !0)
|
|
24
24
|
]));
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
pannable:
|
|
30
|
-
zoomable:
|
|
31
|
-
limits:
|
|
32
|
-
onChange: (
|
|
33
|
-
|
|
27
|
+
function de(t) {
|
|
28
|
+
const s = g({ ...W }), n = new Y({
|
|
29
|
+
pannable: t.pannable,
|
|
30
|
+
zoomable: t.zoomable,
|
|
31
|
+
limits: t.limits,
|
|
32
|
+
onChange: (e) => {
|
|
33
|
+
s.value = e;
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
(
|
|
39
|
-
|
|
36
|
+
return $(
|
|
37
|
+
t.element,
|
|
38
|
+
(e, a, v) => {
|
|
39
|
+
e && (n.attach(e), v(() => n.detach()));
|
|
40
40
|
},
|
|
41
41
|
{ immediate: !0 }
|
|
42
|
-
), D(() =>
|
|
43
|
-
transform:
|
|
44
|
-
fit: (
|
|
45
|
-
zoomIn: () =>
|
|
46
|
-
zoomOut: () =>
|
|
47
|
-
reset: () =>
|
|
48
|
-
setTransform: (
|
|
42
|
+
), D(() => n.detach()), {
|
|
43
|
+
transform: s,
|
|
44
|
+
fit: (e, a) => n.fit(e, a),
|
|
45
|
+
zoomIn: () => n.zoomIn(),
|
|
46
|
+
zoomOut: () => n.zoomOut(),
|
|
47
|
+
reset: () => n.reset(),
|
|
48
|
+
setTransform: (e) => n.set(e)
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
function
|
|
54
|
-
return
|
|
51
|
+
function ve(t) {
|
|
52
|
+
const s = g(/* @__PURE__ */ new Set()), n = p(() => q(t.data(), t.accessors())), e = p(() => G(n.value, s.value)), a = p(() => J(e.value, t.options()));
|
|
53
|
+
function v(f) {
|
|
54
|
+
return s.value.has(f);
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
56
|
+
function b(f) {
|
|
57
|
+
s.value = Q(s.value, f);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
60
|
-
|
|
59
|
+
function r() {
|
|
60
|
+
s.value = /* @__PURE__ */ new Set();
|
|
61
61
|
}
|
|
62
|
-
return { layout:
|
|
62
|
+
return { layout: a, isCollapsed: v, toggle: b, expandAll: r };
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
function ue(t, s, n) {
|
|
65
|
+
const e = g(n() ? X() : null);
|
|
66
|
+
function a() {
|
|
67
|
+
e.value = n() ? ee(t.value, s()) : null;
|
|
68
|
+
}
|
|
69
|
+
return V(() => {
|
|
70
|
+
a(), window.addEventListener("scroll", a, { passive: !0, capture: !0 }), window.addEventListener("resize", a, { passive: !0 });
|
|
71
|
+
}), D(() => {
|
|
72
|
+
window.removeEventListener("scroll", a, { capture: !0 }), window.removeEventListener("resize", a);
|
|
73
|
+
}), { rect: e, update: a };
|
|
74
|
+
}
|
|
75
|
+
const fe = ["width", "height"], pe = ["d"], be = ["onClick"], he = /* @__PURE__ */ L({
|
|
65
76
|
__name: "TreeView",
|
|
66
77
|
props: {
|
|
67
78
|
data: {},
|
|
@@ -75,136 +86,201 @@ const le = ["width", "height"], ie = ["d"], ae = ["onClick"], re = /* @__PURE__
|
|
|
75
86
|
fitOnMount: { type: Boolean, default: !0 },
|
|
76
87
|
size: { default: "fill" },
|
|
77
88
|
scaleLimits: { default: void 0 },
|
|
78
|
-
nodeClass: { type: [String, Function], default: void 0 }
|
|
89
|
+
nodeClass: { type: [String, Function], default: void 0 },
|
|
90
|
+
virtualize: { type: Boolean, default: !0 },
|
|
91
|
+
overscan: { default: te }
|
|
79
92
|
},
|
|
80
93
|
emits: ["node-click", "toggle", "transform"],
|
|
81
|
-
setup(
|
|
82
|
-
const t =
|
|
83
|
-
data: () =>
|
|
84
|
-
options: () =>
|
|
94
|
+
setup(t, { expose: s, emit: n }) {
|
|
95
|
+
const e = t, a = n, v = g(null), b = g(null), { layout: r, isCollapsed: f, toggle: l, expandAll: k } = ve({
|
|
96
|
+
data: () => e.data,
|
|
97
|
+
options: () => e.options,
|
|
85
98
|
accessors: () => ({
|
|
86
|
-
getId:
|
|
87
|
-
getChildren:
|
|
88
|
-
getParentId:
|
|
99
|
+
getId: e.getId,
|
|
100
|
+
getChildren: e.getChildren,
|
|
101
|
+
getParentId: e.getParentId
|
|
89
102
|
})
|
|
90
|
-
}),
|
|
91
|
-
element:
|
|
92
|
-
pannable: () =>
|
|
93
|
-
zoomable: () =>
|
|
94
|
-
limits: () =>
|
|
95
|
-
}),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
}), u = de({
|
|
104
|
+
element: v,
|
|
105
|
+
pannable: () => e.pannable,
|
|
106
|
+
zoomable: () => e.zoomable,
|
|
107
|
+
limits: () => e.scaleLimits ?? ne
|
|
108
|
+
}), C = ue(
|
|
109
|
+
b,
|
|
110
|
+
() => u.transform.value.scale,
|
|
111
|
+
() => e.virtualize
|
|
112
|
+
), I = p(
|
|
113
|
+
() => e.virtualize ? oe(r.value, C.rect.value, e.overscan) : r.value
|
|
114
|
+
), A = p(() => ({
|
|
115
|
+
width: `${r.value.width}px`,
|
|
116
|
+
height: `${r.value.height}px`,
|
|
117
|
+
transform: ae(u.transform.value)
|
|
118
|
+
})), P = p(() => {
|
|
119
|
+
if (e.size !== "content") return;
|
|
120
|
+
const { scale: o } = u.transform.value;
|
|
102
121
|
return {
|
|
103
|
-
width: `${
|
|
104
|
-
height: `${
|
|
122
|
+
width: `${r.value.width * o}px`,
|
|
123
|
+
height: `${r.value.height * o}px`,
|
|
105
124
|
overflow: "visible"
|
|
106
125
|
};
|
|
107
126
|
});
|
|
108
|
-
function
|
|
127
|
+
function R(o) {
|
|
109
128
|
return {
|
|
110
|
-
left: `${
|
|
111
|
-
top: `${
|
|
112
|
-
width: `${
|
|
113
|
-
height: `${
|
|
129
|
+
left: `${o.x}px`,
|
|
130
|
+
top: `${o.y}px`,
|
|
131
|
+
width: `${o.width}px`,
|
|
132
|
+
height: `${o.height}px`
|
|
114
133
|
};
|
|
115
134
|
}
|
|
116
|
-
function
|
|
117
|
-
return typeof
|
|
135
|
+
function H(o) {
|
|
136
|
+
return typeof e.nodeClass == "function" ? e.nodeClass(o) : e.nodeClass;
|
|
118
137
|
}
|
|
119
|
-
function
|
|
120
|
-
const h =
|
|
121
|
-
return h?.name ?? h?.title ?? h?.label ??
|
|
138
|
+
function j(o) {
|
|
139
|
+
const h = o.data;
|
|
140
|
+
return h?.name ?? h?.title ?? h?.label ?? o.id;
|
|
122
141
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
142
|
+
function T(o) {
|
|
143
|
+
l(o.id), a("toggle", o, f(o.id));
|
|
125
144
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
145
|
+
function U(o, h) {
|
|
146
|
+
a("node-click", o, h), e.collapsible && o.hasChildren && T(o);
|
|
128
147
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
148
|
+
function B(o) {
|
|
149
|
+
u.fit({ width: r.value.width, height: r.value.height }, o);
|
|
131
150
|
}
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
}),
|
|
135
|
-
() =>
|
|
136
|
-
(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
return V(() => {
|
|
152
|
+
e.fitOnMount && M(() => B());
|
|
153
|
+
}), $(
|
|
154
|
+
() => u.transform.value,
|
|
155
|
+
(o) => {
|
|
156
|
+
C.update(), a("transform", o);
|
|
157
|
+
}
|
|
158
|
+
), $(r, () => {
|
|
159
|
+
M(() => C.update());
|
|
160
|
+
}), s({
|
|
161
|
+
fit: B,
|
|
162
|
+
zoomIn: u.zoomIn,
|
|
163
|
+
zoomOut: u.zoomOut,
|
|
164
|
+
resetTransform: u.reset,
|
|
165
|
+
setTransform: u.setTransform,
|
|
166
|
+
expandAll: k,
|
|
167
|
+
toggle: l,
|
|
168
|
+
layout: r,
|
|
169
|
+
transform: u.transform
|
|
170
|
+
}), (o, h) => (c(), d("div", {
|
|
148
171
|
ref_key: "rootElement",
|
|
149
|
-
ref:
|
|
150
|
-
class:
|
|
151
|
-
style:
|
|
172
|
+
ref: v,
|
|
173
|
+
class: y(["tree-view", { "tree-view--pannable": t.pannable, "tree-view--content": t.size === "content" }]),
|
|
174
|
+
style: z(P.value)
|
|
152
175
|
}, [
|
|
153
|
-
|
|
176
|
+
w("div", {
|
|
177
|
+
ref_key: "canvasElement",
|
|
178
|
+
ref: b,
|
|
154
179
|
class: "tree-view__canvas",
|
|
155
|
-
style:
|
|
180
|
+
style: z(A.value)
|
|
156
181
|
}, [
|
|
157
|
-
(c(),
|
|
182
|
+
(c(), d("svg", {
|
|
158
183
|
class: "tree-view__links",
|
|
159
|
-
width:
|
|
160
|
-
height:
|
|
184
|
+
width: N(r).width,
|
|
185
|
+
height: N(r).height
|
|
161
186
|
}, [
|
|
162
|
-
(c(!0),
|
|
163
|
-
|
|
187
|
+
(c(!0), d(x, null, E(I.value.links, (i) => O(o.$slots, "link", { link: i }, () => [
|
|
188
|
+
w("path", {
|
|
164
189
|
class: "tree-view__link",
|
|
165
|
-
d:
|
|
166
|
-
}, null, 8,
|
|
167
|
-
], void 0,
|
|
168
|
-
], 8,
|
|
169
|
-
(c(!0),
|
|
170
|
-
key:
|
|
171
|
-
class:
|
|
172
|
-
style:
|
|
173
|
-
onClick: (
|
|
190
|
+
d: i.path
|
|
191
|
+
}, null, 8, pe)
|
|
192
|
+
], void 0, i.id)), 128))
|
|
193
|
+
], 8, fe)),
|
|
194
|
+
(c(!0), d(x, null, E(I.value.nodes, (i) => (c(), d("div", {
|
|
195
|
+
key: i.id,
|
|
196
|
+
class: y(["tree-view__node", H(i)]),
|
|
197
|
+
style: z(R(i)),
|
|
198
|
+
onClick: (S) => U(i, S)
|
|
174
199
|
}, [
|
|
175
|
-
|
|
176
|
-
node:
|
|
177
|
-
data:
|
|
178
|
-
depth:
|
|
179
|
-
collapsed:
|
|
180
|
-
hasChildren:
|
|
181
|
-
toggle: () =>
|
|
200
|
+
O(o.$slots, "node", {
|
|
201
|
+
node: i,
|
|
202
|
+
data: i.data,
|
|
203
|
+
depth: i.depth,
|
|
204
|
+
collapsed: i.collapsed,
|
|
205
|
+
hasChildren: i.hasChildren,
|
|
206
|
+
toggle: () => T(i)
|
|
182
207
|
}, () => [
|
|
183
|
-
|
|
184
|
-
label:
|
|
185
|
-
"has-children":
|
|
186
|
-
collapsed:
|
|
187
|
-
onToggle: (
|
|
208
|
+
K(F, {
|
|
209
|
+
label: j(i),
|
|
210
|
+
"has-children": i.hasChildren,
|
|
211
|
+
collapsed: i.collapsed,
|
|
212
|
+
onToggle: (S) => T(i)
|
|
188
213
|
}, null, 8, ["label", "has-children", "collapsed", "onToggle"])
|
|
189
214
|
])
|
|
190
|
-
], 14,
|
|
215
|
+
], 14, be))), 128))
|
|
191
216
|
], 4)
|
|
192
217
|
], 6));
|
|
193
218
|
}
|
|
194
|
-
}),
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
}), me = {
|
|
220
|
+
key: 0,
|
|
221
|
+
class: "tv-bracket__hint"
|
|
222
|
+
}, ge = ["title", "onClick", "onMouseenter"], we = {
|
|
223
|
+
key: 0,
|
|
224
|
+
class: "tv-bracket__tooltip"
|
|
225
|
+
}, ke = { class: "tv-bracket__name" }, _e = { class: "tv-bracket__score" }, ye = /* @__PURE__ */ L({
|
|
226
|
+
__name: "BracketCard",
|
|
227
|
+
props: {
|
|
228
|
+
match: {},
|
|
229
|
+
rows: { default: void 0 },
|
|
230
|
+
myTeamHint: { default: "Нажми для перехода в матч 👇" },
|
|
231
|
+
techDefeatLabel: { default: "ТП" },
|
|
232
|
+
techDefeatHint: { default: "Техническое поражение" }
|
|
233
|
+
},
|
|
234
|
+
emits: ["select"],
|
|
235
|
+
setup(t, { emit: s }) {
|
|
236
|
+
const n = t, e = s, a = g(null), v = p(
|
|
237
|
+
() => re(n.match, { rows: n.rows, techDefeatLabel: n.techDefeatLabel })
|
|
238
|
+
);
|
|
239
|
+
function b(r) {
|
|
240
|
+
r.team && e("select", r.team, r);
|
|
241
|
+
}
|
|
242
|
+
return (r, f) => (c(), d("div", {
|
|
243
|
+
class: y(["tv-bracket", { "tv-bracket--my-team": v.value.hasMyTeam }])
|
|
244
|
+
}, [
|
|
245
|
+
v.value.hasMyTeam && t.myTeamHint ? (c(), d("div", me, m(t.myTeamHint), 1)) : _("", !0),
|
|
246
|
+
(c(!0), d(x, null, E(v.value.rows, (l) => (c(), d("div", {
|
|
247
|
+
key: l.key,
|
|
248
|
+
class: y(["tv-bracket__row", {
|
|
249
|
+
"tv-bracket__row--winner": l.isWinner,
|
|
250
|
+
"tv-bracket__row--loser": l.isLoser && !l.isEmpty,
|
|
251
|
+
"tv-bracket__row--my": l.isMyTeam,
|
|
252
|
+
"tv-bracket__row--empty": l.isEmpty
|
|
253
|
+
}]),
|
|
254
|
+
title: l.isMyTeam ? "Моя команда" : void 0,
|
|
255
|
+
onClick: (k) => b(l),
|
|
256
|
+
onMouseenter: (k) => a.value = l.key,
|
|
257
|
+
onMouseleave: f[0] || (f[0] = (k) => a.value = null)
|
|
258
|
+
}, [
|
|
259
|
+
l.team ? (c(), d(x, { key: 0 }, [
|
|
260
|
+
l.isTechDefeat && a.value === l.key ? (c(), d("span", we, m(t.techDefeatHint), 1)) : _("", !0),
|
|
261
|
+
w("span", ke, m(l.team.name), 1),
|
|
262
|
+
w("span", _e, m(l.score), 1)
|
|
263
|
+
], 64)) : _("", !0)
|
|
264
|
+
], 42, ge))), 128))
|
|
265
|
+
], 2));
|
|
266
|
+
}
|
|
267
|
+
}), xe = ".tree-view{--tv-link-color: #cbd5e1;--tv-link-width: 2px;--tv-node-bg: #ffffff;--tv-node-color: #0f172a;--tv-node-border: 1px solid #e2e8f0;--tv-node-radius: 10px;--tv-node-shadow: 0 1px 2px rgb(15 23 42 / 8%);--tv-node-padding: 8px 12px;--tv-accent: #2563eb;position:relative;width:100%;height:100%;overflow:hidden;touch-action:none}.tree-view--pannable{cursor:grab}.tree-view--content{width:auto;height:auto;overflow:visible}.tree-view__canvas{position:absolute;top:0;left:0;transform-origin:0 0}.tree-view__links{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.tree-view__link{fill:none;stroke:var(--tv-link-color);stroke-width:var(--tv-link-width)}.tree-view__node{position:absolute;box-sizing:border-box;display:flex;align-items:stretch;justify-content:stretch}.tree-view-card{box-sizing:border-box;display:flex;gap:8px;align-items:center;justify-content:space-between;width:100%;height:100%;padding:var(--tv-node-padding);overflow:hidden;color:var(--tv-node-color);background:var(--tv-node-bg);border:var(--tv-node-border);border-radius:var(--tv-node-radius);box-shadow:var(--tv-node-shadow)}.tree-view-card__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-view-card__toggle{flex:none;width:20px;height:20px;padding:0;font:inherit;line-height:1;color:var(--tv-accent);cursor:pointer;background:none;border:none}.tv-bracket{--tv-bracket-winner-bg: #0427c4;--tv-bracket-winner-color: #ffffff;--tv-bracket-loser-bg: #e5e5e5;--tv-bracket-loser-color: #7d7d8d;--tv-bracket-my-bg: #00146c;--tv-bracket-my-color: #ffffff;--tv-bracket-radius: 6px;position:relative;box-sizing:border-box;display:flex;flex-direction:column;width:100%;height:100%;font-size:12px}.tv-bracket--my-team{border-radius:var(--tv-bracket-radius);outline:2px solid var(--tv-bracket-my-bg)}.tv-bracket__hint{position:absolute;right:0;bottom:100%;left:0;padding:3px 8px;overflow:hidden;font-size:11px;font-weight:600;line-height:1.2;color:var(--tv-bracket-my-color);text-align:center;text-overflow:ellipsis;white-space:nowrap;background:var(--tv-bracket-my-bg);border-radius:var(--tv-bracket-radius) var(--tv-bracket-radius) 0 0}.tv-bracket__row{position:relative;box-sizing:border-box;display:flex;flex:1 1 0;align-items:center;justify-content:space-between;min-height:0;padding:4px 8px;cursor:pointer;transition:all 75ms}.tv-bracket__row--winner{font-weight:700;color:var(--tv-bracket-winner-color);background:var(--tv-bracket-winner-bg)}.tv-bracket__row--loser{color:var(--tv-bracket-loser-color);background:var(--tv-bracket-loser-bg)}.tv-bracket__row--my{color:var(--tv-bracket-my-color);background:var(--tv-bracket-my-bg)}.tv-bracket__row--empty{cursor:default;background:var(--tv-bracket-loser-bg)}.tv-bracket__name{max-width:120px;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.tv-bracket__score{display:flex;flex:none;align-items:center;justify-content:center;width:20px;height:20px;font-weight:700;font-variant-numeric:tabular-nums}.tv-bracket__tooltip{position:absolute;top:-26px;left:6px;z-index:5000;padding:4px 8px;font-weight:400;color:var(--tv-bracket-loser-color);white-space:nowrap;background:#fff;border:1px solid #d1d5db;border-radius:4px;box-shadow:0 4px 10px #0f172a26}";
|
|
268
|
+
ie(xe);
|
|
269
|
+
const ze = {
|
|
270
|
+
install(t) {
|
|
271
|
+
t.component("TreeView", he), t.component("TreeNodeCard", F), t.component("BracketCard", ye);
|
|
199
272
|
}
|
|
200
273
|
};
|
|
201
274
|
export {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
275
|
+
ye as BracketCard,
|
|
276
|
+
Le as DEFAULT_OPTIONS,
|
|
277
|
+
F as TreeNodeCard,
|
|
278
|
+
he as TreeView,
|
|
279
|
+
ze as TreeViewPlugin,
|
|
280
|
+
Ie as bracketCardHeight,
|
|
281
|
+
Be as buildBracketCard,
|
|
282
|
+
ze as default,
|
|
283
|
+
de as usePanZoom,
|
|
284
|
+
ve as useTreeLayout
|
|
209
285
|
};
|
|
210
286
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/TreeNodeCard.vue","../src/usePanZoom.ts","../src/useTreeLayout.ts","../src/TreeView.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Карточка узла по умолчанию.\n * Нужна только чтобы «из коробки» что-то отображалось —\n * в реальном проекте обычно используют слот #node и рисуют свой блок.\n */\ndefineProps<{\n label: string\n hasChildren?: boolean\n collapsed?: boolean\n}>()\n\nconst emit = defineEmits<{ (e: 'toggle'): void }>()\n</script>\n\n<template>\n <div class=\"tree-view-card\">\n <span class=\"tree-view-card__label\">{{ label }}</span>\n\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"tree-view-card__toggle\"\n data-tv-no-pan\n :aria-label=\"collapsed ? 'Развернуть' : 'Свернуть'\"\n @click.stop=\"emit('toggle')\"\n >\n {{ collapsed ? '+' : '−' }}\n </button>\n </div>\n</template>\n","import { onBeforeUnmount, ref, watch, type Ref } from 'vue'\nimport {\n IDENTITY_TRANSFORM,\n PanZoomController,\n type ScaleLimits,\n type Size,\n type Transform,\n} from '@bigplay/tree-view-core'\n\nexport interface UsePanZoomParams {\n /** Элемент, который слушаем: по нему тянут и крутят колесо. */\n element: Ref<HTMLElement | null>\n pannable: () => boolean\n zoomable: () => boolean\n limits: () => ScaleLimits\n}\n\n/**\n * Тонкая обёртка над `PanZoomController` из core:\n * переносит его состояние в реактивный `ref` и следит за жизненным циклом.\n */\nexport function usePanZoom(params: UsePanZoomParams) {\n const transform = ref<Transform>({ ...IDENTITY_TRANSFORM })\n\n const controller = new PanZoomController({\n pannable: params.pannable,\n zoomable: params.zoomable,\n limits: params.limits,\n onChange: (next) => {\n transform.value = next\n },\n })\n\n watch(\n params.element,\n (element, _previous, onCleanup) => {\n if (!element) return\n\n controller.attach(element)\n onCleanup(() => controller.detach())\n },\n { immediate: true },\n )\n\n onBeforeUnmount(() => controller.detach())\n\n return {\n transform,\n fit: (content: Size, padding?: number) => controller.fit(content, padding),\n zoomIn: () => controller.zoomIn(),\n zoomOut: () => controller.zoomOut(),\n reset: () => controller.reset(),\n setTransform: (next: Transform) => controller.set(next),\n }\n}\n","import { computed, ref, type ComputedRef } from 'vue'\nimport {\n applyCollapsed,\n layoutTree,\n toggleCollapsed,\n toTree,\n type Accessors,\n type Layout,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/** Функции-геттеры, чтобы композабл видел свежие значения пропов. */\nexport interface UseTreeLayoutParams<T> {\n data: () => T | T[] | null | undefined\n options: () => Partial<TreeViewOptions> | undefined\n accessors: () => Accessors<T>\n}\n\nexport interface UseTreeLayoutResult<T> {\n /** Готовая раскладка: узлы с координатами и линии. */\n layout: ComputedRef<Layout<T>>\n /** Свёрнут ли узел с таким id. */\n isCollapsed: (id: string) => boolean\n /** Свернуть/развернуть узел. */\n toggle: (id: string) => void\n /** Развернуть всё дерево. */\n expandAll: () => void\n}\n\n/**\n * Вся «логика» компонента: данные → дерево → раскладка.\n * Каждый шаг — отдельная чистая функция из core, здесь только реактивность.\n */\nexport function useTreeLayout<T>(params: UseTreeLayoutParams<T>): UseTreeLayoutResult<T> {\n const collapsedIds = ref(new Set<string>())\n\n const tree = computed(() => toTree(params.data(), params.accessors()))\n const visibleTree = computed(() => applyCollapsed(tree.value, collapsedIds.value))\n const layout = computed(() => layoutTree(visibleTree.value, params.options()))\n\n function isCollapsed(id: string): boolean {\n return collapsedIds.value.has(id)\n }\n\n function toggle(id: string): void {\n // Новое множество вместо мутации — иначе Vue не заметит изменения.\n collapsedIds.value = toggleCollapsed(collapsedIds.value, id)\n }\n\n function expandAll(): void {\n collapsedIds.value = new Set()\n }\n\n return { layout, isCollapsed, toggle, expandAll }\n}\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Компонент-обёртка. Вся математика — в пакете core, здесь только\n * реактивность, разметка и события.\n */\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\nimport {\n DEFAULT_SCALE_LIMITS,\n toCssTransform,\n type Accessors,\n type LayoutNode,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport { usePanZoom } from './usePanZoom'\nimport { useTreeLayout } from './useTreeLayout'\n\nconst props = withDefaults(\n defineProps<{\n /** Данные: объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: T | T[] | null | undefined\n /** Размеры и направление. Можно передать только часть полей. */\n options?: Partial<TreeViewOptions>\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId?: Accessors<T>['getId']\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren?: Accessors<T>['getChildren']\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId?: Accessors<T>['getParentId']\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible?: boolean\n /** Разрешить перетаскивание холста. */\n pannable?: boolean\n /** Разрешить зум колесом и «щипком». */\n zoomable?: boolean\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount?: boolean\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается,\n * дерево двигают мышью;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n * Нужен, когда сетка стоит внутри страницы со своим скроллом.\n */\n size?: 'fill' | 'content'\n /** Пределы масштаба. */\n scaleLimits?: ScaleLimits\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass?: string | ((node: LayoutNode<T>) => string)\n }>(),\n {\n options: undefined,\n getId: undefined,\n getChildren: undefined,\n getParentId: undefined,\n collapsible: false,\n pannable: true,\n zoomable: true,\n fitOnMount: true,\n size: 'fill',\n scaleLimits: undefined,\n nodeClass: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'node-click', node: LayoutNode<T>, event: MouseEvent): void\n (e: 'toggle', node: LayoutNode<T>, collapsed: boolean): void\n (e: 'transform', transform: Transform): void\n}>()\n\nconst rootElement = ref<HTMLElement | null>(null)\n\nconst { layout, isCollapsed, toggle, expandAll } = useTreeLayout<T>({\n data: () => props.data,\n options: () => props.options,\n accessors: () => ({\n getId: props.getId,\n getChildren: props.getChildren,\n getParentId: props.getParentId,\n }),\n})\n\nconst panZoom = usePanZoom({\n element: rootElement,\n pannable: () => props.pannable,\n zoomable: () => props.zoomable,\n limits: () => props.scaleLimits ?? DEFAULT_SCALE_LIMITS,\n})\n\nconst canvasStyle = computed(() => ({\n width: `${layout.value.width}px`,\n height: `${layout.value.height}px`,\n transform: toCssTransform(panZoom.transform.value),\n}))\n\nconst rootStyle = computed(() => {\n if (props.size !== 'content') return undefined\n\n const { scale } = panZoom.transform.value\n return {\n width: `${layout.value.width * scale}px`,\n height: `${layout.value.height * scale}px`,\n overflow: 'visible',\n }\n})\n\nfunction nodeStyle(node: LayoutNode<T>) {\n return {\n left: `${node.x}px`,\n top: `${node.y}px`,\n width: `${node.width}px`,\n height: `${node.height}px`,\n }\n}\n\nfunction nodeClassOf(node: LayoutNode<T>): string | undefined {\n return typeof props.nodeClass === 'function' ? props.nodeClass(node) : props.nodeClass\n}\n\n/** Подпись карточки по умолчанию: name → title → label → id. */\nfunction labelOf(node: LayoutNode<T>): string {\n const data = node.data as { name?: string; title?: string; label?: string } | null\n return data?.name ?? data?.title ?? data?.label ?? node.id\n}\n\nfunction toggleNode(node: LayoutNode<T>): void {\n toggle(node.id)\n emit('toggle', node, isCollapsed(node.id))\n}\n\nfunction onNodeClick(node: LayoutNode<T>, event: MouseEvent): void {\n emit('node-click', node, event)\n if (props.collapsible && node.hasChildren) toggleNode(node)\n}\n\n/** Вписывает дерево в видимую область. */\nfunction fit(padding?: number): void {\n panZoom.fit({ width: layout.value.width, height: layout.value.height }, padding)\n}\n\nonMounted(() => {\n if (props.fitOnMount) void nextTick(() => fit())\n})\n\nwatch(\n () => panZoom.transform.value,\n (transform) => emit('transform', transform),\n)\n\ndefineExpose({\n fit,\n zoomIn: panZoom.zoomIn,\n zoomOut: panZoom.zoomOut,\n resetTransform: panZoom.reset,\n setTransform: panZoom.setTransform,\n expandAll,\n toggle,\n layout,\n transform: panZoom.transform,\n})\n</script>\n\n<template>\n <div\n ref=\"rootElement\"\n class=\"tree-view\"\n :class=\"{ 'tree-view--pannable': pannable, 'tree-view--content': size === 'content' }\"\n :style=\"rootStyle\"\n >\n <div class=\"tree-view__canvas\" :style=\"canvasStyle\">\n <svg class=\"tree-view__links\" :width=\"layout.width\" :height=\"layout.height\">\n <template v-for=\"link in layout.links\" :key=\"link.id\">\n <slot name=\"link\" :link=\"link\">\n <path class=\"tree-view__link\" :d=\"link.path\" />\n </slot>\n </template>\n </svg>\n\n <div\n v-for=\"node in layout.nodes\"\n :key=\"node.id\"\n class=\"tree-view__node\"\n :class=\"nodeClassOf(node)\"\n :style=\"nodeStyle(node)\"\n @click=\"onNodeClick(node, $event)\"\n >\n <!--\n Главная точка кастомизации: сюда подставляют свою карточку.\n Доступно всё, что может понадобиться для отрисовки и управления.\n -->\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"node.data\"\n :depth=\"node.depth\"\n :collapsed=\"node.collapsed\"\n :has-children=\"node.hasChildren\"\n :toggle=\"() => toggleNode(node)\"\n >\n <TreeNodeCard\n :label=\"labelOf(node)\"\n :has-children=\"node.hasChildren\"\n :collapsed=\"node.collapsed\"\n @toggle=\"toggleNode(node)\"\n />\n </slot>\n </div>\n </div>\n </div>\n</template>\n","import type { App, Plugin } from 'vue'\nimport TreeView from './TreeView.vue'\nimport TreeNodeCard from './TreeNodeCard.vue'\n// Стили общие для Vue 2 и Vue 3 — лежат в core, чтобы не расходились.\n// Они попадают прямо в бандл и подключаются сами: отдельный импорт в проекте не нужен.\nimport { injectStyles } from '@bigplay/tree-view-core'\nimport styles from '@bigplay/tree-view-core/style.css?inline'\n\ninjectStyles(styles)\n\nexport { TreeView, TreeNodeCard }\nexport { useTreeLayout } from './useTreeLayout'\nexport { usePanZoom } from './usePanZoom'\n\n// Типы из core переэкспортируем, чтобы их не приходилось ставить отдельно.\nexport type {\n Accessors,\n Direction,\n Layout,\n LayoutLink,\n LayoutNode,\n LinkStyle,\n ScaleLimits,\n Transform,\n TreeNode,\n TreeViewOptions,\n} from '@bigplay/tree-view-core'\nexport { DEFAULT_OPTIONS } from '@bigplay/tree-view-core'\n\n/** Плагин: `app.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin: Plugin = {\n install(app: App) {\n app.component('TreeView', TreeView)\n app.component('TreeNodeCard', TreeNodeCard)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["emit","__emit","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","__props","_hoisted_3","usePanZoom","params","transform","ref","IDENTITY_TRANSFORM","controller","PanZoomController","next","watch","element","_previous","onCleanup","onBeforeUnmount","content","padding","useTreeLayout","collapsedIds","tree","computed","toTree","visibleTree","applyCollapsed","layout","layoutTree","isCollapsed","id","toggle","toggleCollapsed","expandAll","props","rootElement","panZoom","DEFAULT_SCALE_LIMITS","canvasStyle","toCssTransform","rootStyle","scale","nodeStyle","node","nodeClassOf","labelOf","data","toggleNode","onNodeClick","event","fit","onMounted","nextTick","__expose","_normalizeClass","_unref","_Fragment","_renderList","link","_renderSlot","_ctx","_normalizeStyle","$event","_createVNode","TreeNodeCard","injectStyles","styles","TreeViewPlugin","app","TreeView"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/TreeNodeCard.vue","../src/usePanZoom.ts","../src/useTreeLayout.ts","../src/useVisibleRect.ts","../src/TreeView.vue","../src/BracketCard.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Карточка узла по умолчанию.\n * Нужна только чтобы «из коробки» что-то отображалось —\n * в реальном проекте обычно используют слот #node и рисуют свой блок.\n */\ndefineProps<{\n label: string\n hasChildren?: boolean\n collapsed?: boolean\n}>()\n\nconst emit = defineEmits<{ (e: 'toggle'): void }>()\n</script>\n\n<template>\n <div class=\"tree-view-card\">\n <span class=\"tree-view-card__label\">{{ label }}</span>\n\n <button\n v-if=\"hasChildren\"\n type=\"button\"\n class=\"tree-view-card__toggle\"\n data-tv-no-pan\n :aria-label=\"collapsed ? 'Развернуть' : 'Свернуть'\"\n @click.stop=\"emit('toggle')\"\n >\n {{ collapsed ? '+' : '−' }}\n </button>\n </div>\n</template>\n","import { onBeforeUnmount, ref, watch, type Ref } from 'vue'\nimport {\n IDENTITY_TRANSFORM,\n PanZoomController,\n type ScaleLimits,\n type Size,\n type Transform,\n} from '@bigplay/tree-view-core'\n\nexport interface UsePanZoomParams {\n /** Элемент, который слушаем: по нему тянут и крутят колесо. */\n element: Ref<HTMLElement | null>\n pannable: () => boolean\n zoomable: () => boolean\n limits: () => ScaleLimits\n}\n\n/**\n * Тонкая обёртка над `PanZoomController` из core:\n * переносит его состояние в реактивный `ref` и следит за жизненным циклом.\n */\nexport function usePanZoom(params: UsePanZoomParams) {\n const transform = ref<Transform>({ ...IDENTITY_TRANSFORM })\n\n const controller = new PanZoomController({\n pannable: params.pannable,\n zoomable: params.zoomable,\n limits: params.limits,\n onChange: (next) => {\n transform.value = next\n },\n })\n\n watch(\n params.element,\n (element, _previous, onCleanup) => {\n if (!element) return\n\n controller.attach(element)\n onCleanup(() => controller.detach())\n },\n { immediate: true },\n )\n\n onBeforeUnmount(() => controller.detach())\n\n return {\n transform,\n fit: (content: Size, padding?: number) => controller.fit(content, padding),\n zoomIn: () => controller.zoomIn(),\n zoomOut: () => controller.zoomOut(),\n reset: () => controller.reset(),\n setTransform: (next: Transform) => controller.set(next),\n }\n}\n","import { computed, ref, type ComputedRef } from 'vue'\nimport {\n applyCollapsed,\n layoutTree,\n toggleCollapsed,\n toTree,\n type Accessors,\n type Layout,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/** Функции-геттеры, чтобы композабл видел свежие значения пропов. */\nexport interface UseTreeLayoutParams<T> {\n data: () => T | T[] | null | undefined\n options: () => Partial<TreeViewOptions<T>> | undefined\n accessors: () => Accessors<T>\n}\n\nexport interface UseTreeLayoutResult<T> {\n /** Готовая раскладка: узлы с координатами и линии. */\n layout: ComputedRef<Layout<T>>\n /** Свёрнут ли узел с таким id. */\n isCollapsed: (id: string) => boolean\n /** Свернуть/развернуть узел. */\n toggle: (id: string) => void\n /** Развернуть всё дерево. */\n expandAll: () => void\n}\n\n/**\n * Вся «логика» компонента: данные → дерево → раскладка.\n * Каждый шаг — отдельная чистая функция из core, здесь только реактивность.\n */\nexport function useTreeLayout<T>(params: UseTreeLayoutParams<T>): UseTreeLayoutResult<T> {\n const collapsedIds = ref(new Set<string>())\n\n const tree = computed(() => toTree(params.data(), params.accessors()))\n const visibleTree = computed(() => applyCollapsed(tree.value, collapsedIds.value))\n const layout = computed(() => layoutTree(visibleTree.value, params.options()))\n\n function isCollapsed(id: string): boolean {\n return collapsedIds.value.has(id)\n }\n\n function toggle(id: string): void {\n // Новое множество вместо мутации — иначе Vue не заметит изменения.\n collapsedIds.value = toggleCollapsed(collapsedIds.value, id)\n }\n\n function expandAll(): void {\n collapsedIds.value = new Set()\n }\n\n return { layout, isCollapsed, toggle, expandAll }\n}\n","import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'\nimport { estimateVisibleRect, measureVisibleRect, type Rect } from '@bigplay/tree-view-core'\n\n/**\n * Следит за тем, какая часть холста сейчас видна.\n *\n * Пересчитывается там же, где это меняется: при перетаскивании и зуме,\n * при скролле страницы и при изменении размеров окна. Отдельный троттлинг\n * не нужен — и те, и другие события браузер отдаёт не чаще кадра.\n */\nexport function useVisibleRect(canvas: Ref<HTMLElement | null>, scale: () => number, enabled: () => boolean) {\n // До первого измерения берём размер окна — иначе первый кадр нарисует всё дерево.\n const rect = ref<Rect | null>(enabled() ? estimateVisibleRect() : null)\n\n function update(): void {\n rect.value = enabled() ? measureVisibleRect(canvas.value, scale()) : null\n }\n\n onMounted(() => {\n update()\n window.addEventListener('scroll', update, { passive: true, capture: true })\n window.addEventListener('resize', update, { passive: true })\n })\n\n onBeforeUnmount(() => {\n window.removeEventListener('scroll', update, { capture: true })\n window.removeEventListener('resize', update)\n })\n\n return { rect, update }\n}\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Компонент-обёртка. Вся математика — в пакете core, здесь только\n * реактивность, разметка и события.\n */\nimport { computed, nextTick, onMounted, ref, watch } from 'vue'\nimport {\n DEFAULT_OVERSCAN,\n DEFAULT_SCALE_LIMITS,\n toCssTransform,\n visibleLayout,\n type Accessors,\n type LayoutNode,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport { usePanZoom } from './usePanZoom'\nimport { useTreeLayout } from './useTreeLayout'\nimport { useVisibleRect } from './useVisibleRect'\n\nconst props = withDefaults(\n defineProps<{\n /** Данные: объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: T | T[] | null | undefined\n /** Размеры и направление. Можно передать только часть полей. */\n options?: Partial<TreeViewOptions<T>>\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId?: Accessors<T>['getId']\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren?: Accessors<T>['getChildren']\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId?: Accessors<T>['getParentId']\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible?: boolean\n /** Разрешить перетаскивание холста. */\n pannable?: boolean\n /** Разрешить зум колесом и «щипком». */\n zoomable?: boolean\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount?: boolean\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается,\n * дерево двигают мышью;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n * Нужен, когда сетка стоит внутри страницы со своим скроллом.\n */\n size?: 'fill' | 'content'\n /** Пределы масштаба. */\n scaleLimits?: ScaleLimits\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass?: string | ((node: LayoutNode<T>) => string)\n /**\n * Рисовать только то, что видно на экране. На больших сетках это\n * главная экономия: в DOM попадает несколько десятков карточек\n * вместо тысячи. На маленьких деревьях ничего не меняет.\n */\n virtualize?: boolean\n /** Запас вокруг видимой области в пикселях. */\n overscan?: number\n }>(),\n {\n options: undefined,\n getId: undefined,\n getChildren: undefined,\n getParentId: undefined,\n collapsible: false,\n pannable: true,\n zoomable: true,\n fitOnMount: true,\n size: 'fill',\n scaleLimits: undefined,\n nodeClass: undefined,\n virtualize: true,\n overscan: DEFAULT_OVERSCAN,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'node-click', node: LayoutNode<T>, event: MouseEvent): void\n (e: 'toggle', node: LayoutNode<T>, collapsed: boolean): void\n (e: 'transform', transform: Transform): void\n}>()\n\nconst rootElement = ref<HTMLElement | null>(null)\nconst canvasElement = ref<HTMLElement | null>(null)\n\nconst { layout, isCollapsed, toggle, expandAll } = useTreeLayout<T>({\n data: () => props.data,\n options: () => props.options,\n accessors: () => ({\n getId: props.getId,\n getChildren: props.getChildren,\n getParentId: props.getParentId,\n }),\n})\n\nconst panZoom = usePanZoom({\n element: rootElement,\n pannable: () => props.pannable,\n zoomable: () => props.zoomable,\n limits: () => props.scaleLimits ?? DEFAULT_SCALE_LIMITS,\n})\n\nconst visible = useVisibleRect(\n canvasElement,\n () => panZoom.transform.value.scale,\n () => props.virtualize,\n)\n\n/** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\nconst drawn = computed(() =>\n props.virtualize ? visibleLayout(layout.value, visible.rect.value, props.overscan) : layout.value,\n)\n\nconst canvasStyle = computed(() => ({\n width: `${layout.value.width}px`,\n height: `${layout.value.height}px`,\n transform: toCssTransform(panZoom.transform.value),\n}))\n\nconst rootStyle = computed(() => {\n if (props.size !== 'content') return undefined\n\n const { scale } = panZoom.transform.value\n return {\n width: `${layout.value.width * scale}px`,\n height: `${layout.value.height * scale}px`,\n overflow: 'visible',\n }\n})\n\nfunction nodeStyle(node: LayoutNode<T>) {\n return {\n left: `${node.x}px`,\n top: `${node.y}px`,\n width: `${node.width}px`,\n height: `${node.height}px`,\n }\n}\n\nfunction nodeClassOf(node: LayoutNode<T>): string | undefined {\n return typeof props.nodeClass === 'function' ? props.nodeClass(node) : props.nodeClass\n}\n\n/** Подпись карточки по умолчанию: name → title → label → id. */\nfunction labelOf(node: LayoutNode<T>): string {\n const data = node.data as { name?: string; title?: string; label?: string } | null\n return data?.name ?? data?.title ?? data?.label ?? node.id\n}\n\nfunction toggleNode(node: LayoutNode<T>): void {\n toggle(node.id)\n emit('toggle', node, isCollapsed(node.id))\n}\n\nfunction onNodeClick(node: LayoutNode<T>, event: MouseEvent): void {\n emit('node-click', node, event)\n if (props.collapsible && node.hasChildren) toggleNode(node)\n}\n\n/** Вписывает дерево в видимую область. */\nfunction fit(padding?: number): void {\n panZoom.fit({ width: layout.value.width, height: layout.value.height }, padding)\n}\n\nonMounted(() => {\n if (props.fitOnMount) void nextTick(() => fit())\n})\n\nwatch(\n () => panZoom.transform.value,\n (transform) => {\n // Холст поехал — видимая часть стала другой.\n visible.update()\n emit('transform', transform)\n },\n)\n\n// Новые данные — и пересчитывать видимую часть надо уже по ним.\nwatch(layout, () => void nextTick(() => visible.update()))\n\ndefineExpose({\n fit,\n zoomIn: panZoom.zoomIn,\n zoomOut: panZoom.zoomOut,\n resetTransform: panZoom.reset,\n setTransform: panZoom.setTransform,\n expandAll,\n toggle,\n layout,\n transform: panZoom.transform,\n})\n</script>\n\n<template>\n <div\n ref=\"rootElement\"\n class=\"tree-view\"\n :class=\"{ 'tree-view--pannable': pannable, 'tree-view--content': size === 'content' }\"\n :style=\"rootStyle\"\n >\n <div ref=\"canvasElement\" class=\"tree-view__canvas\" :style=\"canvasStyle\">\n <svg class=\"tree-view__links\" :width=\"layout.width\" :height=\"layout.height\">\n <template v-for=\"link in drawn.links\" :key=\"link.id\">\n <slot name=\"link\" :link=\"link\">\n <path class=\"tree-view__link\" :d=\"link.path\" />\n </slot>\n </template>\n </svg>\n\n <div\n v-for=\"node in drawn.nodes\"\n :key=\"node.id\"\n class=\"tree-view__node\"\n :class=\"nodeClassOf(node)\"\n :style=\"nodeStyle(node)\"\n @click=\"onNodeClick(node, $event)\"\n >\n <!--\n Главная точка кастомизации: сюда подставляют свою карточку.\n Доступно всё, что может понадобиться для отрисовки и управления.\n -->\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"node.data\"\n :depth=\"node.depth\"\n :collapsed=\"node.collapsed\"\n :has-children=\"node.hasChildren\"\n :toggle=\"() => toggleNode(node)\"\n >\n <TreeNodeCard\n :label=\"labelOf(node)\"\n :has-children=\"node.hasChildren\"\n :collapsed=\"node.collapsed\"\n @toggle=\"toggleNode(node)\"\n />\n </slot>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Карточка матча турнирной сетки — та самая, что на сайте:\n * победитель синий, проигравший серый, команда пользователя в рамке\n * с подсказкой, техническое поражение показывает «ТП» и объяснение по наведению.\n *\n * Какие строки рисовать — решает `buildBracketCard` из core, поэтому\n * версия для Vue 2 выглядит точно так же.\n */\nimport { computed, ref } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n type BracketCardTeam,\n} from '@bigplay/tree-view-core'\n\nconst props = withDefaults(\n defineProps<{\n /** Матч: нужны только команды. */\n match: BracketCardMatch | null | undefined\n /** Сколько строк рисовать. У финала — одна. */\n rows?: number\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint?: string\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel?: string\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint?: string\n }>(),\n {\n rows: undefined,\n myTeamHint: 'Нажми для перехода в матч 👇',\n techDefeatLabel: 'ТП',\n techDefeatHint: 'Техническое поражение',\n },\n)\n\nconst emit = defineEmits<{ (e: 'select', team: BracketCardTeam, row: BracketCardRow): void }>()\n\nconst hoveredKey = ref<string | null>(null)\n\nconst card = computed(() =>\n buildBracketCard(props.match, { rows: props.rows, techDefeatLabel: props.techDefeatLabel }),\n)\n\nfunction onRowClick(row: BracketCardRow): void {\n if (row.team) emit('select', row.team, row)\n}\n</script>\n\n<template>\n <div class=\"tv-bracket\" :class=\"{ 'tv-bracket--my-team': card.hasMyTeam }\">\n <div v-if=\"card.hasMyTeam && myTeamHint\" class=\"tv-bracket__hint\">{{ myTeamHint }}</div>\n\n <div\n v-for=\"row in card.rows\"\n :key=\"row.key\"\n class=\"tv-bracket__row\"\n :class=\"{\n 'tv-bracket__row--winner': row.isWinner,\n 'tv-bracket__row--loser': row.isLoser && !row.isEmpty,\n 'tv-bracket__row--my': row.isMyTeam,\n 'tv-bracket__row--empty': row.isEmpty,\n }\"\n :title=\"row.isMyTeam ? 'Моя команда' : undefined\"\n @click=\"onRowClick(row)\"\n @mouseenter=\"hoveredKey = row.key\"\n @mouseleave=\"hoveredKey = null\"\n >\n <template v-if=\"row.team\">\n <span v-if=\"row.isTechDefeat && hoveredKey === row.key\" class=\"tv-bracket__tooltip\">\n {{ techDefeatHint }}\n </span>\n\n <span class=\"tv-bracket__name\">{{ row.team.name }}</span>\n <span class=\"tv-bracket__score\">{{ row.score }}</span>\n </template>\n </div>\n </div>\n</template>\n","import type { App, Plugin } from 'vue'\nimport TreeView from './TreeView.vue'\nimport TreeNodeCard from './TreeNodeCard.vue'\nimport BracketCard from './BracketCard.vue'\n// Стили общие для Vue 2 и Vue 3 — лежат в core, чтобы не расходились.\n// Они попадают прямо в бандл и подключаются сами: отдельный импорт в проекте не нужен.\nimport { injectStyles } from '@bigplay/tree-view-core'\nimport styles from '@bigplay/tree-view-core/style.css?inline'\n\ninjectStyles(styles)\n\nexport { TreeView, TreeNodeCard, BracketCard }\nexport { useTreeLayout } from './useTreeLayout'\nexport { usePanZoom } from './usePanZoom'\n\n// Типы из core переэкспортируем, чтобы их не приходилось ставить отдельно.\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n Direction,\n Layout,\n LayoutLink,\n LayoutNode,\n LinkStyle,\n ScaleLimits,\n Transform,\n TreeNode,\n TreeViewOptions,\n} from '@bigplay/tree-view-core'\nexport { DEFAULT_OPTIONS, buildBracketCard, bracketCardHeight } from '@bigplay/tree-view-core'\n\n/** Плагин: `app.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin: Plugin = {\n install(app: App) {\n app.component('TreeView', TreeView)\n app.component('TreeNodeCard', TreeNodeCard)\n app.component('BracketCard', BracketCard)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["emit","__emit","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","__props","_hoisted_3","usePanZoom","params","transform","ref","IDENTITY_TRANSFORM","controller","PanZoomController","next","watch","element","_previous","onCleanup","onBeforeUnmount","content","padding","useTreeLayout","collapsedIds","tree","computed","toTree","visibleTree","applyCollapsed","layout","layoutTree","isCollapsed","id","toggle","toggleCollapsed","expandAll","useVisibleRect","canvas","scale","enabled","rect","estimateVisibleRect","update","measureVisibleRect","onMounted","props","rootElement","canvasElement","panZoom","DEFAULT_SCALE_LIMITS","visible","drawn","visibleLayout","canvasStyle","toCssTransform","rootStyle","nodeStyle","node","nodeClassOf","labelOf","data","toggleNode","onNodeClick","event","fit","nextTick","__expose","_normalizeClass","_unref","_Fragment","_renderList","link","_renderSlot","_ctx","_normalizeStyle","$event","_createVNode","TreeNodeCard","hoveredKey","card","buildBracketCard","onRowClick","row","_hoisted_4","_hoisted_5","injectStyles","styles","TreeViewPlugin","app","TreeView","BracketCard"],"mappings":"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"}
|
package/dist/useTreeLayout.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Accessors, Layout, TreeViewOptions } from '@bigplay/tree-view-core';
|
|
|
3
3
|
/** Функции-геттеры, чтобы композабл видел свежие значения пропов. */
|
|
4
4
|
export interface UseTreeLayoutParams<T> {
|
|
5
5
|
data: () => T | T[] | null | undefined;
|
|
6
|
-
options: () => Partial<TreeViewOptions
|
|
6
|
+
options: () => Partial<TreeViewOptions<T>> | undefined;
|
|
7
7
|
accessors: () => Accessors<T>;
|
|
8
8
|
}
|
|
9
9
|
export interface UseTreeLayoutResult<T> {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { Rect } from '@bigplay/tree-view-core';
|
|
3
|
+
/**
|
|
4
|
+
* Следит за тем, какая часть холста сейчас видна.
|
|
5
|
+
*
|
|
6
|
+
* Пересчитывается там же, где это меняется: при перетаскивании и зуме,
|
|
7
|
+
* при скролле страницы и при изменении размеров окна. Отдельный троттлинг
|
|
8
|
+
* не нужен — и те, и другие события браузер отдаёт не чаще кадра.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useVisibleRect(canvas: Ref<HTMLElement | null>, scale: () => number, enabled: () => boolean): {
|
|
11
|
+
rect: Ref<{
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
} | null, Rect | {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
} | null>;
|
|
22
|
+
update: () => void;
|
|
23
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigplay/tree-view",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Компонент дерева и турнирных сеток для Vue 3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typecheck": "vue-tsc --noEmit -p tsconfig.json"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@bigplay/tree-view-core": "^0.1
|
|
38
|
+
"@bigplay/tree-view-core": "^0.2.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"vue": "^3.3.0"
|