@bigplay/tree-view-vue2 0.4.0 → 0.6.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.d.ts +9 -0
- package/dist/TreeView.d.ts +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +202 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/BracketCard.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ import { BracketCardMatch, BracketCardRow } from '@bigplay/tree-view-core';
|
|
|
7
7
|
declare const _default: import('vue/types/vue').ExtendedVue<Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue>, {
|
|
8
8
|
hoveredKey: string | null;
|
|
9
9
|
}, {
|
|
10
|
+
/**
|
|
11
|
+
* Кнопка стоит со стороны предыдущих матчей — туда же уходят линии.
|
|
12
|
+
* data-tv-no-pan: нажатие не должно утаскивать холст.
|
|
13
|
+
*/
|
|
14
|
+
renderToggle(h: typeof Vue.prototype.$createElement): VNode;
|
|
10
15
|
renderRow(h: typeof Vue.prototype.$createElement, row: BracketCardRow): VNode;
|
|
11
16
|
}, {
|
|
12
17
|
card: import('@bigplay/tree-view-core').BracketCardModel;
|
|
@@ -16,5 +21,9 @@ declare const _default: import('vue/types/vue').ExtendedVue<Vue<Record<string, a
|
|
|
16
21
|
myTeamHint: string;
|
|
17
22
|
techDefeatLabel: string;
|
|
18
23
|
techDefeatHint: string;
|
|
24
|
+
hasChildren: boolean;
|
|
25
|
+
collapsed: boolean;
|
|
26
|
+
collapseHint: string;
|
|
27
|
+
expandHint: string;
|
|
19
28
|
}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin>;
|
|
20
29
|
export default _default;
|
package/dist/TreeView.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const l=require("vue"),i=require("@bigplay/tree-view-core"),c=l.extend({name:"TreeNodeCard",props:{label:{type:String,required:!0},hasChildren:{type:Boolean,default:!1},collapsed:{type:Boolean,default:!1}},render(e){const t=[e("span",{class:"tree-view-card__label"},this.label)];return this.hasChildren&&t.push(e("button",{class:"tree-view-card__toggle",attrs:{type:"button","data-tv-no-pan":"","aria-label":this.collapsed?"Развернуть":"Свернуть"},on:{click:r=>{r.stopPropagation(),this.$emit("toggle")}}},this.collapsed?"+":"−")),e("div",{class:"tree-view-card"},t)}}),n=new WeakMap,h=l.extend({name:"TreeView",props:{data:{type:[Object,Array],default:null},options:{type:Object,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:{type:String,default:"fill"},scaleLimits:{type:Object,default:void 0},nodeClass:{type:[String,Function],default:void 0},virtualize:{type:Boolean,default:!0},overscan:{type:Number,default:i.DEFAULT_OVERSCAN}},data(){return{collapsedIds:[],transform:{x:0,y:0,scale:1},visibleRect:this.virtualize?i.estimateVisibleRect():null}},computed:{layout(){const e=i.toTree(this.data,{getId:this.getId,getChildren:this.getChildren,getParentId:this.getParentId});return i.layoutTree(i.applyCollapsed(e,new Set(this.collapsedIds)),this.options)},drawn(){return this.virtualize?i.visibleLayout(this.layout,this.visibleRect,this.overscan):this.layout},canvasStyle(){return{width:`${this.layout.width}px`,height:`${this.layout.height}px`,transform:i.toCssTransform(this.transform)}},rootStyle(){if(this.size==="content")return{width:`${this.layout.width*this.transform.scale}px`,height:`${this.layout.height*this.transform.scale}px`,overflow:"visible"}}},watch:{layout(){this.$nextTick(()=>this.updateVisibleRect())}},mounted(){const e=new i.PanZoomController({pannable:()=>this.pannable,zoomable:()=>this.zoomable,limits:this.scaleLimits?()=>this.scaleLimits:void 0,onChange:t=>{this.transform=t,this.updateVisibleRect(),this.$emit("transform",t)}});e.attach(this.$el),n.set(this,e),this.updateVisibleRect(),window.addEventListener("scroll",this.updateVisibleRect,{passive:!0,capture:!0}),window.addEventListener("resize",this.updateVisibleRect,{passive:!0}),this.fitOnMount&&this.$nextTick(()=>this.fit())},beforeDestroy(){n.get(this)?.detach(),n.delete(this),window.removeEventListener("scroll",this.updateVisibleRect,{capture:!0}),window.removeEventListener("resize",this.updateVisibleRect)},methods:{updateVisibleRect(){this.visibleRect=this.virtualize?i.measureVisibleRect(this.$refs.canvas,this.transform.scale):null},fit(e){n.get(this)?.fit({width:this.layout.width,height:this.layout.height},e)},zoomIn(){n.get(this)?.zoomIn()},zoomOut(){n.get(this)?.zoomOut()},resetTransform(){n.get(this)?.reset()},setTransform(e){n.get(this)?.set(e)},expandAll(){this.collapsedIds=[]},toggle(e){this.collapsedIds=[...i.toggleCollapsed(new Set(this.collapsedIds),e)]},toggleNode(e){this.toggle(e.id),this.$emit("toggle",e,this.collapsedIds.includes(e.id))},onNodeClick(e,t){this.$emit("node-click",e,t),this.collapsible&&e.hasChildren&&this.toggleNode(e)},nodeStyle(e){return{left:`${e.x}px`,top:`${e.y}px`,width:`${e.width}px`,height:`${e.height}px`}},nodeClassOf(e){return typeof this.nodeClass=="function"?this.nodeClass(e):this.nodeClass},labelOf(e){const t=e.data;return t?.name??t?.title??t?.label??e.id},renderNodeContent(e,t){const r=this.$scopedSlots.node;return r?r({node:t,data:t.data,depth:t.depth,collapsed:t.collapsed,hasChildren:t.hasChildren,toggle:()=>this.toggleNode(t)})??[]:e(c,{props:{label:this.labelOf(t),hasChildren:t.hasChildren,collapsed:t.collapsed},on:{toggle:()=>this.toggleNode(t)}})}},render(e){const t=this.layout,r=this.drawn,s=this.$scopedSlots.link,a=r.links.map(o=>e("g",{key:o.id,attrs:{"data-depth":o.target.depth}},[s?s({link:o})??[]:e("path",{class:"tree-view__link",attrs:{d:o.path}})])),d=r.nodes.map(o=>e("div",{key:o.id,class:["tree-view__node",this.nodeClassOf(o)],style:this.nodeStyle(o),attrs:{"data-depth":o.depth},on:{click:b=>this.onNodeClick(o,b)}},[].concat(this.renderNodeContent(e,o))));return e("div",{class:["tree-view",{"tree-view--pannable":this.pannable,"tree-view--content":this.size==="content"}],style:this.rootStyle},[e("div",{ref:"canvas",class:"tree-view__canvas",style:this.canvasStyle},[e("svg",{class:"tree-view__links",attrs:{width:t.width,height:t.height}},a),...d])])}}),p=l.extend({name:"BracketCard",props:{match:{type:Object,default:null},rows:{type:Number,default:void 0},myTeamHint:{type:String,default:"Нажми для перехода в матч 👇"},techDefeatLabel:{type:String,default:"ТП"},techDefeatHint:{type:String,default:"Техническое поражение"}},data(){return{hoveredKey:null}},computed:{card(){return i.buildBracketCard(this.match,{rows:this.rows,techDefeatLabel:this.techDefeatLabel})}},methods:{renderRow(e,t){const r=[];return t.team&&(t.isTechDefeat&&this.hoveredKey===t.key&&r.push(e("span",{class:"tv-bracket__tooltip"},this.techDefeatHint)),r.push(e("span",{class:"tv-bracket__name"},t.team.name)),r.push(e("span",{class:"tv-bracket__score"},t.score))),e("div",{key:t.key,class:["tv-bracket__row",{"tv-bracket__row--winner":t.isWinner,"tv-bracket__row--loser":t.isLoser&&!t.isEmpty,"tv-bracket__row--my":t.isMyTeam,"tv-bracket__row--empty":t.isEmpty}],attrs:t.isMyTeam?{title:"Моя команда"}:{},on:{click:()=>{t.team&&this.$emit("select",t.team,t)},mouseenter:()=>{this.hoveredKey=t.key},mouseleave:()=>{this.hoveredKey=null}}},r)}},render(e){const t=this.card,r=[];t.hasMyTeam&&this.myTeamHint&&r.push(e("div",{class:"tv-bracket__hint"},this.myTeamHint));for(const s of t.rows)r.push(this.renderRow(e,s));return e("div",{class:["tv-bracket",{"tv-bracket--my-team":t.hasMyTeam}]},r)}}),u=l.extend({name:"BracketRounds",props:{rounds:{type:Array,default:()=>[]},options:{type:Object,default:void 0},isShow:{type:Boolean,default:!0},swipeSiblingGap:{type:Number,default:12},hidePassed:{type:Boolean,default:!0},swipe:{type:Boolean,default:void 0},mobileQuery:{type:String,default:i.MOBILE_MEDIA_QUERY}},data(){return{isNarrow:!1,available:0,current:0,stop:[]}},computed:{isSwipe(){return this.isShow&&(this.swipe===void 0?this.isNarrow:this.swipe)},base(){return i.resolveOptions(this.options)},nodeWidth(){return typeof this.base.nodeWidth=="number"?this.base.nodeWidth:i.DEFAULT_OPTIONS.nodeWidth},levelGap(){return this.isSwipe?i.levelGapForWidth(this.available,this.nodeWidth):this.base.levelGap},model(){return i.buildBracketRounds(this.rounds,{nodeWidth:this.nodeWidth,levelGap:this.levelGap})},mirrored(){return this.base.direction==="right-to-left"||this.base.direction==="bottom-to-top"},gridOptions(){if(!this.isSwipe)return{...this.options};const e=this.swipeSiblingGap;return{...this.options,levelGap:this.levelGap,levelLayout:"stack",siblingGap:(t,r)=>i.roundSiblingGap(r,e)}}},watch:{current:"applyHidden",isSwipe:"applyHidden",model:"applyHidden"},mounted(){this.stop.push(i.watchMedia(this.mobileQuery,e=>{this.isNarrow=e}),i.watchWidth(this.$el.parentElement,e=>{this.available=e}),i.watchChildren(this.$refs.content,()=>this.applyHidden())),this.applyHidden()},beforeDestroy(){this.stop.forEach(e=>e())},methods:{applyHidden(){const e=this.$refs.content;if(!e)return;const t=this.isSwipe&&this.hidePassed?this.current:0,r=this.model.columns.length;i.hideRoundsBefore(e,t,s=>i.roundOfDepth(s,r,this.mirrored))},onScroll(){const e=this.$el;if(!e||!this.isSwipe)return;const t=this.model.columns[0]?.width??0,r=i.roundAtScroll(e.scrollLeft,t,this.model.columns.length);r!==this.current&&(this.current=r,this.$emit("round",r))},scrollToRound(e){const t=this.$el;if(!t)return;const r=this.isSwipe?t:i.findScrollParent(t);if(!r)return;const s=this.$refs.content,a=s?i.nodesInColumn(s,e.index,e.width,this.model.offset)[0]:void 0,d=a?i.horizontalBox(a,r):{left:i.offsetWithin(t,r)+e.index*e.width,width:e.width};i.smoothScrollLeft(r,i.roundScrollLeft({...d,viewport:r.clientWidth,scrollWidth:r.scrollWidth}))},onSelect(e){this.$emit("select",e),this.scrollToRound(e)},renderColumn(e,t){return e("div",{key:t.key,class:"tv-rounds__item",style:{width:`${t.width}px`},on:{click:()=>this.onSelect(t)}},this.$scopedSlots.round?.({round:t})??t.name)}},render(e){const t=[];this.isShow&&t.push(e("div",{class:"tv-rounds__bar",style:{width:`${this.model.width}px`}},this.model.columns.map(s=>this.renderColumn(e,s))));const r=this.$scopedSlots.default?.({options:this.gridOptions,isSwipe:this.isSwipe,round:this.current})??this.$slots.default;return t.push(e("div",{ref:"content",class:"tv-rounds__content",style:this.isShow?{paddingLeft:`${this.model.offset}px`}:{}},r)),e("div",{class:["tv-rounds",{"tv-rounds--swipe":this.isSwipe}],on:{scroll:()=>this.onScroll()}},t)}}),f=".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}.tree-view--pannable{cursor:grab;touch-action:none}.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}.tv-rounds{--tv-rounds-bg: #f5f5f7;--tv-rounds-color: #9ca3af;--tv-rounds-border: 2px solid #e5e7eb;--tv-rounds-gap: 20px;--tv-rounds-fade: .22s;--tv-rounds-link-color: #94a3b8;--tv-rounds-link-width: 3px;display:flex;flex-direction:column;width:max-content;min-width:100%}.tv-rounds__bar{display:flex;min-width:100%;margin-bottom:var(--tv-rounds-gap);background:var(--tv-rounds-bg);border-bottom:var(--tv-rounds-border)}.tv-rounds__item{flex:none;box-sizing:border-box;padding:10px 20px;font:inherit;font-size:14px;font-weight:600;color:var(--tv-rounds-color);text-align:center;text-transform:uppercase;white-space:nowrap;background:none;border:none;border-right:var(--tv-rounds-border)}.tv-rounds--swipe .tree-view{--tv-link-color: var(--tv-rounds-link-color);--tv-link-width: var(--tv-rounds-link-width)}.tv-rounds--swipe{width:100%;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:none}.tv-rounds--swipe::-webkit-scrollbar{display:none}.tv-rounds--swipe .tv-rounds__item{scroll-snap-align:start}.tv-rounds--swipe [data-depth]{transition:opacity var(--tv-rounds-fade, .22s) ease}.tv-rounds--swipe [data-depth].tv-hidden{visibility:hidden;opacity:0;transition:opacity var(--tv-rounds-fade, .22s) ease,visibility 0s linear var(--tv-rounds-fade, .22s)}.tv-hidden{visibility:hidden}@media(prefers-reduced-motion:reduce){.tv-rounds--swipe [data-depth],.tv-rounds--swipe [data-depth].tv-hidden{transition:none}}";i.injectStyles(f);const v={install(e){e.component("TreeView",h),e.component("TreeNodeCard",c),e.component("BracketCard",p),e.component("BracketRounds",u)}};Object.defineProperty(exports,"DEFAULT_OPTIONS",{enumerable:!0,get:()=>i.DEFAULT_OPTIONS});Object.defineProperty(exports,"MOBILE_MEDIA_QUERY",{enumerable:!0,get:()=>i.MOBILE_MEDIA_QUERY});Object.defineProperty(exports,"bracketCardHeight",{enumerable:!0,get:()=>i.bracketCardHeight});Object.defineProperty(exports,"buildBracketCard",{enumerable:!0,get:()=>i.buildBracketCard});Object.defineProperty(exports,"buildBracketRounds",{enumerable:!0,get:()=>i.buildBracketRounds});exports.BracketCard=p;exports.BracketRounds=u;exports.TreeNodeCard=c;exports.TreeView=h;exports.TreeViewPlugin=v;exports.default=v;
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const d=require("vue"),i=require("@bigplay/tree-view-core"),v=d.extend({name:"TreeNodeCard",props:{label:{type:String,required:!0},hasChildren:{type:Boolean,default:!1},collapsed:{type:Boolean,default:!1}},render(t){const e=[t("span",{class:"tree-view-card__label"},this.label)];return this.hasChildren&&e.push(t("button",{class:"tree-view-card__toggle",attrs:{type:"button","data-tv-no-pan":"","aria-label":this.collapsed?"Развернуть":"Свернуть"},on:{click:s=>{s.stopPropagation(),this.$emit("toggle")}}},this.collapsed?"+":"−")),t("div",{class:"tree-view-card"},e)}}),a=new WeakMap,b=d.extend({name:"TreeView",props:{data:{type:[Object,Array],default:null},options:{type:Object,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:{type:String,default:"fill"},scaleLimits:{type:Object,default:void 0},nodeClass:{type:[String,Function],default:void 0},virtualize:{type:Boolean,default:!0},overscan:{type:Number,default:i.DEFAULT_OVERSCAN}},data(){return{collapsedIds:[],transform:{x:0,y:0,scale:1},visibleRect:this.virtualize?i.estimateVisibleRect():null}},inject:{activeRound:{default:null}},computed:{layout(){const t=i.toTree(this.data,{getId:this.getId,getChildren:this.getChildren,getParentId:this.getParentId});return i.layoutTree(i.applyCollapsed(t,new Set(this.collapsedIds)),this.options)},drawn(){return this.virtualize?i.visibleLayout(this.layout,this.visibleRect,this.overscan):this.layout},canvasHeight(){const e=this.activeRound?.value??null;if(e===null)return this.layout.height;const s=this.layout.nodes.reduce((n,c)=>Math.max(n,c.depth),0)+1,r=i.resolveOptions(this.options).direction,o=r==="right-to-left"||r==="bottom-to-top";return i.roundContentHeight(this.layout.nodes,e,s,o)||this.layout.height},canvasStyle(){return{width:`${this.layout.width}px`,height:`${this.canvasHeight}px`,transform:i.toCssTransform(this.transform)}},rootStyle(){if(this.size==="content")return{width:`${this.layout.width*this.transform.scale}px`,height:`${this.canvasHeight*this.transform.scale}px`,overflow:"visible"}}},watch:{layout(){this.$nextTick(()=>this.updateVisibleRect())}},mounted(){const t=new i.PanZoomController({pannable:()=>this.pannable,zoomable:()=>this.zoomable,limits:this.scaleLimits?()=>this.scaleLimits:void 0,onChange:e=>{this.transform=e,this.updateVisibleRect(),this.$emit("transform",e)}});t.attach(this.$el),a.set(this,t),this.updateVisibleRect(),window.addEventListener("scroll",this.updateVisibleRect,{passive:!0,capture:!0}),window.addEventListener("resize",this.updateVisibleRect,{passive:!0}),this.fitOnMount&&this.$nextTick(()=>this.fit())},beforeDestroy(){a.get(this)?.detach(),a.delete(this),window.removeEventListener("scroll",this.updateVisibleRect,{capture:!0}),window.removeEventListener("resize",this.updateVisibleRect)},methods:{updateVisibleRect(){this.visibleRect=this.virtualize?i.measureVisibleRect(this.$refs.canvas,this.transform.scale):null},fit(t){a.get(this)?.fit({width:this.layout.width,height:this.layout.height},t)},zoomIn(){a.get(this)?.zoomIn()},zoomOut(){a.get(this)?.zoomOut()},resetTransform(){a.get(this)?.reset()},setTransform(t){a.get(this)?.set(t)},expandAll(){this.collapsedIds=[]},toggle(t){this.collapsedIds=[...i.toggleCollapsed(new Set(this.collapsedIds),t)]},toggleNode(t){this.toggle(t.id),this.$emit("toggle",t,this.collapsedIds.includes(t.id))},onNodeClick(t,e){this.$emit("node-click",t,e),this.collapsible&&t.hasChildren&&this.toggleNode(t)},nodeStyle(t){return{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`}},nodeClassOf(t){return typeof this.nodeClass=="function"?this.nodeClass(t):this.nodeClass},labelOf(t){const e=t.data;return e?.name??e?.title??e?.label??t.id},renderNodeContent(t,e){const s=this.$scopedSlots.node;return s?s({node:e,data:e.data,depth:e.depth,collapsed:e.collapsed,hasChildren:e.hasChildren,toggle:()=>this.toggleNode(e)})??[]:t(v,{props:{label:this.labelOf(e),hasChildren:e.hasChildren,collapsed:e.collapsed},on:{toggle:()=>this.toggleNode(e)}})}},render(t){const e=this.layout,s=this.drawn,r=this.$scopedSlots.link,o=s.links.map(n=>t("g",{key:n.id,attrs:{"data-depth":n.target.depth}},[r?r({link:n})??[]:t("path",{class:"tree-view__link",attrs:{d:n.path}})])),l=s.nodes.map(n=>t("div",{key:n.id,class:["tree-view__node",this.nodeClassOf(n)],style:this.nodeStyle(n),attrs:{"data-depth":n.depth},on:{click:c=>this.onNodeClick(n,c)}},[].concat(this.renderNodeContent(t,n))));return t("div",{class:["tree-view",{"tree-view--pannable":this.pannable,"tree-view--content":this.size==="content"}],style:this.rootStyle},[t("div",{ref:"canvas",class:"tree-view__canvas",style:this.canvasStyle},[t("svg",{class:"tree-view__links",attrs:{width:e.width,height:e.height}},o),...l])])}}),f=d.extend({name:"BracketCard",props:{match:{type:Object,default:null},rows:{type:Number,default:void 0},myTeamHint:{type:String,default:"Нажми для перехода в матч 👇"},techDefeatLabel:{type:String,default:"ТП"},techDefeatHint:{type:String,default:"Техническое поражение"},hasChildren:{type:Boolean,default:!1},collapsed:{type:Boolean,default:!1},collapseHint:{type:String,default:"Свернуть предыдущие матчи"},expandHint:{type:String,default:"Показать предыдущие матчи"}},data(){return{hoveredKey:null}},computed:{card(){return i.buildBracketCard(this.match,{rows:this.rows,techDefeatLabel:this.techDefeatLabel})}},methods:{renderToggle(t){const e=this.collapsed?this.expandHint:this.collapseHint;return t("button",{class:["tv-bracket__toggle",{"tv-bracket__toggle--collapsed":this.collapsed}],attrs:{type:"button",title:e,"aria-label":e,"aria-expanded":String(!this.collapsed),"data-tv-no-pan":""},on:{click:s=>{s.stopPropagation(),this.$emit("toggle")}}},this.collapsed?"+":"−")},renderRow(t,e){const s=[];return e.team&&(e.isTechDefeat&&this.hoveredKey===e.key&&s.push(t("span",{class:"tv-bracket__tooltip"},this.techDefeatHint)),s.push(t("span",{class:"tv-bracket__name"},e.team.name)),s.push(t("span",{class:"tv-bracket__score"},e.score))),t("div",{key:e.key,class:["tv-bracket__row",{"tv-bracket__row--winner":e.isWinner,"tv-bracket__row--loser":e.isLoser&&!e.isEmpty,"tv-bracket__row--my":e.isMyTeam,"tv-bracket__row--empty":e.isEmpty}],attrs:e.isMyTeam?{title:"Моя команда"}:{},on:{click:()=>{e.team&&this.$emit("select",e.team,e)},mouseenter:()=>{this.hoveredKey=e.key},mouseleave:()=>{this.hoveredKey=null}}},s)}},render(t){const e=this.card,s=[];return e.hasMyTeam&&this.myTeamHint&&s.push(t("div",{class:"tv-bracket__hint"},this.myTeamHint)),this.hasChildren&&s.push(this.renderToggle(t)),s.push(t("div",{class:"tv-bracket__rows"},e.rows.map(r=>this.renderRow(t,r)))),t("div",{class:["tv-bracket",{"tv-bracket--my-team":e.hasMyTeam}]},s)}}),h=new WeakSet,p=new WeakMap,u=new WeakSet,g=d.extend({name:"BracketRounds",props:{rounds:{type:Array,default:()=>[]},options:{type:Object,default:void 0},isShow:{type:Boolean,default:!0},swipeSiblingGap:{type:Number,default:12},hidePassed:{type:Boolean,default:!0},swipe:{type:Boolean,default:void 0},mobileQuery:{type:String,default:i.MOBILE_MEDIA_QUERY}},data(){return{isNarrow:!1,available:0,current:0,stop:[]}},computed:{isSwipe(){return this.isShow&&(this.swipe===void 0?this.isNarrow:this.swipe)},base(){return i.resolveOptions(this.options)},nodeWidth(){return typeof this.base.nodeWidth=="number"?this.base.nodeWidth:i.DEFAULT_OPTIONS.nodeWidth},levelGap(){return this.isSwipe?i.levelGapForWidth(this.available,this.nodeWidth):this.base.levelGap},model(){return i.buildBracketRounds(this.rounds,{nodeWidth:this.nodeWidth,levelGap:this.levelGap})},mirrored(){return this.base.direction==="right-to-left"||this.base.direction==="bottom-to-top"},gridOptions(){if(!this.isSwipe)return{...this.options};const t=this.swipeSiblingGap;return{...this.options,levelGap:this.levelGap,levelLayout:"stack",siblingGap:t,groupGap:(e,s)=>i.roundSiblingGap(s,t),linkStyle:"bracket",elbowOffset:this.options?.elbowOffset??i.SWIPE_ELBOW_OFFSET}}},watch:{current:"applyHidden",isSwipe:"applyHidden",model:"applyHidden"},mounted(){this.stop.push(i.watchMedia(this.mobileQuery,t=>{this.isNarrow=t}),i.watchWidth(this.$el.parentElement,t=>{this.available=t}),i.watchChildren(this.$refs.content,()=>this.applyHidden())),this.applyHidden()},beforeDestroy(){this.stop.forEach(t=>t())},provide(){const t=this;return{activeRound:{get value(){return t.isSwipe?t.current:null}}}},methods:{applyHidden(){const t=this.$refs.content;if(!t)return;const e=this.isSwipe&&this.hidePassed?this.current:0,s=this.model.columns.length;i.hideRoundsBefore(t,e,r=>i.roundOfDepth(r,s,this.mirrored))},onScroll(){const t=this.$refs.scroll;if(!t||!this.isSwipe)return;const e=this.$refs.head;e&&(e.scrollLeft=t.scrollLeft);const s=this.model.columns[0]?.width??0;let r=i.roundAtScroll(t.scrollLeft,s,this.model.columns.length);if(!h.has(this)&&s>0){const o=p.get(this)??0,l=Math.min(Math.max(r,o-1),o+1);l!==r&&(r=l,t.scrollLeft=r*s),u.has(this)||(u.add(this),i.afterScrollEnd(t,()=>{u.delete(this),p.set(this,this.current)}))}r!==this.current&&(this.current=r,this.$emit("round",r))},scrollToRound(t){const e=this.$el;if(!e)return;const s=this.isSwipe?this.$refs.scroll??null:i.findScrollParent(e);if(!s)return;const r=this.$refs.content,o=r?i.nodesInColumn(r,t.index,t.width,this.model.offset)[0]:void 0,l=o?i.horizontalBox(o,s):{left:i.offsetWithin(e,s)+t.index*t.width,width:t.width};h.add(this),i.smoothScrollLeft(s,i.roundScrollLeft({...l,viewport:s.clientWidth,scrollWidth:s.scrollWidth})),i.afterScrollEnd(s,()=>{h.delete(this),p.set(this,this.current)})},onSelect(t){this.$emit("select",t),this.scrollToRound(t)},renderColumn(t,e){return t("div",{key:e.key,class:"tv-rounds__item",style:{width:`${e.width}px`},on:{click:()=>this.onSelect(e)}},this.$scopedSlots.round?.({round:e})??e.name)}},render(t){const e=[];this.isShow&&e.push(t("div",{ref:"head",class:"tv-rounds__head"},[t("div",{class:"tv-rounds__bar",style:{width:`${this.model.width}px`}},this.model.columns.map(o=>this.renderColumn(t,o)))]));const s=this.$scopedSlots.default?.({options:this.gridOptions,isSwipe:this.isSwipe,round:this.current})??this.$slots.default,r=[];return this.isSwipe&&r.push(t("div",{class:"tv-rounds__snap",style:{width:`${this.model.width}px`},attrs:{"aria-hidden":"true"}},this.model.columns.map(o=>t("i",{key:o.key,class:"tv-rounds__snap-item",style:{width:`${o.width}px`}})))),r.push(t("div",{ref:"content",class:"tv-rounds__content",style:this.isShow?{paddingLeft:`${this.model.offset}px`}:{}},s)),e.push(t("div",{ref:"scroll",class:"tv-rounds__scroll",on:{scroll:()=>this.onScroll()}},r)),t("div",{class:["tv-rounds",{"tv-rounds--swipe":this.isSwipe}]},e)}}),m=".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}.tree-view--pannable{cursor:grab;touch-action:none}.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;--tv-bracket-shadow: 0 0 0 1px rgb(15 23 42 / 8%), 0 1px 3px rgb(15 23 42 / 10%);position:relative;box-sizing:border-box;display:flex;flex-direction:column;width:100%;height:100%;font-size:12px}.tv-bracket__rows{box-sizing:border-box;display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden;border-radius:var(--tv-bracket-radius);box-shadow:var(--tv-bracket-shadow)}.tv-bracket--my-team .tv-bracket__rows{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__toggle{position:absolute;top:50%;left:-11px;z-index:1;display:flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;font:inherit;font-size:13px;font-weight:700;line-height:1;color:var(--tv-bracket-winner-bg);cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:50%;box-shadow:0 1px 3px #0f172a2e;transform:translateY(-50%)}.tv-bracket__toggle:hover{border-color:var(--tv-bracket-winner-bg)}.tv-bracket__toggle--collapsed{color:var(--tv-bracket-winner-color);background:var(--tv-bracket-winner-bg);border-color:var(--tv-bracket-winner-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}.tv-rounds{--tv-rounds-bg: #f5f5f7;--tv-rounds-color: #9ca3af;--tv-rounds-border: 2px solid #e5e7eb;--tv-rounds-gap: 20px;--tv-rounds-fade: .22s;--tv-rounds-collapse-scale: .86;--tv-rounds-collapse-ease: cubic-bezier(.4, 0, .2, 1);--tv-rounds-link-color: #94a3b8;--tv-rounds-link-width: 3px;display:flex;flex-direction:column;width:max-content;min-width:100%}.tv-rounds__head{position:sticky;top:0;z-index:2;overflow:hidden;margin-bottom:var(--tv-rounds-gap);background:var(--tv-rounds-bg);border-bottom:var(--tv-rounds-border)}.tv-rounds__bar{display:flex;min-width:100%}.tv-rounds__scroll{position:relative}.tv-rounds__snap{display:flex;height:1px;margin-bottom:-1px}.tv-rounds__snap-item{flex:none;display:block}.tv-rounds__item{flex:none;box-sizing:border-box;padding:10px 20px;font:inherit;font-size:14px;font-weight:600;color:var(--tv-rounds-color);text-align:center;text-transform:uppercase;white-space:nowrap;background:none;border:none;border-right:var(--tv-rounds-border)}.tv-rounds--swipe .tv-bracket__hint{display:none}.tv-rounds--swipe .tree-view{--tv-link-color: var(--tv-rounds-link-color);--tv-link-width: var(--tv-rounds-link-width)}.tv-rounds--swipe{width:100%;min-width:0}.tv-rounds--swipe .tv-rounds__scroll{overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:none}.tv-rounds--swipe .tv-rounds__scroll::-webkit-scrollbar{display:none}.tv-rounds--swipe .tv-rounds__snap-item{scroll-snap-align:start;scroll-snap-stop:always}.tv-rounds--swipe [data-depth]{transition:none}.tv-rounds--swipe [data-depth].tv-hidden{visibility:hidden;opacity:0;transition:opacity var(--tv-rounds-fade, .22s) ease,transform var(--tv-rounds-fade, .22s) var(--tv-rounds-collapse-ease),visibility 0s linear var(--tv-rounds-fade, .22s)}.tv-rounds--swipe .tree-view__node.tv-hidden{transform:scale(var(--tv-rounds-collapse-scale));transform-origin:right center}.tv-hidden{visibility:hidden}@media(prefers-reduced-motion:reduce){.tv-rounds--swipe [data-depth],.tv-rounds--swipe [data-depth].tv-hidden{transition:none}.tv-rounds--swipe .tree-view__node.tv-hidden{transform:none}}";i.injectStyles(m);const w={install(t){t.component("TreeView",b),t.component("TreeNodeCard",v),t.component("BracketCard",f),t.component("BracketRounds",g)}};Object.defineProperty(exports,"DEFAULT_OPTIONS",{enumerable:!0,get:()=>i.DEFAULT_OPTIONS});Object.defineProperty(exports,"MOBILE_MEDIA_QUERY",{enumerable:!0,get:()=>i.MOBILE_MEDIA_QUERY});Object.defineProperty(exports,"bracketCardHeight",{enumerable:!0,get:()=>i.bracketCardHeight});Object.defineProperty(exports,"buildBracketCard",{enumerable:!0,get:()=>i.buildBracketCard});Object.defineProperty(exports,"buildBracketRounds",{enumerable:!0,get:()=>i.buildBracketRounds});exports.BracketCard=f;exports.BracketRounds=g;exports.TreeNodeCard=v;exports.TreeView=b;exports.TreeViewPlugin=w;exports.default=w;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/TreeNodeCard.ts","../src/TreeView.ts","../src/BracketCard.ts","../src/BracketRounds.ts","../src/index.ts"],"sourcesContent":["import Vue from 'vue'\n\n/**\n * Карточка узла по умолчанию для Vue 2.\n * Разметка и классы один в один как в Vue 3 — стили общие.\n */\nexport default Vue.extend({\n name: 'TreeNodeCard',\n props: {\n label: { type: String, required: true },\n hasChildren: { type: Boolean, default: false },\n collapsed: { type: Boolean, default: false },\n },\n render(h) {\n const children = [h('span', { class: 'tree-view-card__label' }, this.label)]\n\n if (this.hasChildren) {\n children.push(\n h(\n 'button',\n {\n class: 'tree-view-card__toggle',\n attrs: {\n type: 'button',\n 'data-tv-no-pan': '',\n 'aria-label': this.collapsed ? 'Развернуть' : 'Свернуть',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n ),\n )\n }\n\n return h('div', { class: 'tree-view-card' }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n applyCollapsed,\n DEFAULT_OVERSCAN,\n estimateVisibleRect,\n layoutTree,\n measureVisibleRect,\n PanZoomController,\n toCssTransform,\n toggleCollapsed,\n toTree,\n visibleLayout,\n type Accessors,\n type Layout,\n type LayoutNode,\n type Rect,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard'\n\n/** Данные узла могут быть какими угодно — библиотека читает их только аксессорами. */\ntype NodeData = any\n\n/**\n * Контроллеры держим здесь, а не в data(): Vue 2 сделал бы объект реактивным,\n * а внутри него DOM-элемент и Map — наблюдать за ними незачем.\n */\nconst controllers = new WeakMap<Vue, PanZoomController>()\n\nexport default Vue.extend({\n name: 'TreeView',\n props: {\n /** Объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: { type: [Object, Array] as PropType<NodeData>, default: null },\n /** Размеры и направление. Можно передать только часть полей. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId: { type: Function as PropType<Accessors<NodeData>['getId']>, default: undefined },\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren: { type: Function as PropType<Accessors<NodeData>['getChildren']>, default: undefined },\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId: { type: Function as PropType<Accessors<NodeData>['getParentId']>, default: undefined },\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible: { type: Boolean, default: false },\n /** Разрешить перетаскивание холста. */\n pannable: { type: Boolean, default: true },\n /** Разрешить зум колесом и «щипком». */\n zoomable: { type: Boolean, default: true },\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount: { type: Boolean, default: true },\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n */\n size: { type: String as PropType<'fill' | 'content'>, default: 'fill' },\n /** Пределы масштаба. */\n scaleLimits: { type: Object as PropType<ScaleLimits>, default: undefined },\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass: {\n type: [String, Function] as PropType<string | ((node: LayoutNode<NodeData>) => string)>,\n default: undefined,\n },\n /**\n * Рисовать только то, что видно на экране. На больших сетках в DOM\n * попадает несколько десятков карточек вместо тысячи.\n */\n virtualize: { type: Boolean, default: true },\n /** Запас вокруг видимой области в пикселях. */\n overscan: { type: Number, default: DEFAULT_OVERSCAN },\n },\n\n data() {\n return {\n /** Массив, а не Set: Vue 2 не умеет наблюдать за Set. */\n collapsedIds: [] as string[],\n transform: { x: 0, y: 0, scale: 1 } as Transform,\n /**\n * Видимая часть холста; `null` — рисуем всё.\n * До первого измерения берём размер окна: иначе первый кадр\n * нарисует дерево целиком и тут же выбросит лишнее.\n */\n visibleRect: (this.virtualize ? estimateVisibleRect() : null) as Rect | null,\n }\n },\n\n computed: {\n layout(): Layout<NodeData> {\n const tree = toTree<NodeData>(this.data, {\n getId: this.getId,\n getChildren: this.getChildren,\n getParentId: this.getParentId,\n })\n\n return layoutTree(applyCollapsed(tree, new Set(this.collapsedIds)), this.options)\n },\n\n /** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\n drawn(): Layout<NodeData> {\n return this.virtualize ? visibleLayout(this.layout, this.visibleRect, this.overscan) : this.layout\n },\n\n canvasStyle(): Record<string, string> {\n return {\n width: `${this.layout.width}px`,\n height: `${this.layout.height}px`,\n transform: toCssTransform(this.transform),\n }\n },\n\n rootStyle(): Record<string, string> | undefined {\n if (this.size !== 'content') return undefined\n\n return {\n width: `${this.layout.width * this.transform.scale}px`,\n height: `${this.layout.height * this.transform.scale}px`,\n overflow: 'visible',\n }\n },\n },\n\n watch: {\n // Новые данные — видимую часть надо пересчитать уже по ним.\n layout() {\n this.$nextTick(() => this.updateVisibleRect())\n },\n },\n\n mounted() {\n const controller = new PanZoomController({\n pannable: () => this.pannable,\n zoomable: () => this.zoomable,\n limits: this.scaleLimits ? () => this.scaleLimits : undefined,\n onChange: (transform) => {\n this.transform = transform\n // Холст поехал — видимая часть стала другой.\n this.updateVisibleRect()\n this.$emit('transform', transform)\n },\n })\n\n controller.attach(this.$el as HTMLElement)\n controllers.set(this, controller)\n\n this.updateVisibleRect()\n window.addEventListener('scroll', this.updateVisibleRect, { passive: true, capture: true })\n window.addEventListener('resize', this.updateVisibleRect, { passive: true })\n\n if (this.fitOnMount) this.$nextTick(() => this.fit())\n },\n\n beforeDestroy() {\n controllers.get(this)?.detach()\n controllers.delete(this)\n window.removeEventListener('scroll', this.updateVisibleRect, { capture: true } as EventListenerOptions)\n window.removeEventListener('resize', this.updateVisibleRect)\n },\n\n methods: {\n /** Пересчитывает, какая часть холста сейчас видна на экране. */\n updateVisibleRect(): void {\n this.visibleRect = this.virtualize\n ? measureVisibleRect(this.$refs.canvas as Element | undefined, this.transform.scale)\n : null\n },\n\n /** Вписывает дерево в видимую область. */\n fit(padding?: number): void {\n controllers.get(this)?.fit({ width: this.layout.width, height: this.layout.height }, padding)\n },\n zoomIn(): void {\n controllers.get(this)?.zoomIn()\n },\n zoomOut(): void {\n controllers.get(this)?.zoomOut()\n },\n resetTransform(): void {\n controllers.get(this)?.reset()\n },\n setTransform(transform: Transform): void {\n controllers.get(this)?.set(transform)\n },\n /** Развернуть все свёрнутые ветки. */\n expandAll(): void {\n this.collapsedIds = []\n },\n /** Свернуть или развернуть узел по id. */\n toggle(id: string): void {\n this.collapsedIds = [...toggleCollapsed(new Set(this.collapsedIds), id)]\n },\n\n toggleNode(node: LayoutNode<NodeData>): void {\n this.toggle(node.id)\n this.$emit('toggle', node, this.collapsedIds.includes(node.id))\n },\n\n onNodeClick(node: LayoutNode<NodeData>, event: MouseEvent): void {\n this.$emit('node-click', node, event)\n if (this.collapsible && node.hasChildren) this.toggleNode(node)\n },\n\n nodeStyle(node: LayoutNode<NodeData>): Record<string, string> {\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\n nodeClassOf(node: LayoutNode<NodeData>): string | undefined {\n return typeof this.nodeClass === 'function' ? this.nodeClass(node) : this.nodeClass\n },\n\n /** Подпись карточки по умолчанию: name → title → label → id. */\n labelOf(node: LayoutNode<NodeData>): 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\n /** Своя карточка из scoped-слота или карточка по умолчанию. */\n renderNodeContent(h: typeof Vue.prototype.$createElement, node: LayoutNode<NodeData>): VNode | VNode[] {\n const slot = this.$scopedSlots.node\n\n if (slot) {\n return (slot({\n node,\n data: node.data,\n depth: node.depth,\n collapsed: node.collapsed,\n hasChildren: node.hasChildren,\n toggle: () => this.toggleNode(node),\n }) ?? []) as VNode[]\n }\n\n return h(TreeNodeCard, {\n props: { label: this.labelOf(node), hasChildren: node.hasChildren, collapsed: node.collapsed },\n on: { toggle: () => this.toggleNode(node) },\n })\n },\n },\n\n render(h): VNode {\n const layout = this.layout\n const drawn = this.drawn\n const linkSlot = this.$scopedSlots.link\n\n // data-depth — уровень, из которого линия выходит: по нему её находят\n // снаружи, например когда полоса раундов прячет пройденное.\n const links = drawn.links.map((link) =>\n h('g', { key: link.id, attrs: { 'data-depth': link.target.depth } }, [\n linkSlot\n ? ((linkSlot({ link }) ?? []) as VNode[])\n : h('path', { class: 'tree-view__link', attrs: { d: link.path } }),\n ]),\n )\n\n const nodes = drawn.nodes.map((node) =>\n h(\n 'div',\n {\n key: node.id,\n class: ['tree-view__node', this.nodeClassOf(node)],\n style: this.nodeStyle(node),\n attrs: { 'data-depth': node.depth },\n on: { click: (event: MouseEvent) => this.onNodeClick(node, event) },\n },\n ([] as VNode[]).concat(this.renderNodeContent(h, node)),\n ),\n )\n\n return h(\n 'div',\n {\n class: [\n 'tree-view',\n { 'tree-view--pannable': this.pannable, 'tree-view--content': this.size === 'content' },\n ],\n style: this.rootStyle,\n },\n [\n h('div', { ref: 'canvas', class: 'tree-view__canvas', style: this.canvasStyle }, [\n h(\n 'svg',\n {\n class: 'tree-view__links',\n attrs: { width: layout.width, height: layout.height },\n },\n links as VNode[],\n ),\n ...nodes,\n ]),\n ],\n )\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n} from '@bigplay/tree-view-core'\n\n/**\n * Карточка матча для Vue 2. Разметка и классы один в один как в Vue 3 —\n * строки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketCard',\n props: {\n /** Матч: нужны только команды. */\n match: { type: Object as PropType<BracketCardMatch | null>, default: null },\n /** Сколько строк рисовать. У финала — одна. */\n rows: { type: Number, default: undefined },\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint: { type: String, default: 'Нажми для перехода в матч 👇' },\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel: { type: String, default: 'ТП' },\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint: { type: String, default: 'Техническое поражение' },\n },\n\n data() {\n return { hoveredKey: null as string | null }\n },\n\n computed: {\n card(): ReturnType<typeof buildBracketCard> {\n return buildBracketCard(this.match, { rows: this.rows, techDefeatLabel: this.techDefeatLabel })\n },\n },\n\n methods: {\n renderRow(h: typeof Vue.prototype.$createElement, row: BracketCardRow): VNode {\n const content: VNode[] = []\n\n if (row.team) {\n if (row.isTechDefeat && this.hoveredKey === row.key) {\n content.push(h('span', { class: 'tv-bracket__tooltip' }, this.techDefeatHint))\n }\n content.push(h('span', { class: 'tv-bracket__name' }, row.team.name))\n content.push(h('span', { class: 'tv-bracket__score' }, row.score))\n }\n\n return h(\n 'div',\n {\n key: row.key,\n class: [\n 'tv-bracket__row',\n {\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 ],\n attrs: row.isMyTeam ? { title: 'Моя команда' } : {},\n on: {\n click: () => {\n if (row.team) this.$emit('select', row.team, row)\n },\n mouseenter: () => {\n this.hoveredKey = row.key\n },\n mouseleave: () => {\n this.hoveredKey = null\n },\n },\n },\n content,\n )\n },\n },\n\n render(h): VNode {\n const card = this.card\n const children: VNode[] = []\n\n if (card.hasMyTeam && this.myTeamHint) {\n children.push(h('div', { class: 'tv-bracket__hint' }, this.myTeamHint))\n }\n\n for (const row of card.rows) children.push(this.renderRow(h, row))\n\n return h('div', { class: ['tv-bracket', { 'tv-bracket--my-team': card.hasMyTeam }] }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketRounds,\n findScrollParent,\n hideRoundsBefore,\n horizontalBox,\n levelGapForWidth,\n nodesInColumn,\n offsetWithin,\n resolveOptions,\n roundAtScroll,\n roundOfDepth,\n roundScrollLeft,\n roundSiblingGap,\n smoothScrollLeft,\n watchChildren,\n watchMedia,\n watchWidth,\n type BracketRound,\n type BracketRoundColumn,\n type BracketRoundsModel,\n type TreeNode,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/**\n * Полоса раундов для Vue 2. Разметка и классы один в один как в Vue 3 —\n * колонки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketRounds',\n props: {\n /** Раунды по порядку — от первого к финалу. */\n rounds: { type: Array as PropType<BracketRound[]>, default: () => [] },\n /** Настройки сетки — те же, что уходят в `TreeView`. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Показывать полосу. Содержимое слота рисуется в любом случае. */\n isShow: { type: Boolean, default: true },\n /**\n * Базовое расстояние между матчами одного раунда в свайпере.\n * Раунд с несколькими матчами получает кратно больше — до четырёх раз.\n */\n swipeSiblingGap: { type: Number, default: 12 },\n /** Прятать пройденные раунды и линии, которые из них выходят. */\n hidePassed: { type: Boolean, default: true },\n /** Включить свайпер принудительно. Не передан — решает ширина экрана. */\n swipe: { type: Boolean, default: undefined },\n /** Медиавыражение «узкий экран», при котором включается свайпер. */\n mobileQuery: { type: String, default: MOBILE_MEDIA_QUERY },\n },\n\n data() {\n return {\n // Узкий ли экран. До монтирования — нет: на сервере ширину знать неоткуда.\n isNarrow: false,\n // Сколько места отведено полосе: по нему считается ширина раунда в свайпере.\n available: 0,\n // Раунд, на котором стоит прокрутка. Меняется прямо во время свайпа.\n current: 0,\n stop: [] as Array<() => void>,\n }\n },\n\n computed: {\n /** Полосы нет — свайпать нечего: сетку в это время таскают и масштабируют. */\n isSwipe(): boolean {\n return this.isShow && (this.swipe === undefined ? this.isNarrow : this.swipe)\n },\n\n base(): TreeViewOptions {\n return resolveOptions(this.options)\n },\n\n /**\n * Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —\n * число. Если это всё-таки функция, считать колонки не по чему: берём значение\n * по умолчанию, чтобы полоса не развалилась.\n */\n nodeWidth(): number {\n return typeof this.base.nodeWidth === 'number'\n ? this.base.nodeWidth\n : (DEFAULT_OPTIONS.nodeWidth as number)\n },\n\n levelGap(): number {\n return this.isSwipe\n ? levelGapForWidth(this.available, this.nodeWidth)\n : this.base.levelGap\n },\n\n model(): BracketRoundsModel {\n return buildBracketRounds(this.rounds, {\n nodeWidth: this.nodeWidth,\n levelGap: this.levelGap,\n })\n },\n\n /** Дерево турнирной сетки растёт справа налево: первый раунд — самый глубокий. */\n mirrored(): boolean {\n return this.base.direction === 'right-to-left' || this.base.direction === 'bottom-to-top'\n },\n\n /**\n * Настройки для сетки в свайпере.\n *\n * Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по\n * себе: в дереве место второго раунда выводится из первого, поэтому отдельно\n * его отступ не задать. Сам отступ зависит от того, сколько матчей в раунде.\n */\n gridOptions(): Partial<TreeViewOptions> {\n if (!this.isSwipe) return { ...this.options }\n\n const base = this.swipeSiblingGap\n\n return {\n ...this.options,\n levelGap: this.levelGap,\n levelLayout: 'stack',\n siblingGap: (_node: TreeNode<unknown>, count: number) => roundSiblingGap(count, base),\n }\n },\n },\n\n watch: {\n current: 'applyHidden',\n isSwipe: 'applyHidden',\n model: 'applyHidden',\n },\n\n mounted() {\n this.stop.push(\n watchMedia(this.mobileQuery, (matches) => {\n this.isNarrow = matches\n }),\n watchWidth((this.$el as HTMLElement).parentElement, (width) => {\n this.available = width\n }),\n // Сетка перерисовывает карточки — скрытое расставляем заново.\n watchChildren(this.$refs.content as HTMLElement, () => this.applyHidden()),\n )\n\n this.applyHidden()\n },\n\n beforeDestroy() {\n this.stop.forEach((off) => off())\n },\n\n methods: {\n /** Прячет всё, что осталось позади: карточки пройденных раундов и их линии. */\n applyHidden(): void {\n const content = this.$refs.content as HTMLElement | undefined\n if (!content) return\n\n const hideBefore = this.isSwipe && this.hidePassed ? this.current : 0\n const count = this.model.columns.length\n\n hideRoundsBefore(content, hideBefore, (depth) =>\n roundOfDepth(depth, count, this.mirrored),\n )\n },\n\n onScroll(): void {\n const element = this.$el as HTMLElement | null\n if (!element || !this.isSwipe) return\n\n const width = this.model.columns[0]?.width ?? 0\n const next = roundAtScroll(element.scrollLeft, width, this.model.columns.length)\n if (next === this.current) return\n\n this.current = next\n this.$emit('round', next)\n },\n\n /**\n * Подводит сетку к матчам раунда — только по горизонтали.\n *\n * Меряем по самой карточке: так раунд встаёт по центру экрана точно, а у финала\n * прокрутка упирается в правый край и он виден целиком. Карточек не нашли\n * (виртуализация) — считаем по колонке, она той же ширины и на том же месте.\n *\n * Вертикаль не трогаем: страница остаётся там, где была.\n */\n scrollToRound(column: BracketRoundColumn): void {\n const element = this.$el as HTMLElement | null\n if (!element) return\n\n // В свайпере прокручивается сама полоса, иначе — блок с overflow-x вокруг неё.\n const container = this.isSwipe ? element : findScrollParent(element)\n if (!container) return\n\n const content = this.$refs.content as HTMLElement | undefined\n // Карточки раунда стоят друг под другом, поэтому по горизонтали годится любая.\n const card = content\n ? nodesInColumn(content, column.index, column.width, this.model.offset)[0]\n : undefined\n\n const box = card\n ? horizontalBox(card, container)\n : {\n left: offsetWithin(element, container) + column.index * column.width,\n width: column.width,\n }\n\n smoothScrollLeft(\n container,\n roundScrollLeft({\n ...box,\n viewport: container.clientWidth,\n scrollWidth: container.scrollWidth,\n }),\n )\n },\n\n onSelect(column: BracketRoundColumn): void {\n this.$emit('select', column)\n this.scrollToRound(column)\n },\n\n renderColumn(h: typeof Vue.prototype.$createElement, column: BracketRoundColumn): VNode {\n return h(\n 'div',\n {\n key: column.key,\n class: 'tv-rounds__item',\n style: { width: `${column.width}px` },\n on: { click: () => this.onSelect(column) },\n },\n this.$scopedSlots.round?.({ round: column }) ?? column.name,\n )\n },\n },\n\n render(h): VNode {\n const children: VNode[] = []\n\n if (this.isShow) {\n children.push(\n h(\n 'div',\n { class: 'tv-rounds__bar', style: { width: `${this.model.width}px` } },\n this.model.columns.map((column) => this.renderColumn(h, column)),\n ),\n )\n }\n\n // Сюда кладут саму сетку. `options` — настройки, подогнанные под экран:\n // в свайпере раунд занимает всю ширину, а матчи внутри стоят ближе.\n const content =\n this.$scopedSlots.default?.({\n options: this.gridOptions,\n isSwipe: this.isSwipe,\n round: this.current,\n }) ?? this.$slots.default\n\n children.push(\n h(\n 'div',\n {\n ref: 'content',\n class: 'tv-rounds__content',\n style: this.isShow ? { paddingLeft: `${this.model.offset}px` } : {},\n },\n content,\n ),\n )\n\n return h(\n 'div',\n {\n class: ['tv-rounds', { 'tv-rounds--swipe': this.isSwipe }],\n on: { scroll: () => this.onScroll() },\n },\n children,\n )\n },\n})\n","import type { VueConstructor } from 'vue'\nimport TreeView from './TreeView'\nimport TreeNodeCard from './TreeNodeCard'\nimport BracketCard from './BracketCard'\nimport BracketRounds from './BracketRounds'\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, BracketRounds }\n\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n BracketRound,\n BracketRoundColumn,\n BracketRoundsModel,\n BracketRoundsOptions,\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 {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketCard,\n bracketCardHeight,\n buildBracketRounds,\n} from '@bigplay/tree-view-core'\n\n/** Плагин: `Vue.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin = {\n install(Vue: VueConstructor) {\n Vue.component('TreeView', TreeView)\n Vue.component('TreeNodeCard', TreeNodeCard)\n Vue.component('BracketCard', BracketCard)\n Vue.component('BracketRounds', BracketRounds)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["TreeNodeCard","Vue","h","children","event","controllers","TreeView","DEFAULT_OVERSCAN","estimateVisibleRect","tree","toTree","layoutTree","applyCollapsed","visibleLayout","toCssTransform","controller","PanZoomController","transform","measureVisibleRect","padding","id","toggleCollapsed","node","data","slot","layout","drawn","linkSlot","links","link","nodes","BracketCard","buildBracketCard","row","content","card","BracketRounds","MOBILE_MEDIA_QUERY","resolveOptions","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","watchMedia","matches","watchWidth","width","watchChildren","off","hideBefore","hideRoundsBefore","depth","roundOfDepth","element","next","roundAtScroll","column","container","findScrollParent","nodesInColumn","box","horizontalBox","offsetWithin","smoothScrollLeft","roundScrollLeft","injectStyles","styles","TreeViewPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/TreeNodeCard.ts","../src/TreeView.ts","../src/BracketCard.ts","../src/BracketRounds.ts","../src/index.ts"],"sourcesContent":["import Vue from 'vue'\n\n/**\n * Карточка узла по умолчанию для Vue 2.\n * Разметка и классы один в один как в Vue 3 — стили общие.\n */\nexport default Vue.extend({\n name: 'TreeNodeCard',\n props: {\n label: { type: String, required: true },\n hasChildren: { type: Boolean, default: false },\n collapsed: { type: Boolean, default: false },\n },\n render(h) {\n const children = [h('span', { class: 'tree-view-card__label' }, this.label)]\n\n if (this.hasChildren) {\n children.push(\n h(\n 'button',\n {\n class: 'tree-view-card__toggle',\n attrs: {\n type: 'button',\n 'data-tv-no-pan': '',\n 'aria-label': this.collapsed ? 'Развернуть' : 'Свернуть',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n ),\n )\n }\n\n return h('div', { class: 'tree-view-card' }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n applyCollapsed,\n DEFAULT_OVERSCAN,\n estimateVisibleRect,\n layoutTree,\n measureVisibleRect,\n PanZoomController,\n resolveOptions,\n roundContentHeight,\n toCssTransform,\n toggleCollapsed,\n toTree,\n visibleLayout,\n type Accessors,\n type Layout,\n type LayoutNode,\n type Rect,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard'\n\n/** Данные узла могут быть какими угодно — библиотека читает их только аксессорами. */\ntype NodeData = any\n\n/**\n * Контроллеры держим здесь, а не в data(): Vue 2 сделал бы объект реактивным,\n * а внутри него DOM-элемент и Map — наблюдать за ними незачем.\n */\nconst controllers = new WeakMap<Vue, PanZoomController>()\n\nexport default Vue.extend({\n name: 'TreeView',\n props: {\n /** Объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: { type: [Object, Array] as PropType<NodeData>, default: null },\n /** Размеры и направление. Можно передать только часть полей. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId: { type: Function as PropType<Accessors<NodeData>['getId']>, default: undefined },\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren: { type: Function as PropType<Accessors<NodeData>['getChildren']>, default: undefined },\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId: { type: Function as PropType<Accessors<NodeData>['getParentId']>, default: undefined },\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible: { type: Boolean, default: false },\n /** Разрешить перетаскивание холста. */\n pannable: { type: Boolean, default: true },\n /** Разрешить зум колесом и «щипком». */\n zoomable: { type: Boolean, default: true },\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount: { type: Boolean, default: true },\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n */\n size: { type: String as PropType<'fill' | 'content'>, default: 'fill' },\n /** Пределы масштаба. */\n scaleLimits: { type: Object as PropType<ScaleLimits>, default: undefined },\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass: {\n type: [String, Function] as PropType<string | ((node: LayoutNode<NodeData>) => string)>,\n default: undefined,\n },\n /**\n * Рисовать только то, что видно на экране. На больших сетках в DOM\n * попадает несколько десятков карточек вместо тысячи.\n */\n virtualize: { type: Boolean, default: true },\n /** Запас вокруг видимой области в пикселях. */\n overscan: { type: Number, default: DEFAULT_OVERSCAN },\n },\n\n data() {\n return {\n /** Массив, а не Set: Vue 2 не умеет наблюдать за Set. */\n collapsedIds: [] as string[],\n transform: { x: 0, y: 0, scale: 1 } as Transform,\n /**\n * Видимая часть холста; `null` — рисуем всё.\n * До первого измерения берём размер окна: иначе первый кадр\n * нарисует дерево целиком и тут же выбросит лишнее.\n */\n visibleRect: (this.virtualize ? estimateVisibleRect() : null) as Rect | null,\n }\n },\n\n inject: {\n // Кладёт BracketRounds. Сетка может стоять и сама по себе — тогда undefined.\n activeRound: { default: null },\n },\n\n computed: {\n layout(): Layout<NodeData> {\n const tree = toTree<NodeData>(this.data, {\n getId: this.getId,\n getChildren: this.getChildren,\n getParentId: this.getParentId,\n })\n\n return layoutTree(applyCollapsed(tree, new Set(this.collapsedIds)), this.options)\n },\n\n /** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\n drawn(): Layout<NodeData> {\n return this.virtualize ? visibleLayout(this.layout, this.visibleRect, this.overscan) : this.layout\n },\n\n /**\n * Высота холста.\n *\n * Обычно — по всей раскладке. В свайпере виден один раунд, и высота берётся\n * по нему: иначе финал с одной ячейкой держит высоту самого населённого\n * раунда и под ним остаётся пустота. Номер раунда кладёт BracketRounds.\n */\n canvasHeight(): number {\n // Vue 2 не выводит типы инъекций — достаём явно.\n const injected = (this as unknown as { activeRound?: { value: number | null } }).activeRound\n const round = injected?.value ?? null\n if (round === null) return this.layout.height\n\n const rounds = this.layout.nodes.reduce((max, node) => Math.max(max, node.depth), 0) + 1\n const direction = resolveOptions(this.options).direction\n const mirrored = direction === 'right-to-left' || direction === 'bottom-to-top'\n const height = roundContentHeight(this.layout.nodes, round, rounds, mirrored)\n\n // Раунда с таким номером в раскладке нет — держим прежнюю высоту,\n // чтобы сетка не прыгнула в ноль.\n return height || this.layout.height\n },\n\n canvasStyle(): Record<string, string> {\n return {\n width: `${this.layout.width}px`,\n height: `${this.canvasHeight}px`,\n transform: toCssTransform(this.transform),\n }\n },\n\n rootStyle(): Record<string, string> | undefined {\n if (this.size !== 'content') return undefined\n\n return {\n width: `${this.layout.width * this.transform.scale}px`,\n height: `${this.canvasHeight * this.transform.scale}px`,\n overflow: 'visible',\n }\n },\n },\n\n watch: {\n // Новые данные — видимую часть надо пересчитать уже по ним.\n layout() {\n this.$nextTick(() => this.updateVisibleRect())\n },\n },\n\n mounted() {\n const controller = new PanZoomController({\n pannable: () => this.pannable,\n zoomable: () => this.zoomable,\n limits: this.scaleLimits ? () => this.scaleLimits : undefined,\n onChange: (transform) => {\n this.transform = transform\n // Холст поехал — видимая часть стала другой.\n this.updateVisibleRect()\n this.$emit('transform', transform)\n },\n })\n\n controller.attach(this.$el as HTMLElement)\n controllers.set(this, controller)\n\n this.updateVisibleRect()\n window.addEventListener('scroll', this.updateVisibleRect, { passive: true, capture: true })\n window.addEventListener('resize', this.updateVisibleRect, { passive: true })\n\n if (this.fitOnMount) this.$nextTick(() => this.fit())\n },\n\n beforeDestroy() {\n controllers.get(this)?.detach()\n controllers.delete(this)\n window.removeEventListener('scroll', this.updateVisibleRect, { capture: true } as EventListenerOptions)\n window.removeEventListener('resize', this.updateVisibleRect)\n },\n\n methods: {\n /** Пересчитывает, какая часть холста сейчас видна на экране. */\n updateVisibleRect(): void {\n this.visibleRect = this.virtualize\n ? measureVisibleRect(this.$refs.canvas as Element | undefined, this.transform.scale)\n : null\n },\n\n /** Вписывает дерево в видимую область. */\n fit(padding?: number): void {\n controllers.get(this)?.fit({ width: this.layout.width, height: this.layout.height }, padding)\n },\n zoomIn(): void {\n controllers.get(this)?.zoomIn()\n },\n zoomOut(): void {\n controllers.get(this)?.zoomOut()\n },\n resetTransform(): void {\n controllers.get(this)?.reset()\n },\n setTransform(transform: Transform): void {\n controllers.get(this)?.set(transform)\n },\n /** Развернуть все свёрнутые ветки. */\n expandAll(): void {\n this.collapsedIds = []\n },\n /** Свернуть или развернуть узел по id. */\n toggle(id: string): void {\n this.collapsedIds = [...toggleCollapsed(new Set(this.collapsedIds), id)]\n },\n\n toggleNode(node: LayoutNode<NodeData>): void {\n this.toggle(node.id)\n this.$emit('toggle', node, this.collapsedIds.includes(node.id))\n },\n\n onNodeClick(node: LayoutNode<NodeData>, event: MouseEvent): void {\n this.$emit('node-click', node, event)\n if (this.collapsible && node.hasChildren) this.toggleNode(node)\n },\n\n nodeStyle(node: LayoutNode<NodeData>): Record<string, string> {\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\n nodeClassOf(node: LayoutNode<NodeData>): string | undefined {\n return typeof this.nodeClass === 'function' ? this.nodeClass(node) : this.nodeClass\n },\n\n /** Подпись карточки по умолчанию: name → title → label → id. */\n labelOf(node: LayoutNode<NodeData>): 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\n /** Своя карточка из scoped-слота или карточка по умолчанию. */\n renderNodeContent(h: typeof Vue.prototype.$createElement, node: LayoutNode<NodeData>): VNode | VNode[] {\n const slot = this.$scopedSlots.node\n\n if (slot) {\n return (slot({\n node,\n data: node.data,\n depth: node.depth,\n collapsed: node.collapsed,\n hasChildren: node.hasChildren,\n toggle: () => this.toggleNode(node),\n }) ?? []) as VNode[]\n }\n\n return h(TreeNodeCard, {\n props: { label: this.labelOf(node), hasChildren: node.hasChildren, collapsed: node.collapsed },\n on: { toggle: () => this.toggleNode(node) },\n })\n },\n },\n\n render(h): VNode {\n const layout = this.layout\n const drawn = this.drawn\n const linkSlot = this.$scopedSlots.link\n\n // data-depth — уровень, из которого линия выходит: по нему её находят\n // снаружи, например когда полоса раундов прячет пройденное.\n const links = drawn.links.map((link) =>\n h('g', { key: link.id, attrs: { 'data-depth': link.target.depth } }, [\n linkSlot\n ? ((linkSlot({ link }) ?? []) as VNode[])\n : h('path', { class: 'tree-view__link', attrs: { d: link.path } }),\n ]),\n )\n\n const nodes = drawn.nodes.map((node) =>\n h(\n 'div',\n {\n key: node.id,\n class: ['tree-view__node', this.nodeClassOf(node)],\n style: this.nodeStyle(node),\n attrs: { 'data-depth': node.depth },\n on: { click: (event: MouseEvent) => this.onNodeClick(node, event) },\n },\n ([] as VNode[]).concat(this.renderNodeContent(h, node)),\n ),\n )\n\n return h(\n 'div',\n {\n class: [\n 'tree-view',\n { 'tree-view--pannable': this.pannable, 'tree-view--content': this.size === 'content' },\n ],\n style: this.rootStyle,\n },\n [\n h('div', { ref: 'canvas', class: 'tree-view__canvas', style: this.canvasStyle }, [\n h(\n 'svg',\n {\n class: 'tree-view__links',\n attrs: { width: layout.width, height: layout.height },\n },\n links as VNode[],\n ),\n ...nodes,\n ]),\n ],\n )\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n} from '@bigplay/tree-view-core'\n\n/**\n * Карточка матча для Vue 2. Разметка и классы один в один как в Vue 3 —\n * строки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketCard',\n props: {\n /** Матч: нужны только команды. */\n match: { type: Object as PropType<BracketCardMatch | null>, default: null },\n /** Сколько строк рисовать. У финала — одна. */\n rows: { type: Number, default: undefined },\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint: { type: String, default: 'Нажми для перехода в матч 👇' },\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel: { type: String, default: 'ТП' },\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint: { type: String, default: 'Техническое поражение' },\n /** У матча есть предыдущие — рисуем кнопку, которая их сворачивает. */\n hasChildren: { type: Boolean, default: false },\n /** Предыдущие матчи сейчас свёрнуты. */\n collapsed: { type: Boolean, default: false },\n /** Подписи кнопки для screen reader. */\n collapseHint: { type: String, default: 'Свернуть предыдущие матчи' },\n expandHint: { type: String, default: 'Показать предыдущие матчи' },\n },\n\n data() {\n return { hoveredKey: null as string | null }\n },\n\n computed: {\n card(): ReturnType<typeof buildBracketCard> {\n return buildBracketCard(this.match, { rows: this.rows, techDefeatLabel: this.techDefeatLabel })\n },\n },\n\n methods: {\n /**\n * Кнопка стоит со стороны предыдущих матчей — туда же уходят линии.\n * data-tv-no-pan: нажатие не должно утаскивать холст.\n */\n renderToggle(h: typeof Vue.prototype.$createElement): VNode {\n const hint = this.collapsed ? this.expandHint : this.collapseHint\n\n return h(\n 'button',\n {\n class: ['tv-bracket__toggle', { 'tv-bracket__toggle--collapsed': this.collapsed }],\n attrs: {\n type: 'button',\n title: hint,\n 'aria-label': hint,\n 'aria-expanded': String(!this.collapsed),\n 'data-tv-no-pan': '',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n )\n },\n\n renderRow(h: typeof Vue.prototype.$createElement, row: BracketCardRow): VNode {\n const content: VNode[] = []\n\n if (row.team) {\n if (row.isTechDefeat && this.hoveredKey === row.key) {\n content.push(h('span', { class: 'tv-bracket__tooltip' }, this.techDefeatHint))\n }\n content.push(h('span', { class: 'tv-bracket__name' }, row.team.name))\n content.push(h('span', { class: 'tv-bracket__score' }, row.score))\n }\n\n return h(\n 'div',\n {\n key: row.key,\n class: [\n 'tv-bracket__row',\n {\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 ],\n attrs: row.isMyTeam ? { title: 'Моя команда' } : {},\n on: {\n click: () => {\n if (row.team) this.$emit('select', row.team, row)\n },\n mouseenter: () => {\n this.hoveredKey = row.key\n },\n mouseleave: () => {\n this.hoveredKey = null\n },\n },\n },\n content,\n )\n },\n },\n\n render(h): VNode {\n const card = this.card\n const children: VNode[] = []\n\n if (card.hasMyTeam && this.myTeamHint) {\n children.push(h('div', { class: 'tv-bracket__hint' }, this.myTeamHint))\n }\n\n if (this.hasChildren) children.push(this.renderToggle(h))\n\n // Строки лежат в своей обёртке: она держит скругление и контур, поэтому\n // матч читается как один блок, а не как список из четырёх команд.\n // Подсказка и кнопка остаются снаружи — их обрезать нельзя.\n children.push(\n h(\n 'div',\n { class: 'tv-bracket__rows' },\n card.rows.map((row) => this.renderRow(h, row)),\n ),\n )\n\n return h('div', { class: ['tv-bracket', { 'tv-bracket--my-team': card.hasMyTeam }] }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n afterScrollEnd,\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketRounds,\n findScrollParent,\n hideRoundsBefore,\n horizontalBox,\n levelGapForWidth,\n nodesInColumn,\n offsetWithin,\n resolveOptions,\n roundAtScroll,\n roundOfDepth,\n roundScrollLeft,\n roundSiblingGap,\n SWIPE_ELBOW_OFFSET,\n smoothScrollLeft,\n watchChildren,\n watchMedia,\n watchWidth,\n type BracketRound,\n type BracketRoundColumn,\n type BracketRoundsModel,\n type TreeNode,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/**\n * Компоненты, у которых сейчас идёт прокрутка по клику на раунд.\n *\n * Не в `data`: реактивность флагу не нужна, а в options-api лишнее поле ломает\n * вывод типов у `this` в методах. Тот же приём, что у контроллеров в TreeView.\n */\nconst programmatic = new WeakSet<Vue>()\n\n/** Раунд, на котором прокрутка остановилась в прошлый раз. */\nconst anchors = new WeakMap<Vue, number>()\n/** Компоненты, у которых мы ждём, когда прокрутка замрёт. */\nconst settling = new WeakSet<Vue>()\n\n/**\n * Полоса раундов для Vue 2. Разметка и классы один в один как в Vue 3 —\n * колонки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketRounds',\n props: {\n /** Раунды по порядку — от первого к финалу. */\n rounds: { type: Array as PropType<BracketRound[]>, default: () => [] },\n /** Настройки сетки — те же, что уходят в `TreeView`. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Показывать полосу. Содержимое слота рисуется в любом случае. */\n isShow: { type: Boolean, default: true },\n /**\n * Базовое расстояние между матчами одного раунда в свайпере.\n * Раунд с несколькими матчами получает кратно больше — до четырёх раз.\n */\n swipeSiblingGap: { type: Number, default: 12 },\n /** Прятать пройденные раунды и линии, которые из них выходят. */\n hidePassed: { type: Boolean, default: true },\n /** Включить свайпер принудительно. Не передан — решает ширина экрана. */\n swipe: { type: Boolean, default: undefined },\n /** Медиавыражение «узкий экран», при котором включается свайпер. */\n mobileQuery: { type: String, default: MOBILE_MEDIA_QUERY },\n },\n\n data() {\n return {\n // Узкий ли экран. До монтирования — нет: на сервере ширину знать неоткуда.\n isNarrow: false,\n // Сколько места отведено полосе: по нему считается ширина раунда в свайпере.\n available: 0,\n // Раунд, на котором стоит прокрутка. Меняется прямо во время свайпа.\n current: 0,\n stop: [] as Array<() => void>,\n }\n },\n\n computed: {\n /** Полосы нет — свайпать нечего: сетку в это время таскают и масштабируют. */\n isSwipe(): boolean {\n return this.isShow && (this.swipe === undefined ? this.isNarrow : this.swipe)\n },\n\n base(): TreeViewOptions {\n return resolveOptions(this.options)\n },\n\n /**\n * Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —\n * число. Если это всё-таки функция, считать колонки не по чему: берём значение\n * по умолчанию, чтобы полоса не развалилась.\n */\n nodeWidth(): number {\n return typeof this.base.nodeWidth === 'number'\n ? this.base.nodeWidth\n : (DEFAULT_OPTIONS.nodeWidth as number)\n },\n\n levelGap(): number {\n return this.isSwipe\n ? levelGapForWidth(this.available, this.nodeWidth)\n : this.base.levelGap\n },\n\n model(): BracketRoundsModel {\n return buildBracketRounds(this.rounds, {\n nodeWidth: this.nodeWidth,\n levelGap: this.levelGap,\n })\n },\n\n /** Дерево турнирной сетки растёт справа налево: первый раунд — самый глубокий. */\n mirrored(): boolean {\n return this.base.direction === 'right-to-left' || this.base.direction === 'bottom-to-top'\n },\n\n /**\n * Настройки для сетки в свайпере.\n *\n * Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по\n * себе: в дереве место второго раунда выводится из первого, поэтому отдельно\n * его отступ не задать.\n *\n * Внутри пары матчи стоят тесно, а между парами промежуток крупнее — иначе\n * весь раунд читается как один список и непонятно, кто с кем сходится дальше.\n * Линии рисуются скобкой: пара сходится в одну вертикаль сразу за карточками,\n * и уже от её середины идёт линия к следующему матчу. В раскладке `'stack'`\n * родитель стоит не по центру детей, поэтому обычная «ступенька» выходила бы\n * из верхнего матча пары, а не из середины.\n */\n gridOptions(): Partial<TreeViewOptions> {\n if (!this.isSwipe) return { ...this.options }\n\n const base = this.swipeSiblingGap\n\n return {\n ...this.options,\n levelGap: this.levelGap,\n levelLayout: 'stack',\n siblingGap: base,\n groupGap: (_node: TreeNode<unknown>, count: number) => roundSiblingGap(count, base),\n linkStyle: 'bracket',\n elbowOffset: this.options?.elbowOffset ?? SWIPE_ELBOW_OFFSET,\n }\n },\n },\n\n watch: {\n current: 'applyHidden',\n isSwipe: 'applyHidden',\n model: 'applyHidden',\n },\n\n mounted() {\n this.stop.push(\n watchMedia(this.mobileQuery, (matches) => {\n this.isNarrow = matches\n }),\n watchWidth((this.$el as HTMLElement).parentElement, (width) => {\n this.available = width\n }),\n // Сетка перерисовывает карточки — скрытое расставляем заново.\n watchChildren(this.$refs.content as HTMLElement, () => this.applyHidden()),\n )\n\n this.applyHidden()\n },\n\n beforeDestroy() {\n this.stop.forEach((off) => off())\n },\n\n provide(): Record<string, unknown> {\n // Vue 2 не умеет реактивный provide — отдаём объект, у которого геттер\n // всегда возвращает свежее значение. TreeView читает его как .value.\n const self = this as unknown as { isSwipe: boolean; current: number }\n\n return {\n activeRound: {\n get value(): number | null {\n return self.isSwipe ? self.current : null\n },\n },\n }\n },\n\n methods: {\n /** Прячет всё, что осталось позади: карточки пройденных раундов и их линии. */\n applyHidden(): void {\n const content = this.$refs.content as HTMLElement | undefined\n if (!content) return\n\n const hideBefore = this.isSwipe && this.hidePassed ? this.current : 0\n const count = this.model.columns.length\n\n hideRoundsBefore(content, hideBefore, (depth) =>\n roundOfDepth(depth, count, this.mirrored),\n )\n },\n\n onScroll(): void {\n const element = this.$refs.scroll as HTMLElement | undefined\n if (!element || !this.isSwipe) return\n\n // Полоса едет за сеткой: она в отдельном слое и своей прокрутки не имеет.\n const head = this.$refs.head as HTMLElement | undefined\n if (head) head.scrollLeft = element.scrollLeft\n\n const width = this.model.columns[0]?.width ?? 0\n let next = roundAtScroll(element.scrollLeft, width, this.model.columns.length)\n\n /*\n * Резкий свайп не должен проскакивать раунды.\n *\n * На это стоит `scroll-snap-stop: always`, но при инерции он спасает не\n * всегда: событий прокрутки за кадр приходит мало, и палец успевает утащить\n * ленту через несколько раундов раньше, чем браузер решит прилипнуть.\n */\n if (!programmatic.has(this) && width > 0) {\n const anchor = anchors.get(this) ?? 0\n const limited = Math.min(Math.max(next, anchor - 1), anchor + 1)\n\n if (limited !== next) {\n next = limited\n element.scrollLeft = next * width\n }\n\n // Один свайп — один раунд: пока прокрутка не замрёт, точка отсчёта\n // не двигается. Ограничивать шаг между событиями бесполезно — при\n // инерции они приходят подряд по одному раунду.\n if (!settling.has(this)) {\n settling.add(this)\n afterScrollEnd(element, () => {\n settling.delete(this)\n anchors.set(this, this.current)\n })\n }\n }\n\n if (next === this.current) return\n\n this.current = next\n this.$emit('round', next)\n },\n\n /**\n * Подводит сетку к матчам раунда — только по горизонтали.\n *\n * Меряем по самой карточке: так раунд встаёт по центру экрана точно, а у финала\n * прокрутка упирается в правый край и он виден целиком. Карточек не нашли\n * (виртуализация) — считаем по колонке, она той же ширины и на том же месте.\n *\n * Вертикаль не трогаем: страница остаётся там, где была.\n */\n scrollToRound(column: BracketRoundColumn): void {\n const element = this.$el as HTMLElement | null\n if (!element) return\n\n // В свайпере прокручивается блок с сеткой, иначе — блок с overflow-x вокруг.\n const container = this.isSwipe\n ? ((this.$refs.scroll as HTMLElement | undefined) ?? null)\n : findScrollParent(element)\n if (!container) return\n\n const content = this.$refs.content as HTMLElement | undefined\n // Карточки раунда стоят друг под другом, поэтому по горизонтали годится любая.\n const card = content\n ? nodesInColumn(content, column.index, column.width, this.model.offset)[0]\n : undefined\n\n const box = card\n ? horizontalBox(card, container)\n : {\n left: offsetWithin(element, container) + column.index * column.width,\n width: column.width,\n }\n\n // Прокрутка по клику едет сразу к нужному раунду — ограничитель «не дальше\n // соседнего» для неё выключаем, иначе из финала в первый раунд не попасть.\n programmatic.add(this)\n smoothScrollLeft(\n container,\n roundScrollLeft({\n ...box,\n viewport: container.clientWidth,\n scrollWidth: container.scrollWidth,\n }),\n )\n afterScrollEnd(container, () => {\n programmatic.delete(this)\n anchors.set(this, this.current)\n })\n },\n\n onSelect(column: BracketRoundColumn): void {\n this.$emit('select', column)\n this.scrollToRound(column)\n },\n\n renderColumn(h: typeof Vue.prototype.$createElement, column: BracketRoundColumn): VNode {\n return h(\n 'div',\n {\n key: column.key,\n class: 'tv-rounds__item',\n style: { width: `${column.width}px` },\n on: { click: () => this.onSelect(column) },\n },\n this.$scopedSlots.round?.({ round: column }) ?? column.name,\n )\n },\n },\n\n render(h): VNode {\n const children: VNode[] = []\n\n // Полоса лежит ВНЕ прокручиваемого блока — иначе её не прилепить к странице:\n // элемент с `overflow-x` становится скролл-контейнером по обеим осям, и\n // `position: sticky` внутри него липнет к нему же, а не к экрану.\n if (this.isShow) {\n children.push(\n h('div', { ref: 'head', class: 'tv-rounds__head' }, [\n h(\n 'div',\n { class: 'tv-rounds__bar', style: { width: `${this.model.width}px` } },\n this.model.columns.map((column) => this.renderColumn(h, column)),\n ),\n ]),\n )\n }\n\n // Сюда кладут саму сетку. `options` — настройки, подогнанные под экран:\n // в свайпере раунд занимает всю ширину, а матчи внутри стоят ближе.\n const content =\n this.$scopedSlots.default?.({\n options: this.gridOptions,\n isSwipe: this.isSwipe,\n round: this.current,\n }) ?? this.$slots.default\n\n const inner: VNode[] = []\n\n // Точки прилипания раньше висели на подписях раундов, а они уехали из\n // прокручиваемого блока. Здесь их заменяют невидимые якоря той же ширины.\n if (this.isSwipe) {\n inner.push(\n h(\n 'div',\n {\n class: 'tv-rounds__snap',\n style: { width: `${this.model.width}px` },\n attrs: { 'aria-hidden': 'true' },\n },\n this.model.columns.map((column) =>\n h('i', {\n key: column.key,\n class: 'tv-rounds__snap-item',\n style: { width: `${column.width}px` },\n }),\n ),\n ),\n )\n }\n\n inner.push(\n h(\n 'div',\n {\n ref: 'content',\n class: 'tv-rounds__content',\n style: this.isShow ? { paddingLeft: `${this.model.offset}px` } : {},\n },\n content,\n ),\n )\n\n children.push(\n h(\n 'div',\n {\n ref: 'scroll',\n class: 'tv-rounds__scroll',\n on: { scroll: () => this.onScroll() },\n },\n inner,\n ),\n )\n\n return h('div', { class: ['tv-rounds', { 'tv-rounds--swipe': this.isSwipe }] }, children)\n },\n})\n","import type { VueConstructor } from 'vue'\nimport TreeView from './TreeView'\nimport TreeNodeCard from './TreeNodeCard'\nimport BracketCard from './BracketCard'\nimport BracketRounds from './BracketRounds'\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, BracketRounds }\n\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n BracketRound,\n BracketRoundColumn,\n BracketRoundsModel,\n BracketRoundsOptions,\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 {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketCard,\n bracketCardHeight,\n buildBracketRounds,\n} from '@bigplay/tree-view-core'\n\n/** Плагин: `Vue.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin = {\n install(Vue: VueConstructor) {\n Vue.component('TreeView', TreeView)\n Vue.component('TreeNodeCard', TreeNodeCard)\n Vue.component('BracketCard', BracketCard)\n Vue.component('BracketRounds', BracketRounds)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["TreeNodeCard","Vue","h","children","event","controllers","TreeView","DEFAULT_OVERSCAN","estimateVisibleRect","tree","toTree","layoutTree","applyCollapsed","visibleLayout","round","rounds","max","node","direction","resolveOptions","mirrored","roundContentHeight","toCssTransform","controller","PanZoomController","transform","measureVisibleRect","padding","id","toggleCollapsed","data","slot","layout","drawn","linkSlot","links","link","nodes","BracketCard","buildBracketCard","hint","row","content","card","programmatic","anchors","settling","BracketRounds","MOBILE_MEDIA_QUERY","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","SWIPE_ELBOW_OFFSET","watchMedia","matches","watchWidth","width","watchChildren","off","self","hideBefore","hideRoundsBefore","depth","roundOfDepth","element","head","next","roundAtScroll","anchor","limited","afterScrollEnd","column","container","findScrollParent","nodesInColumn","box","horizontalBox","offsetWithin","smoothScrollLeft","roundScrollLeft","inner","injectStyles","styles","TreeViewPlugin"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { DEFAULT_OVERSCAN as
|
|
3
|
-
import { DEFAULT_OPTIONS as
|
|
4
|
-
const
|
|
1
|
+
import l from "vue";
|
|
2
|
+
import { DEFAULT_OVERSCAN as f, toggleCollapsed as g, measureVisibleRect as w, PanZoomController as m, toCssTransform as y, resolveOptions as v, roundContentHeight as x, visibleLayout as k, toTree as _, layoutTree as S, applyCollapsed as C, estimateVisibleRect as $, buildBracketCard as T, MOBILE_MEDIA_QUERY as z, findScrollParent as R, nodesInColumn as B, horizontalBox as O, offsetWithin as L, smoothScrollLeft as E, roundScrollLeft as I, afterScrollEnd as u, roundAtScroll as H, hideRoundsBefore as N, roundOfDepth as W, watchMedia as M, watchWidth as j, watchChildren as D, SWIPE_ELBOW_OFFSET as P, roundSiblingGap as V, buildBracketRounds as G, levelGapForWidth as A, DEFAULT_OPTIONS as F, injectStyles as U } from "@bigplay/tree-view-core";
|
|
3
|
+
import { DEFAULT_OPTIONS as it, MOBILE_MEDIA_QUERY as st, bracketCardHeight as ot, buildBracketCard as rt, buildBracketRounds as nt } from "@bigplay/tree-view-core";
|
|
4
|
+
const b = l.extend({
|
|
5
5
|
name: "TreeNodeCard",
|
|
6
6
|
props: {
|
|
7
7
|
label: { type: String, required: !0 },
|
|
@@ -30,7 +30,7 @@ const d = a.extend({
|
|
|
30
30
|
)
|
|
31
31
|
), t("div", { class: "tree-view-card" }, e);
|
|
32
32
|
}
|
|
33
|
-
}),
|
|
33
|
+
}), n = /* @__PURE__ */ new WeakMap(), K = l.extend({
|
|
34
34
|
name: "TreeView",
|
|
35
35
|
props: {
|
|
36
36
|
/** Объект, массив объектов или плоский список (вместе с `getParentId`). */
|
|
@@ -70,7 +70,7 @@ const d = a.extend({
|
|
|
70
70
|
*/
|
|
71
71
|
virtualize: { type: Boolean, default: !0 },
|
|
72
72
|
/** Запас вокруг видимой области в пикселях. */
|
|
73
|
-
overscan: { type: Number, default:
|
|
73
|
+
overscan: { type: Number, default: f }
|
|
74
74
|
},
|
|
75
75
|
data() {
|
|
76
76
|
return {
|
|
@@ -82,34 +82,51 @@ const d = a.extend({
|
|
|
82
82
|
* До первого измерения берём размер окна: иначе первый кадр
|
|
83
83
|
* нарисует дерево целиком и тут же выбросит лишнее.
|
|
84
84
|
*/
|
|
85
|
-
visibleRect: this.virtualize ?
|
|
85
|
+
visibleRect: this.virtualize ? $() : null
|
|
86
86
|
};
|
|
87
87
|
},
|
|
88
|
+
inject: {
|
|
89
|
+
// Кладёт BracketRounds. Сетка может стоять и сама по себе — тогда undefined.
|
|
90
|
+
activeRound: { default: null }
|
|
91
|
+
},
|
|
88
92
|
computed: {
|
|
89
93
|
layout() {
|
|
90
|
-
const t =
|
|
94
|
+
const t = _(this.data, {
|
|
91
95
|
getId: this.getId,
|
|
92
96
|
getChildren: this.getChildren,
|
|
93
97
|
getParentId: this.getParentId
|
|
94
98
|
});
|
|
95
|
-
return
|
|
99
|
+
return S(C(t, new Set(this.collapsedIds)), this.options);
|
|
96
100
|
},
|
|
97
101
|
/** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */
|
|
98
102
|
drawn() {
|
|
99
|
-
return this.virtualize ?
|
|
103
|
+
return this.virtualize ? k(this.layout, this.visibleRect, this.overscan) : this.layout;
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Высота холста.
|
|
107
|
+
*
|
|
108
|
+
* Обычно — по всей раскладке. В свайпере виден один раунд, и высота берётся
|
|
109
|
+
* по нему: иначе финал с одной ячейкой держит высоту самого населённого
|
|
110
|
+
* раунда и под ним остаётся пустота. Номер раунда кладёт BracketRounds.
|
|
111
|
+
*/
|
|
112
|
+
canvasHeight() {
|
|
113
|
+
const e = this.activeRound?.value ?? null;
|
|
114
|
+
if (e === null) return this.layout.height;
|
|
115
|
+
const i = this.layout.nodes.reduce((r, d) => Math.max(r, d.depth), 0) + 1, s = v(this.options).direction, o = s === "right-to-left" || s === "bottom-to-top";
|
|
116
|
+
return x(this.layout.nodes, e, i, o) || this.layout.height;
|
|
100
117
|
},
|
|
101
118
|
canvasStyle() {
|
|
102
119
|
return {
|
|
103
120
|
width: `${this.layout.width}px`,
|
|
104
|
-
height: `${this.
|
|
105
|
-
transform:
|
|
121
|
+
height: `${this.canvasHeight}px`,
|
|
122
|
+
transform: y(this.transform)
|
|
106
123
|
};
|
|
107
124
|
},
|
|
108
125
|
rootStyle() {
|
|
109
126
|
if (this.size === "content")
|
|
110
127
|
return {
|
|
111
128
|
width: `${this.layout.width * this.transform.scale}px`,
|
|
112
|
-
height: `${this.
|
|
129
|
+
height: `${this.canvasHeight * this.transform.scale}px`,
|
|
113
130
|
overflow: "visible"
|
|
114
131
|
};
|
|
115
132
|
}
|
|
@@ -121,7 +138,7 @@ const d = a.extend({
|
|
|
121
138
|
}
|
|
122
139
|
},
|
|
123
140
|
mounted() {
|
|
124
|
-
const t = new
|
|
141
|
+
const t = new m({
|
|
125
142
|
pannable: () => this.pannable,
|
|
126
143
|
zoomable: () => this.zoomable,
|
|
127
144
|
limits: this.scaleLimits ? () => this.scaleLimits : void 0,
|
|
@@ -129,31 +146,31 @@ const d = a.extend({
|
|
|
129
146
|
this.transform = e, this.updateVisibleRect(), this.$emit("transform", e);
|
|
130
147
|
}
|
|
131
148
|
});
|
|
132
|
-
t.attach(this.$el),
|
|
149
|
+
t.attach(this.$el), n.set(this, t), this.updateVisibleRect(), window.addEventListener("scroll", this.updateVisibleRect, { passive: !0, capture: !0 }), window.addEventListener("resize", this.updateVisibleRect, { passive: !0 }), this.fitOnMount && this.$nextTick(() => this.fit());
|
|
133
150
|
},
|
|
134
151
|
beforeDestroy() {
|
|
135
|
-
|
|
152
|
+
n.get(this)?.detach(), n.delete(this), window.removeEventListener("scroll", this.updateVisibleRect, { capture: !0 }), window.removeEventListener("resize", this.updateVisibleRect);
|
|
136
153
|
},
|
|
137
154
|
methods: {
|
|
138
155
|
/** Пересчитывает, какая часть холста сейчас видна на экране. */
|
|
139
156
|
updateVisibleRect() {
|
|
140
|
-
this.visibleRect = this.virtualize ?
|
|
157
|
+
this.visibleRect = this.virtualize ? w(this.$refs.canvas, this.transform.scale) : null;
|
|
141
158
|
},
|
|
142
159
|
/** Вписывает дерево в видимую область. */
|
|
143
160
|
fit(t) {
|
|
144
|
-
|
|
161
|
+
n.get(this)?.fit({ width: this.layout.width, height: this.layout.height }, t);
|
|
145
162
|
},
|
|
146
163
|
zoomIn() {
|
|
147
|
-
|
|
164
|
+
n.get(this)?.zoomIn();
|
|
148
165
|
},
|
|
149
166
|
zoomOut() {
|
|
150
|
-
|
|
167
|
+
n.get(this)?.zoomOut();
|
|
151
168
|
},
|
|
152
169
|
resetTransform() {
|
|
153
|
-
|
|
170
|
+
n.get(this)?.reset();
|
|
154
171
|
},
|
|
155
172
|
setTransform(t) {
|
|
156
|
-
|
|
173
|
+
n.get(this)?.set(t);
|
|
157
174
|
},
|
|
158
175
|
/** Развернуть все свёрнутые ветки. */
|
|
159
176
|
expandAll() {
|
|
@@ -161,7 +178,7 @@ const d = a.extend({
|
|
|
161
178
|
},
|
|
162
179
|
/** Свернуть или развернуть узел по id. */
|
|
163
180
|
toggle(t) {
|
|
164
|
-
this.collapsedIds = [...
|
|
181
|
+
this.collapsedIds = [...g(new Set(this.collapsedIds), t)];
|
|
165
182
|
},
|
|
166
183
|
toggleNode(t) {
|
|
167
184
|
this.toggle(t.id), this.$emit("toggle", t, this.collapsedIds.includes(t.id));
|
|
@@ -195,28 +212,28 @@ const d = a.extend({
|
|
|
195
212
|
collapsed: e.collapsed,
|
|
196
213
|
hasChildren: e.hasChildren,
|
|
197
214
|
toggle: () => this.toggleNode(e)
|
|
198
|
-
}) ?? [] : t(
|
|
215
|
+
}) ?? [] : t(b, {
|
|
199
216
|
props: { label: this.labelOf(e), hasChildren: e.hasChildren, collapsed: e.collapsed },
|
|
200
217
|
on: { toggle: () => this.toggleNode(e) }
|
|
201
218
|
});
|
|
202
219
|
}
|
|
203
220
|
},
|
|
204
221
|
render(t) {
|
|
205
|
-
const e = this.layout, i = this.drawn, s = this.$scopedSlots.link,
|
|
206
|
-
(
|
|
207
|
-
s ? s({ link:
|
|
222
|
+
const e = this.layout, i = this.drawn, s = this.$scopedSlots.link, o = i.links.map(
|
|
223
|
+
(r) => t("g", { key: r.id, attrs: { "data-depth": r.target.depth } }, [
|
|
224
|
+
s ? s({ link: r }) ?? [] : t("path", { class: "tree-view__link", attrs: { d: r.path } })
|
|
208
225
|
])
|
|
209
|
-
),
|
|
210
|
-
(
|
|
226
|
+
), a = i.nodes.map(
|
|
227
|
+
(r) => t(
|
|
211
228
|
"div",
|
|
212
229
|
{
|
|
213
|
-
key:
|
|
214
|
-
class: ["tree-view__node", this.nodeClassOf(
|
|
215
|
-
style: this.nodeStyle(
|
|
216
|
-
attrs: { "data-depth":
|
|
217
|
-
on: { click: (
|
|
230
|
+
key: r.id,
|
|
231
|
+
class: ["tree-view__node", this.nodeClassOf(r)],
|
|
232
|
+
style: this.nodeStyle(r),
|
|
233
|
+
attrs: { "data-depth": r.depth },
|
|
234
|
+
on: { click: (d) => this.onNodeClick(r, d) }
|
|
218
235
|
},
|
|
219
|
-
[].concat(this.renderNodeContent(t,
|
|
236
|
+
[].concat(this.renderNodeContent(t, r))
|
|
220
237
|
)
|
|
221
238
|
);
|
|
222
239
|
return t(
|
|
@@ -236,14 +253,14 @@ const d = a.extend({
|
|
|
236
253
|
class: "tree-view__links",
|
|
237
254
|
attrs: { width: e.width, height: e.height }
|
|
238
255
|
},
|
|
239
|
-
|
|
256
|
+
o
|
|
240
257
|
),
|
|
241
|
-
...
|
|
258
|
+
...a
|
|
242
259
|
])
|
|
243
260
|
]
|
|
244
261
|
);
|
|
245
262
|
}
|
|
246
|
-
}),
|
|
263
|
+
}), Q = l.extend({
|
|
247
264
|
name: "BracketCard",
|
|
248
265
|
props: {
|
|
249
266
|
/** Матч: нужны только команды. */
|
|
@@ -255,17 +272,50 @@ const d = a.extend({
|
|
|
255
272
|
/** Текст в ячейке счёта при техническом поражении. */
|
|
256
273
|
techDefeatLabel: { type: String, default: "ТП" },
|
|
257
274
|
/** Подсказка по наведению на техническое поражение. */
|
|
258
|
-
techDefeatHint: { type: String, default: "Техническое поражение" }
|
|
275
|
+
techDefeatHint: { type: String, default: "Техническое поражение" },
|
|
276
|
+
/** У матча есть предыдущие — рисуем кнопку, которая их сворачивает. */
|
|
277
|
+
hasChildren: { type: Boolean, default: !1 },
|
|
278
|
+
/** Предыдущие матчи сейчас свёрнуты. */
|
|
279
|
+
collapsed: { type: Boolean, default: !1 },
|
|
280
|
+
/** Подписи кнопки для screen reader. */
|
|
281
|
+
collapseHint: { type: String, default: "Свернуть предыдущие матчи" },
|
|
282
|
+
expandHint: { type: String, default: "Показать предыдущие матчи" }
|
|
259
283
|
},
|
|
260
284
|
data() {
|
|
261
285
|
return { hoveredKey: null };
|
|
262
286
|
},
|
|
263
287
|
computed: {
|
|
264
288
|
card() {
|
|
265
|
-
return
|
|
289
|
+
return T(this.match, { rows: this.rows, techDefeatLabel: this.techDefeatLabel });
|
|
266
290
|
}
|
|
267
291
|
},
|
|
268
292
|
methods: {
|
|
293
|
+
/**
|
|
294
|
+
* Кнопка стоит со стороны предыдущих матчей — туда же уходят линии.
|
|
295
|
+
* data-tv-no-pan: нажатие не должно утаскивать холст.
|
|
296
|
+
*/
|
|
297
|
+
renderToggle(t) {
|
|
298
|
+
const e = this.collapsed ? this.expandHint : this.collapseHint;
|
|
299
|
+
return t(
|
|
300
|
+
"button",
|
|
301
|
+
{
|
|
302
|
+
class: ["tv-bracket__toggle", { "tv-bracket__toggle--collapsed": this.collapsed }],
|
|
303
|
+
attrs: {
|
|
304
|
+
type: "button",
|
|
305
|
+
title: e,
|
|
306
|
+
"aria-label": e,
|
|
307
|
+
"aria-expanded": String(!this.collapsed),
|
|
308
|
+
"data-tv-no-pan": ""
|
|
309
|
+
},
|
|
310
|
+
on: {
|
|
311
|
+
click: (i) => {
|
|
312
|
+
i.stopPropagation(), this.$emit("toggle");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
this.collapsed ? "+" : "−"
|
|
317
|
+
);
|
|
318
|
+
},
|
|
269
319
|
renderRow(t, e) {
|
|
270
320
|
const i = [];
|
|
271
321
|
return e.team && (e.isTechDefeat && this.hoveredKey === e.key && i.push(t("span", { class: "tv-bracket__tooltip" }, this.techDefeatHint)), i.push(t("span", { class: "tv-bracket__name" }, e.team.name)), i.push(t("span", { class: "tv-bracket__score" }, e.score))), t(
|
|
@@ -300,11 +350,15 @@ const d = a.extend({
|
|
|
300
350
|
},
|
|
301
351
|
render(t) {
|
|
302
352
|
const e = this.card, i = [];
|
|
303
|
-
e.hasMyTeam && this.myTeamHint && i.push(t("div", { class: "tv-bracket__hint" }, this.myTeamHint))
|
|
304
|
-
|
|
305
|
-
|
|
353
|
+
return e.hasMyTeam && this.myTeamHint && i.push(t("div", { class: "tv-bracket__hint" }, this.myTeamHint)), this.hasChildren && i.push(this.renderToggle(t)), i.push(
|
|
354
|
+
t(
|
|
355
|
+
"div",
|
|
356
|
+
{ class: "tv-bracket__rows" },
|
|
357
|
+
e.rows.map((s) => this.renderRow(t, s))
|
|
358
|
+
)
|
|
359
|
+
), t("div", { class: ["tv-bracket", { "tv-bracket--my-team": e.hasMyTeam }] }, i);
|
|
306
360
|
}
|
|
307
|
-
}),
|
|
361
|
+
}), c = /* @__PURE__ */ new WeakSet(), h = /* @__PURE__ */ new WeakMap(), p = /* @__PURE__ */ new WeakSet(), Y = l.extend({
|
|
308
362
|
name: "BracketRounds",
|
|
309
363
|
props: {
|
|
310
364
|
/** Раунды по порядку — от первого к финалу. */
|
|
@@ -323,7 +377,7 @@ const d = a.extend({
|
|
|
323
377
|
/** Включить свайпер принудительно. Не передан — решает ширина экрана. */
|
|
324
378
|
swipe: { type: Boolean, default: void 0 },
|
|
325
379
|
/** Медиавыражение «узкий экран», при котором включается свайпер. */
|
|
326
|
-
mobileQuery: { type: String, default:
|
|
380
|
+
mobileQuery: { type: String, default: z }
|
|
327
381
|
},
|
|
328
382
|
data() {
|
|
329
383
|
return {
|
|
@@ -342,7 +396,7 @@ const d = a.extend({
|
|
|
342
396
|
return this.isShow && (this.swipe === void 0 ? this.isNarrow : this.swipe);
|
|
343
397
|
},
|
|
344
398
|
base() {
|
|
345
|
-
return
|
|
399
|
+
return v(this.options);
|
|
346
400
|
},
|
|
347
401
|
/**
|
|
348
402
|
* Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —
|
|
@@ -350,13 +404,13 @@ const d = a.extend({
|
|
|
350
404
|
* по умолчанию, чтобы полоса не развалилась.
|
|
351
405
|
*/
|
|
352
406
|
nodeWidth() {
|
|
353
|
-
return typeof this.base.nodeWidth == "number" ? this.base.nodeWidth :
|
|
407
|
+
return typeof this.base.nodeWidth == "number" ? this.base.nodeWidth : F.nodeWidth;
|
|
354
408
|
},
|
|
355
409
|
levelGap() {
|
|
356
|
-
return this.isSwipe ?
|
|
410
|
+
return this.isSwipe ? A(this.available, this.nodeWidth) : this.base.levelGap;
|
|
357
411
|
},
|
|
358
412
|
model() {
|
|
359
|
-
return
|
|
413
|
+
return G(this.rounds, {
|
|
360
414
|
nodeWidth: this.nodeWidth,
|
|
361
415
|
levelGap: this.levelGap
|
|
362
416
|
});
|
|
@@ -370,7 +424,14 @@ const d = a.extend({
|
|
|
370
424
|
*
|
|
371
425
|
* Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по
|
|
372
426
|
* себе: в дереве место второго раунда выводится из первого, поэтому отдельно
|
|
373
|
-
* его отступ не задать.
|
|
427
|
+
* его отступ не задать.
|
|
428
|
+
*
|
|
429
|
+
* Внутри пары матчи стоят тесно, а между парами промежуток крупнее — иначе
|
|
430
|
+
* весь раунд читается как один список и непонятно, кто с кем сходится дальше.
|
|
431
|
+
* Линии рисуются скобкой: пара сходится в одну вертикаль сразу за карточками,
|
|
432
|
+
* и уже от её середины идёт линия к следующему матчу. В раскладке `'stack'`
|
|
433
|
+
* родитель стоит не по центру детей, поэтому обычная «ступенька» выходила бы
|
|
434
|
+
* из верхнего матча пары, а не из середины.
|
|
374
435
|
*/
|
|
375
436
|
gridOptions() {
|
|
376
437
|
if (!this.isSwipe) return { ...this.options };
|
|
@@ -379,7 +440,10 @@ const d = a.extend({
|
|
|
379
440
|
...this.options,
|
|
380
441
|
levelGap: this.levelGap,
|
|
381
442
|
levelLayout: "stack",
|
|
382
|
-
siblingGap:
|
|
443
|
+
siblingGap: t,
|
|
444
|
+
groupGap: (e, i) => V(i, t),
|
|
445
|
+
linkStyle: "bracket",
|
|
446
|
+
elbowOffset: this.options?.elbowOffset ?? P
|
|
383
447
|
};
|
|
384
448
|
}
|
|
385
449
|
},
|
|
@@ -390,36 +454,55 @@ const d = a.extend({
|
|
|
390
454
|
},
|
|
391
455
|
mounted() {
|
|
392
456
|
this.stop.push(
|
|
393
|
-
|
|
457
|
+
M(this.mobileQuery, (t) => {
|
|
394
458
|
this.isNarrow = t;
|
|
395
459
|
}),
|
|
396
|
-
|
|
460
|
+
j(this.$el.parentElement, (t) => {
|
|
397
461
|
this.available = t;
|
|
398
462
|
}),
|
|
399
463
|
// Сетка перерисовывает карточки — скрытое расставляем заново.
|
|
400
|
-
|
|
464
|
+
D(this.$refs.content, () => this.applyHidden())
|
|
401
465
|
), this.applyHidden();
|
|
402
466
|
},
|
|
403
467
|
beforeDestroy() {
|
|
404
468
|
this.stop.forEach((t) => t());
|
|
405
469
|
},
|
|
470
|
+
provide() {
|
|
471
|
+
const t = this;
|
|
472
|
+
return {
|
|
473
|
+
activeRound: {
|
|
474
|
+
get value() {
|
|
475
|
+
return t.isSwipe ? t.current : null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
},
|
|
406
480
|
methods: {
|
|
407
481
|
/** Прячет всё, что осталось позади: карточки пройденных раундов и их линии. */
|
|
408
482
|
applyHidden() {
|
|
409
483
|
const t = this.$refs.content;
|
|
410
484
|
if (!t) return;
|
|
411
485
|
const e = this.isSwipe && this.hidePassed ? this.current : 0, i = this.model.columns.length;
|
|
412
|
-
|
|
486
|
+
N(
|
|
413
487
|
t,
|
|
414
488
|
e,
|
|
415
|
-
(s) =>
|
|
489
|
+
(s) => W(s, i, this.mirrored)
|
|
416
490
|
);
|
|
417
491
|
},
|
|
418
492
|
onScroll() {
|
|
419
|
-
const t = this.$
|
|
493
|
+
const t = this.$refs.scroll;
|
|
420
494
|
if (!t || !this.isSwipe) return;
|
|
421
|
-
const e = this.
|
|
422
|
-
|
|
495
|
+
const e = this.$refs.head;
|
|
496
|
+
e && (e.scrollLeft = t.scrollLeft);
|
|
497
|
+
const i = this.model.columns[0]?.width ?? 0;
|
|
498
|
+
let s = H(t.scrollLeft, i, this.model.columns.length);
|
|
499
|
+
if (!c.has(this) && i > 0) {
|
|
500
|
+
const o = h.get(this) ?? 0, a = Math.min(Math.max(s, o - 1), o + 1);
|
|
501
|
+
a !== s && (s = a, t.scrollLeft = s * i), p.has(this) || (p.add(this), u(t, () => {
|
|
502
|
+
p.delete(this), h.set(this, this.current);
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
505
|
+
s !== this.current && (this.current = s, this.$emit("round", s));
|
|
423
506
|
},
|
|
424
507
|
/**
|
|
425
508
|
* Подводит сетку к матчам раунда — только по горизонтали.
|
|
@@ -433,20 +516,22 @@ const d = a.extend({
|
|
|
433
516
|
scrollToRound(t) {
|
|
434
517
|
const e = this.$el;
|
|
435
518
|
if (!e) return;
|
|
436
|
-
const i = this.isSwipe ?
|
|
519
|
+
const i = this.isSwipe ? this.$refs.scroll ?? null : R(e);
|
|
437
520
|
if (!i) return;
|
|
438
|
-
const s = this.$refs.content,
|
|
439
|
-
left:
|
|
521
|
+
const s = this.$refs.content, o = s ? B(s, t.index, t.width, this.model.offset)[0] : void 0, a = o ? O(o, i) : {
|
|
522
|
+
left: L(e, i) + t.index * t.width,
|
|
440
523
|
width: t.width
|
|
441
524
|
};
|
|
442
|
-
|
|
525
|
+
c.add(this), E(
|
|
443
526
|
i,
|
|
444
|
-
|
|
445
|
-
...
|
|
527
|
+
I({
|
|
528
|
+
...a,
|
|
446
529
|
viewport: i.clientWidth,
|
|
447
530
|
scrollWidth: i.scrollWidth
|
|
448
531
|
})
|
|
449
|
-
)
|
|
532
|
+
), u(i, () => {
|
|
533
|
+
c.delete(this), h.set(this, this.current);
|
|
534
|
+
});
|
|
450
535
|
},
|
|
451
536
|
onSelect(t) {
|
|
452
537
|
this.$emit("select", t), this.scrollToRound(t);
|
|
@@ -467,18 +552,36 @@ const d = a.extend({
|
|
|
467
552
|
render(t) {
|
|
468
553
|
const e = [];
|
|
469
554
|
this.isShow && e.push(
|
|
470
|
-
t(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
555
|
+
t("div", { ref: "head", class: "tv-rounds__head" }, [
|
|
556
|
+
t(
|
|
557
|
+
"div",
|
|
558
|
+
{ class: "tv-rounds__bar", style: { width: `${this.model.width}px` } },
|
|
559
|
+
this.model.columns.map((o) => this.renderColumn(t, o))
|
|
560
|
+
)
|
|
561
|
+
])
|
|
475
562
|
);
|
|
476
563
|
const i = this.$scopedSlots.default?.({
|
|
477
564
|
options: this.gridOptions,
|
|
478
565
|
isSwipe: this.isSwipe,
|
|
479
566
|
round: this.current
|
|
480
|
-
}) ?? this.$slots.default;
|
|
481
|
-
return
|
|
567
|
+
}) ?? this.$slots.default, s = [];
|
|
568
|
+
return this.isSwipe && s.push(
|
|
569
|
+
t(
|
|
570
|
+
"div",
|
|
571
|
+
{
|
|
572
|
+
class: "tv-rounds__snap",
|
|
573
|
+
style: { width: `${this.model.width}px` },
|
|
574
|
+
attrs: { "aria-hidden": "true" }
|
|
575
|
+
},
|
|
576
|
+
this.model.columns.map(
|
|
577
|
+
(o) => t("i", {
|
|
578
|
+
key: o.key,
|
|
579
|
+
class: "tv-rounds__snap-item",
|
|
580
|
+
style: { width: `${o.width}px` }
|
|
581
|
+
})
|
|
582
|
+
)
|
|
583
|
+
)
|
|
584
|
+
), s.push(
|
|
482
585
|
t(
|
|
483
586
|
"div",
|
|
484
587
|
{
|
|
@@ -488,33 +591,36 @@ const d = a.extend({
|
|
|
488
591
|
},
|
|
489
592
|
i
|
|
490
593
|
)
|
|
491
|
-
),
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
594
|
+
), e.push(
|
|
595
|
+
t(
|
|
596
|
+
"div",
|
|
597
|
+
{
|
|
598
|
+
ref: "scroll",
|
|
599
|
+
class: "tv-rounds__scroll",
|
|
600
|
+
on: { scroll: () => this.onScroll() }
|
|
601
|
+
},
|
|
602
|
+
s
|
|
603
|
+
)
|
|
604
|
+
), t("div", { class: ["tv-rounds", { "tv-rounds--swipe": this.isSwipe }] }, e);
|
|
499
605
|
}
|
|
500
|
-
}),
|
|
501
|
-
|
|
502
|
-
const
|
|
606
|
+
}), 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}.tree-view--pannable{cursor:grab;touch-action:none}.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;--tv-bracket-shadow: 0 0 0 1px rgb(15 23 42 / 8%), 0 1px 3px rgb(15 23 42 / 10%);position:relative;box-sizing:border-box;display:flex;flex-direction:column;width:100%;height:100%;font-size:12px}.tv-bracket__rows{box-sizing:border-box;display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden;border-radius:var(--tv-bracket-radius);box-shadow:var(--tv-bracket-shadow)}.tv-bracket--my-team .tv-bracket__rows{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__toggle{position:absolute;top:50%;left:-11px;z-index:1;display:flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;font:inherit;font-size:13px;font-weight:700;line-height:1;color:var(--tv-bracket-winner-bg);cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:50%;box-shadow:0 1px 3px #0f172a2e;transform:translateY(-50%)}.tv-bracket__toggle:hover{border-color:var(--tv-bracket-winner-bg)}.tv-bracket__toggle--collapsed{color:var(--tv-bracket-winner-color);background:var(--tv-bracket-winner-bg);border-color:var(--tv-bracket-winner-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}.tv-rounds{--tv-rounds-bg: #f5f5f7;--tv-rounds-color: #9ca3af;--tv-rounds-border: 2px solid #e5e7eb;--tv-rounds-gap: 20px;--tv-rounds-fade: .22s;--tv-rounds-collapse-scale: .86;--tv-rounds-collapse-ease: cubic-bezier(.4, 0, .2, 1);--tv-rounds-link-color: #94a3b8;--tv-rounds-link-width: 3px;display:flex;flex-direction:column;width:max-content;min-width:100%}.tv-rounds__head{position:sticky;top:0;z-index:2;overflow:hidden;margin-bottom:var(--tv-rounds-gap);background:var(--tv-rounds-bg);border-bottom:var(--tv-rounds-border)}.tv-rounds__bar{display:flex;min-width:100%}.tv-rounds__scroll{position:relative}.tv-rounds__snap{display:flex;height:1px;margin-bottom:-1px}.tv-rounds__snap-item{flex:none;display:block}.tv-rounds__item{flex:none;box-sizing:border-box;padding:10px 20px;font:inherit;font-size:14px;font-weight:600;color:var(--tv-rounds-color);text-align:center;text-transform:uppercase;white-space:nowrap;background:none;border:none;border-right:var(--tv-rounds-border)}.tv-rounds--swipe .tv-bracket__hint{display:none}.tv-rounds--swipe .tree-view{--tv-link-color: var(--tv-rounds-link-color);--tv-link-width: var(--tv-rounds-link-width)}.tv-rounds--swipe{width:100%;min-width:0}.tv-rounds--swipe .tv-rounds__scroll{overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:none}.tv-rounds--swipe .tv-rounds__scroll::-webkit-scrollbar{display:none}.tv-rounds--swipe .tv-rounds__snap-item{scroll-snap-align:start;scroll-snap-stop:always}.tv-rounds--swipe [data-depth]{transition:none}.tv-rounds--swipe [data-depth].tv-hidden{visibility:hidden;opacity:0;transition:opacity var(--tv-rounds-fade, .22s) ease,transform var(--tv-rounds-fade, .22s) var(--tv-rounds-collapse-ease),visibility 0s linear var(--tv-rounds-fade, .22s)}.tv-rounds--swipe .tree-view__node.tv-hidden{transform:scale(var(--tv-rounds-collapse-scale));transform-origin:right center}.tv-hidden{visibility:hidden}@media(prefers-reduced-motion:reduce){.tv-rounds--swipe [data-depth],.tv-rounds--swipe [data-depth].tv-hidden{transition:none}.tv-rounds--swipe .tree-view__node.tv-hidden{transform:none}}";
|
|
607
|
+
U(q);
|
|
608
|
+
const X = {
|
|
503
609
|
install(t) {
|
|
504
|
-
t.component("TreeView",
|
|
610
|
+
t.component("TreeView", K), t.component("TreeNodeCard", b), t.component("BracketCard", Q), t.component("BracketRounds", Y);
|
|
505
611
|
}
|
|
506
612
|
};
|
|
507
613
|
export {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
614
|
+
Q as BracketCard,
|
|
615
|
+
Y as BracketRounds,
|
|
616
|
+
it as DEFAULT_OPTIONS,
|
|
617
|
+
st as MOBILE_MEDIA_QUERY,
|
|
618
|
+
b as TreeNodeCard,
|
|
619
|
+
K as TreeView,
|
|
620
|
+
X as TreeViewPlugin,
|
|
621
|
+
ot as bracketCardHeight,
|
|
622
|
+
rt as buildBracketCard,
|
|
623
|
+
nt as buildBracketRounds,
|
|
624
|
+
X as default
|
|
519
625
|
};
|
|
520
626
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/TreeNodeCard.ts","../src/TreeView.ts","../src/BracketCard.ts","../src/BracketRounds.ts","../src/index.ts"],"sourcesContent":["import Vue from 'vue'\n\n/**\n * Карточка узла по умолчанию для Vue 2.\n * Разметка и классы один в один как в Vue 3 — стили общие.\n */\nexport default Vue.extend({\n name: 'TreeNodeCard',\n props: {\n label: { type: String, required: true },\n hasChildren: { type: Boolean, default: false },\n collapsed: { type: Boolean, default: false },\n },\n render(h) {\n const children = [h('span', { class: 'tree-view-card__label' }, this.label)]\n\n if (this.hasChildren) {\n children.push(\n h(\n 'button',\n {\n class: 'tree-view-card__toggle',\n attrs: {\n type: 'button',\n 'data-tv-no-pan': '',\n 'aria-label': this.collapsed ? 'Развернуть' : 'Свернуть',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n ),\n )\n }\n\n return h('div', { class: 'tree-view-card' }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n applyCollapsed,\n DEFAULT_OVERSCAN,\n estimateVisibleRect,\n layoutTree,\n measureVisibleRect,\n PanZoomController,\n toCssTransform,\n toggleCollapsed,\n toTree,\n visibleLayout,\n type Accessors,\n type Layout,\n type LayoutNode,\n type Rect,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard'\n\n/** Данные узла могут быть какими угодно — библиотека читает их только аксессорами. */\ntype NodeData = any\n\n/**\n * Контроллеры держим здесь, а не в data(): Vue 2 сделал бы объект реактивным,\n * а внутри него DOM-элемент и Map — наблюдать за ними незачем.\n */\nconst controllers = new WeakMap<Vue, PanZoomController>()\n\nexport default Vue.extend({\n name: 'TreeView',\n props: {\n /** Объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: { type: [Object, Array] as PropType<NodeData>, default: null },\n /** Размеры и направление. Можно передать только часть полей. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId: { type: Function as PropType<Accessors<NodeData>['getId']>, default: undefined },\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren: { type: Function as PropType<Accessors<NodeData>['getChildren']>, default: undefined },\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId: { type: Function as PropType<Accessors<NodeData>['getParentId']>, default: undefined },\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible: { type: Boolean, default: false },\n /** Разрешить перетаскивание холста. */\n pannable: { type: Boolean, default: true },\n /** Разрешить зум колесом и «щипком». */\n zoomable: { type: Boolean, default: true },\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount: { type: Boolean, default: true },\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n */\n size: { type: String as PropType<'fill' | 'content'>, default: 'fill' },\n /** Пределы масштаба. */\n scaleLimits: { type: Object as PropType<ScaleLimits>, default: undefined },\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass: {\n type: [String, Function] as PropType<string | ((node: LayoutNode<NodeData>) => string)>,\n default: undefined,\n },\n /**\n * Рисовать только то, что видно на экране. На больших сетках в DOM\n * попадает несколько десятков карточек вместо тысячи.\n */\n virtualize: { type: Boolean, default: true },\n /** Запас вокруг видимой области в пикселях. */\n overscan: { type: Number, default: DEFAULT_OVERSCAN },\n },\n\n data() {\n return {\n /** Массив, а не Set: Vue 2 не умеет наблюдать за Set. */\n collapsedIds: [] as string[],\n transform: { x: 0, y: 0, scale: 1 } as Transform,\n /**\n * Видимая часть холста; `null` — рисуем всё.\n * До первого измерения берём размер окна: иначе первый кадр\n * нарисует дерево целиком и тут же выбросит лишнее.\n */\n visibleRect: (this.virtualize ? estimateVisibleRect() : null) as Rect | null,\n }\n },\n\n computed: {\n layout(): Layout<NodeData> {\n const tree = toTree<NodeData>(this.data, {\n getId: this.getId,\n getChildren: this.getChildren,\n getParentId: this.getParentId,\n })\n\n return layoutTree(applyCollapsed(tree, new Set(this.collapsedIds)), this.options)\n },\n\n /** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\n drawn(): Layout<NodeData> {\n return this.virtualize ? visibleLayout(this.layout, this.visibleRect, this.overscan) : this.layout\n },\n\n canvasStyle(): Record<string, string> {\n return {\n width: `${this.layout.width}px`,\n height: `${this.layout.height}px`,\n transform: toCssTransform(this.transform),\n }\n },\n\n rootStyle(): Record<string, string> | undefined {\n if (this.size !== 'content') return undefined\n\n return {\n width: `${this.layout.width * this.transform.scale}px`,\n height: `${this.layout.height * this.transform.scale}px`,\n overflow: 'visible',\n }\n },\n },\n\n watch: {\n // Новые данные — видимую часть надо пересчитать уже по ним.\n layout() {\n this.$nextTick(() => this.updateVisibleRect())\n },\n },\n\n mounted() {\n const controller = new PanZoomController({\n pannable: () => this.pannable,\n zoomable: () => this.zoomable,\n limits: this.scaleLimits ? () => this.scaleLimits : undefined,\n onChange: (transform) => {\n this.transform = transform\n // Холст поехал — видимая часть стала другой.\n this.updateVisibleRect()\n this.$emit('transform', transform)\n },\n })\n\n controller.attach(this.$el as HTMLElement)\n controllers.set(this, controller)\n\n this.updateVisibleRect()\n window.addEventListener('scroll', this.updateVisibleRect, { passive: true, capture: true })\n window.addEventListener('resize', this.updateVisibleRect, { passive: true })\n\n if (this.fitOnMount) this.$nextTick(() => this.fit())\n },\n\n beforeDestroy() {\n controllers.get(this)?.detach()\n controllers.delete(this)\n window.removeEventListener('scroll', this.updateVisibleRect, { capture: true } as EventListenerOptions)\n window.removeEventListener('resize', this.updateVisibleRect)\n },\n\n methods: {\n /** Пересчитывает, какая часть холста сейчас видна на экране. */\n updateVisibleRect(): void {\n this.visibleRect = this.virtualize\n ? measureVisibleRect(this.$refs.canvas as Element | undefined, this.transform.scale)\n : null\n },\n\n /** Вписывает дерево в видимую область. */\n fit(padding?: number): void {\n controllers.get(this)?.fit({ width: this.layout.width, height: this.layout.height }, padding)\n },\n zoomIn(): void {\n controllers.get(this)?.zoomIn()\n },\n zoomOut(): void {\n controllers.get(this)?.zoomOut()\n },\n resetTransform(): void {\n controllers.get(this)?.reset()\n },\n setTransform(transform: Transform): void {\n controllers.get(this)?.set(transform)\n },\n /** Развернуть все свёрнутые ветки. */\n expandAll(): void {\n this.collapsedIds = []\n },\n /** Свернуть или развернуть узел по id. */\n toggle(id: string): void {\n this.collapsedIds = [...toggleCollapsed(new Set(this.collapsedIds), id)]\n },\n\n toggleNode(node: LayoutNode<NodeData>): void {\n this.toggle(node.id)\n this.$emit('toggle', node, this.collapsedIds.includes(node.id))\n },\n\n onNodeClick(node: LayoutNode<NodeData>, event: MouseEvent): void {\n this.$emit('node-click', node, event)\n if (this.collapsible && node.hasChildren) this.toggleNode(node)\n },\n\n nodeStyle(node: LayoutNode<NodeData>): Record<string, string> {\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\n nodeClassOf(node: LayoutNode<NodeData>): string | undefined {\n return typeof this.nodeClass === 'function' ? this.nodeClass(node) : this.nodeClass\n },\n\n /** Подпись карточки по умолчанию: name → title → label → id. */\n labelOf(node: LayoutNode<NodeData>): 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\n /** Своя карточка из scoped-слота или карточка по умолчанию. */\n renderNodeContent(h: typeof Vue.prototype.$createElement, node: LayoutNode<NodeData>): VNode | VNode[] {\n const slot = this.$scopedSlots.node\n\n if (slot) {\n return (slot({\n node,\n data: node.data,\n depth: node.depth,\n collapsed: node.collapsed,\n hasChildren: node.hasChildren,\n toggle: () => this.toggleNode(node),\n }) ?? []) as VNode[]\n }\n\n return h(TreeNodeCard, {\n props: { label: this.labelOf(node), hasChildren: node.hasChildren, collapsed: node.collapsed },\n on: { toggle: () => this.toggleNode(node) },\n })\n },\n },\n\n render(h): VNode {\n const layout = this.layout\n const drawn = this.drawn\n const linkSlot = this.$scopedSlots.link\n\n // data-depth — уровень, из которого линия выходит: по нему её находят\n // снаружи, например когда полоса раундов прячет пройденное.\n const links = drawn.links.map((link) =>\n h('g', { key: link.id, attrs: { 'data-depth': link.target.depth } }, [\n linkSlot\n ? ((linkSlot({ link }) ?? []) as VNode[])\n : h('path', { class: 'tree-view__link', attrs: { d: link.path } }),\n ]),\n )\n\n const nodes = drawn.nodes.map((node) =>\n h(\n 'div',\n {\n key: node.id,\n class: ['tree-view__node', this.nodeClassOf(node)],\n style: this.nodeStyle(node),\n attrs: { 'data-depth': node.depth },\n on: { click: (event: MouseEvent) => this.onNodeClick(node, event) },\n },\n ([] as VNode[]).concat(this.renderNodeContent(h, node)),\n ),\n )\n\n return h(\n 'div',\n {\n class: [\n 'tree-view',\n { 'tree-view--pannable': this.pannable, 'tree-view--content': this.size === 'content' },\n ],\n style: this.rootStyle,\n },\n [\n h('div', { ref: 'canvas', class: 'tree-view__canvas', style: this.canvasStyle }, [\n h(\n 'svg',\n {\n class: 'tree-view__links',\n attrs: { width: layout.width, height: layout.height },\n },\n links as VNode[],\n ),\n ...nodes,\n ]),\n ],\n )\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n} from '@bigplay/tree-view-core'\n\n/**\n * Карточка матча для Vue 2. Разметка и классы один в один как в Vue 3 —\n * строки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketCard',\n props: {\n /** Матч: нужны только команды. */\n match: { type: Object as PropType<BracketCardMatch | null>, default: null },\n /** Сколько строк рисовать. У финала — одна. */\n rows: { type: Number, default: undefined },\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint: { type: String, default: 'Нажми для перехода в матч 👇' },\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel: { type: String, default: 'ТП' },\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint: { type: String, default: 'Техническое поражение' },\n },\n\n data() {\n return { hoveredKey: null as string | null }\n },\n\n computed: {\n card(): ReturnType<typeof buildBracketCard> {\n return buildBracketCard(this.match, { rows: this.rows, techDefeatLabel: this.techDefeatLabel })\n },\n },\n\n methods: {\n renderRow(h: typeof Vue.prototype.$createElement, row: BracketCardRow): VNode {\n const content: VNode[] = []\n\n if (row.team) {\n if (row.isTechDefeat && this.hoveredKey === row.key) {\n content.push(h('span', { class: 'tv-bracket__tooltip' }, this.techDefeatHint))\n }\n content.push(h('span', { class: 'tv-bracket__name' }, row.team.name))\n content.push(h('span', { class: 'tv-bracket__score' }, row.score))\n }\n\n return h(\n 'div',\n {\n key: row.key,\n class: [\n 'tv-bracket__row',\n {\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 ],\n attrs: row.isMyTeam ? { title: 'Моя команда' } : {},\n on: {\n click: () => {\n if (row.team) this.$emit('select', row.team, row)\n },\n mouseenter: () => {\n this.hoveredKey = row.key\n },\n mouseleave: () => {\n this.hoveredKey = null\n },\n },\n },\n content,\n )\n },\n },\n\n render(h): VNode {\n const card = this.card\n const children: VNode[] = []\n\n if (card.hasMyTeam && this.myTeamHint) {\n children.push(h('div', { class: 'tv-bracket__hint' }, this.myTeamHint))\n }\n\n for (const row of card.rows) children.push(this.renderRow(h, row))\n\n return h('div', { class: ['tv-bracket', { 'tv-bracket--my-team': card.hasMyTeam }] }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketRounds,\n findScrollParent,\n hideRoundsBefore,\n horizontalBox,\n levelGapForWidth,\n nodesInColumn,\n offsetWithin,\n resolveOptions,\n roundAtScroll,\n roundOfDepth,\n roundScrollLeft,\n roundSiblingGap,\n smoothScrollLeft,\n watchChildren,\n watchMedia,\n watchWidth,\n type BracketRound,\n type BracketRoundColumn,\n type BracketRoundsModel,\n type TreeNode,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/**\n * Полоса раундов для Vue 2. Разметка и классы один в один как в Vue 3 —\n * колонки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketRounds',\n props: {\n /** Раунды по порядку — от первого к финалу. */\n rounds: { type: Array as PropType<BracketRound[]>, default: () => [] },\n /** Настройки сетки — те же, что уходят в `TreeView`. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Показывать полосу. Содержимое слота рисуется в любом случае. */\n isShow: { type: Boolean, default: true },\n /**\n * Базовое расстояние между матчами одного раунда в свайпере.\n * Раунд с несколькими матчами получает кратно больше — до четырёх раз.\n */\n swipeSiblingGap: { type: Number, default: 12 },\n /** Прятать пройденные раунды и линии, которые из них выходят. */\n hidePassed: { type: Boolean, default: true },\n /** Включить свайпер принудительно. Не передан — решает ширина экрана. */\n swipe: { type: Boolean, default: undefined },\n /** Медиавыражение «узкий экран», при котором включается свайпер. */\n mobileQuery: { type: String, default: MOBILE_MEDIA_QUERY },\n },\n\n data() {\n return {\n // Узкий ли экран. До монтирования — нет: на сервере ширину знать неоткуда.\n isNarrow: false,\n // Сколько места отведено полосе: по нему считается ширина раунда в свайпере.\n available: 0,\n // Раунд, на котором стоит прокрутка. Меняется прямо во время свайпа.\n current: 0,\n stop: [] as Array<() => void>,\n }\n },\n\n computed: {\n /** Полосы нет — свайпать нечего: сетку в это время таскают и масштабируют. */\n isSwipe(): boolean {\n return this.isShow && (this.swipe === undefined ? this.isNarrow : this.swipe)\n },\n\n base(): TreeViewOptions {\n return resolveOptions(this.options)\n },\n\n /**\n * Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —\n * число. Если это всё-таки функция, считать колонки не по чему: берём значение\n * по умолчанию, чтобы полоса не развалилась.\n */\n nodeWidth(): number {\n return typeof this.base.nodeWidth === 'number'\n ? this.base.nodeWidth\n : (DEFAULT_OPTIONS.nodeWidth as number)\n },\n\n levelGap(): number {\n return this.isSwipe\n ? levelGapForWidth(this.available, this.nodeWidth)\n : this.base.levelGap\n },\n\n model(): BracketRoundsModel {\n return buildBracketRounds(this.rounds, {\n nodeWidth: this.nodeWidth,\n levelGap: this.levelGap,\n })\n },\n\n /** Дерево турнирной сетки растёт справа налево: первый раунд — самый глубокий. */\n mirrored(): boolean {\n return this.base.direction === 'right-to-left' || this.base.direction === 'bottom-to-top'\n },\n\n /**\n * Настройки для сетки в свайпере.\n *\n * Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по\n * себе: в дереве место второго раунда выводится из первого, поэтому отдельно\n * его отступ не задать. Сам отступ зависит от того, сколько матчей в раунде.\n */\n gridOptions(): Partial<TreeViewOptions> {\n if (!this.isSwipe) return { ...this.options }\n\n const base = this.swipeSiblingGap\n\n return {\n ...this.options,\n levelGap: this.levelGap,\n levelLayout: 'stack',\n siblingGap: (_node: TreeNode<unknown>, count: number) => roundSiblingGap(count, base),\n }\n },\n },\n\n watch: {\n current: 'applyHidden',\n isSwipe: 'applyHidden',\n model: 'applyHidden',\n },\n\n mounted() {\n this.stop.push(\n watchMedia(this.mobileQuery, (matches) => {\n this.isNarrow = matches\n }),\n watchWidth((this.$el as HTMLElement).parentElement, (width) => {\n this.available = width\n }),\n // Сетка перерисовывает карточки — скрытое расставляем заново.\n watchChildren(this.$refs.content as HTMLElement, () => this.applyHidden()),\n )\n\n this.applyHidden()\n },\n\n beforeDestroy() {\n this.stop.forEach((off) => off())\n },\n\n methods: {\n /** Прячет всё, что осталось позади: карточки пройденных раундов и их линии. */\n applyHidden(): void {\n const content = this.$refs.content as HTMLElement | undefined\n if (!content) return\n\n const hideBefore = this.isSwipe && this.hidePassed ? this.current : 0\n const count = this.model.columns.length\n\n hideRoundsBefore(content, hideBefore, (depth) =>\n roundOfDepth(depth, count, this.mirrored),\n )\n },\n\n onScroll(): void {\n const element = this.$el as HTMLElement | null\n if (!element || !this.isSwipe) return\n\n const width = this.model.columns[0]?.width ?? 0\n const next = roundAtScroll(element.scrollLeft, width, this.model.columns.length)\n if (next === this.current) return\n\n this.current = next\n this.$emit('round', next)\n },\n\n /**\n * Подводит сетку к матчам раунда — только по горизонтали.\n *\n * Меряем по самой карточке: так раунд встаёт по центру экрана точно, а у финала\n * прокрутка упирается в правый край и он виден целиком. Карточек не нашли\n * (виртуализация) — считаем по колонке, она той же ширины и на том же месте.\n *\n * Вертикаль не трогаем: страница остаётся там, где была.\n */\n scrollToRound(column: BracketRoundColumn): void {\n const element = this.$el as HTMLElement | null\n if (!element) return\n\n // В свайпере прокручивается сама полоса, иначе — блок с overflow-x вокруг неё.\n const container = this.isSwipe ? element : findScrollParent(element)\n if (!container) return\n\n const content = this.$refs.content as HTMLElement | undefined\n // Карточки раунда стоят друг под другом, поэтому по горизонтали годится любая.\n const card = content\n ? nodesInColumn(content, column.index, column.width, this.model.offset)[0]\n : undefined\n\n const box = card\n ? horizontalBox(card, container)\n : {\n left: offsetWithin(element, container) + column.index * column.width,\n width: column.width,\n }\n\n smoothScrollLeft(\n container,\n roundScrollLeft({\n ...box,\n viewport: container.clientWidth,\n scrollWidth: container.scrollWidth,\n }),\n )\n },\n\n onSelect(column: BracketRoundColumn): void {\n this.$emit('select', column)\n this.scrollToRound(column)\n },\n\n renderColumn(h: typeof Vue.prototype.$createElement, column: BracketRoundColumn): VNode {\n return h(\n 'div',\n {\n key: column.key,\n class: 'tv-rounds__item',\n style: { width: `${column.width}px` },\n on: { click: () => this.onSelect(column) },\n },\n this.$scopedSlots.round?.({ round: column }) ?? column.name,\n )\n },\n },\n\n render(h): VNode {\n const children: VNode[] = []\n\n if (this.isShow) {\n children.push(\n h(\n 'div',\n { class: 'tv-rounds__bar', style: { width: `${this.model.width}px` } },\n this.model.columns.map((column) => this.renderColumn(h, column)),\n ),\n )\n }\n\n // Сюда кладут саму сетку. `options` — настройки, подогнанные под экран:\n // в свайпере раунд занимает всю ширину, а матчи внутри стоят ближе.\n const content =\n this.$scopedSlots.default?.({\n options: this.gridOptions,\n isSwipe: this.isSwipe,\n round: this.current,\n }) ?? this.$slots.default\n\n children.push(\n h(\n 'div',\n {\n ref: 'content',\n class: 'tv-rounds__content',\n style: this.isShow ? { paddingLeft: `${this.model.offset}px` } : {},\n },\n content,\n ),\n )\n\n return h(\n 'div',\n {\n class: ['tv-rounds', { 'tv-rounds--swipe': this.isSwipe }],\n on: { scroll: () => this.onScroll() },\n },\n children,\n )\n },\n})\n","import type { VueConstructor } from 'vue'\nimport TreeView from './TreeView'\nimport TreeNodeCard from './TreeNodeCard'\nimport BracketCard from './BracketCard'\nimport BracketRounds from './BracketRounds'\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, BracketRounds }\n\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n BracketRound,\n BracketRoundColumn,\n BracketRoundsModel,\n BracketRoundsOptions,\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 {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketCard,\n bracketCardHeight,\n buildBracketRounds,\n} from '@bigplay/tree-view-core'\n\n/** Плагин: `Vue.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin = {\n install(Vue: VueConstructor) {\n Vue.component('TreeView', TreeView)\n Vue.component('TreeNodeCard', TreeNodeCard)\n Vue.component('BracketCard', BracketCard)\n Vue.component('BracketRounds', BracketRounds)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["TreeNodeCard","Vue","h","children","event","controllers","TreeView","DEFAULT_OVERSCAN","estimateVisibleRect","tree","toTree","layoutTree","applyCollapsed","visibleLayout","toCssTransform","controller","PanZoomController","transform","measureVisibleRect","padding","id","toggleCollapsed","node","data","slot","layout","drawn","linkSlot","links","link","nodes","BracketCard","buildBracketCard","row","content","card","BracketRounds","MOBILE_MEDIA_QUERY","resolveOptions","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","watchMedia","matches","watchWidth","width","watchChildren","off","hideBefore","hideRoundsBefore","depth","roundOfDepth","element","next","roundAtScroll","column","container","findScrollParent","nodesInColumn","box","horizontalBox","offsetWithin","smoothScrollLeft","roundScrollLeft","injectStyles","styles","TreeViewPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/TreeNodeCard.ts","../src/TreeView.ts","../src/BracketCard.ts","../src/BracketRounds.ts","../src/index.ts"],"sourcesContent":["import Vue from 'vue'\n\n/**\n * Карточка узла по умолчанию для Vue 2.\n * Разметка и классы один в один как в Vue 3 — стили общие.\n */\nexport default Vue.extend({\n name: 'TreeNodeCard',\n props: {\n label: { type: String, required: true },\n hasChildren: { type: Boolean, default: false },\n collapsed: { type: Boolean, default: false },\n },\n render(h) {\n const children = [h('span', { class: 'tree-view-card__label' }, this.label)]\n\n if (this.hasChildren) {\n children.push(\n h(\n 'button',\n {\n class: 'tree-view-card__toggle',\n attrs: {\n type: 'button',\n 'data-tv-no-pan': '',\n 'aria-label': this.collapsed ? 'Развернуть' : 'Свернуть',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n ),\n )\n }\n\n return h('div', { class: 'tree-view-card' }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n applyCollapsed,\n DEFAULT_OVERSCAN,\n estimateVisibleRect,\n layoutTree,\n measureVisibleRect,\n PanZoomController,\n resolveOptions,\n roundContentHeight,\n toCssTransform,\n toggleCollapsed,\n toTree,\n visibleLayout,\n type Accessors,\n type Layout,\n type LayoutNode,\n type Rect,\n type ScaleLimits,\n type Transform,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\nimport TreeNodeCard from './TreeNodeCard'\n\n/** Данные узла могут быть какими угодно — библиотека читает их только аксессорами. */\ntype NodeData = any\n\n/**\n * Контроллеры держим здесь, а не в data(): Vue 2 сделал бы объект реактивным,\n * а внутри него DOM-элемент и Map — наблюдать за ними незачем.\n */\nconst controllers = new WeakMap<Vue, PanZoomController>()\n\nexport default Vue.extend({\n name: 'TreeView',\n props: {\n /** Объект, массив объектов или плоский список (вместе с `getParentId`). */\n data: { type: [Object, Array] as PropType<NodeData>, default: null },\n /** Размеры и направление. Можно передать только часть полей. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Как достать id узла. По умолчанию — поле `id`. */\n getId: { type: Function as PropType<Accessors<NodeData>['getId']>, default: undefined },\n /** Как достать детей. По умолчанию — поле `children`. */\n getChildren: { type: Function as PropType<Accessors<NodeData>['getChildren']>, default: undefined },\n /** Как достать id родителя. Передан — значит данные плоские. */\n getParentId: { type: Function as PropType<Accessors<NodeData>['getParentId']>, default: undefined },\n /** Клик по узлу сворачивает/разворачивает ветку. */\n collapsible: { type: Boolean, default: false },\n /** Разрешить перетаскивание холста. */\n pannable: { type: Boolean, default: true },\n /** Разрешить зум колесом и «щипком». */\n zoomable: { type: Boolean, default: true },\n /** Вписать дерево в контейнер при первом отображении. */\n fitOnMount: { type: Boolean, default: true },\n /**\n * Как компонент занимает место:\n * - `'fill'` (по умолчанию) — растягивается на родителя, лишнее обрезается;\n * - `'content'` — контейнер принимает размер дерева и ничего не обрезает.\n */\n size: { type: String as PropType<'fill' | 'content'>, default: 'fill' },\n /** Пределы масштаба. */\n scaleLimits: { type: Object as PropType<ScaleLimits>, default: undefined },\n /** Дополнительный класс на обёртке узла — строкой или функцией. */\n nodeClass: {\n type: [String, Function] as PropType<string | ((node: LayoutNode<NodeData>) => string)>,\n default: undefined,\n },\n /**\n * Рисовать только то, что видно на экране. На больших сетках в DOM\n * попадает несколько десятков карточек вместо тысячи.\n */\n virtualize: { type: Boolean, default: true },\n /** Запас вокруг видимой области в пикселях. */\n overscan: { type: Number, default: DEFAULT_OVERSCAN },\n },\n\n data() {\n return {\n /** Массив, а не Set: Vue 2 не умеет наблюдать за Set. */\n collapsedIds: [] as string[],\n transform: { x: 0, y: 0, scale: 1 } as Transform,\n /**\n * Видимая часть холста; `null` — рисуем всё.\n * До первого измерения берём размер окна: иначе первый кадр\n * нарисует дерево целиком и тут же выбросит лишнее.\n */\n visibleRect: (this.virtualize ? estimateVisibleRect() : null) as Rect | null,\n }\n },\n\n inject: {\n // Кладёт BracketRounds. Сетка может стоять и сама по себе — тогда undefined.\n activeRound: { default: null },\n },\n\n computed: {\n layout(): Layout<NodeData> {\n const tree = toTree<NodeData>(this.data, {\n getId: this.getId,\n getChildren: this.getChildren,\n getParentId: this.getParentId,\n })\n\n return layoutTree(applyCollapsed(tree, new Set(this.collapsedIds)), this.options)\n },\n\n /** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */\n drawn(): Layout<NodeData> {\n return this.virtualize ? visibleLayout(this.layout, this.visibleRect, this.overscan) : this.layout\n },\n\n /**\n * Высота холста.\n *\n * Обычно — по всей раскладке. В свайпере виден один раунд, и высота берётся\n * по нему: иначе финал с одной ячейкой держит высоту самого населённого\n * раунда и под ним остаётся пустота. Номер раунда кладёт BracketRounds.\n */\n canvasHeight(): number {\n // Vue 2 не выводит типы инъекций — достаём явно.\n const injected = (this as unknown as { activeRound?: { value: number | null } }).activeRound\n const round = injected?.value ?? null\n if (round === null) return this.layout.height\n\n const rounds = this.layout.nodes.reduce((max, node) => Math.max(max, node.depth), 0) + 1\n const direction = resolveOptions(this.options).direction\n const mirrored = direction === 'right-to-left' || direction === 'bottom-to-top'\n const height = roundContentHeight(this.layout.nodes, round, rounds, mirrored)\n\n // Раунда с таким номером в раскладке нет — держим прежнюю высоту,\n // чтобы сетка не прыгнула в ноль.\n return height || this.layout.height\n },\n\n canvasStyle(): Record<string, string> {\n return {\n width: `${this.layout.width}px`,\n height: `${this.canvasHeight}px`,\n transform: toCssTransform(this.transform),\n }\n },\n\n rootStyle(): Record<string, string> | undefined {\n if (this.size !== 'content') return undefined\n\n return {\n width: `${this.layout.width * this.transform.scale}px`,\n height: `${this.canvasHeight * this.transform.scale}px`,\n overflow: 'visible',\n }\n },\n },\n\n watch: {\n // Новые данные — видимую часть надо пересчитать уже по ним.\n layout() {\n this.$nextTick(() => this.updateVisibleRect())\n },\n },\n\n mounted() {\n const controller = new PanZoomController({\n pannable: () => this.pannable,\n zoomable: () => this.zoomable,\n limits: this.scaleLimits ? () => this.scaleLimits : undefined,\n onChange: (transform) => {\n this.transform = transform\n // Холст поехал — видимая часть стала другой.\n this.updateVisibleRect()\n this.$emit('transform', transform)\n },\n })\n\n controller.attach(this.$el as HTMLElement)\n controllers.set(this, controller)\n\n this.updateVisibleRect()\n window.addEventListener('scroll', this.updateVisibleRect, { passive: true, capture: true })\n window.addEventListener('resize', this.updateVisibleRect, { passive: true })\n\n if (this.fitOnMount) this.$nextTick(() => this.fit())\n },\n\n beforeDestroy() {\n controllers.get(this)?.detach()\n controllers.delete(this)\n window.removeEventListener('scroll', this.updateVisibleRect, { capture: true } as EventListenerOptions)\n window.removeEventListener('resize', this.updateVisibleRect)\n },\n\n methods: {\n /** Пересчитывает, какая часть холста сейчас видна на экране. */\n updateVisibleRect(): void {\n this.visibleRect = this.virtualize\n ? measureVisibleRect(this.$refs.canvas as Element | undefined, this.transform.scale)\n : null\n },\n\n /** Вписывает дерево в видимую область. */\n fit(padding?: number): void {\n controllers.get(this)?.fit({ width: this.layout.width, height: this.layout.height }, padding)\n },\n zoomIn(): void {\n controllers.get(this)?.zoomIn()\n },\n zoomOut(): void {\n controllers.get(this)?.zoomOut()\n },\n resetTransform(): void {\n controllers.get(this)?.reset()\n },\n setTransform(transform: Transform): void {\n controllers.get(this)?.set(transform)\n },\n /** Развернуть все свёрнутые ветки. */\n expandAll(): void {\n this.collapsedIds = []\n },\n /** Свернуть или развернуть узел по id. */\n toggle(id: string): void {\n this.collapsedIds = [...toggleCollapsed(new Set(this.collapsedIds), id)]\n },\n\n toggleNode(node: LayoutNode<NodeData>): void {\n this.toggle(node.id)\n this.$emit('toggle', node, this.collapsedIds.includes(node.id))\n },\n\n onNodeClick(node: LayoutNode<NodeData>, event: MouseEvent): void {\n this.$emit('node-click', node, event)\n if (this.collapsible && node.hasChildren) this.toggleNode(node)\n },\n\n nodeStyle(node: LayoutNode<NodeData>): Record<string, string> {\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\n nodeClassOf(node: LayoutNode<NodeData>): string | undefined {\n return typeof this.nodeClass === 'function' ? this.nodeClass(node) : this.nodeClass\n },\n\n /** Подпись карточки по умолчанию: name → title → label → id. */\n labelOf(node: LayoutNode<NodeData>): 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\n /** Своя карточка из scoped-слота или карточка по умолчанию. */\n renderNodeContent(h: typeof Vue.prototype.$createElement, node: LayoutNode<NodeData>): VNode | VNode[] {\n const slot = this.$scopedSlots.node\n\n if (slot) {\n return (slot({\n node,\n data: node.data,\n depth: node.depth,\n collapsed: node.collapsed,\n hasChildren: node.hasChildren,\n toggle: () => this.toggleNode(node),\n }) ?? []) as VNode[]\n }\n\n return h(TreeNodeCard, {\n props: { label: this.labelOf(node), hasChildren: node.hasChildren, collapsed: node.collapsed },\n on: { toggle: () => this.toggleNode(node) },\n })\n },\n },\n\n render(h): VNode {\n const layout = this.layout\n const drawn = this.drawn\n const linkSlot = this.$scopedSlots.link\n\n // data-depth — уровень, из которого линия выходит: по нему её находят\n // снаружи, например когда полоса раундов прячет пройденное.\n const links = drawn.links.map((link) =>\n h('g', { key: link.id, attrs: { 'data-depth': link.target.depth } }, [\n linkSlot\n ? ((linkSlot({ link }) ?? []) as VNode[])\n : h('path', { class: 'tree-view__link', attrs: { d: link.path } }),\n ]),\n )\n\n const nodes = drawn.nodes.map((node) =>\n h(\n 'div',\n {\n key: node.id,\n class: ['tree-view__node', this.nodeClassOf(node)],\n style: this.nodeStyle(node),\n attrs: { 'data-depth': node.depth },\n on: { click: (event: MouseEvent) => this.onNodeClick(node, event) },\n },\n ([] as VNode[]).concat(this.renderNodeContent(h, node)),\n ),\n )\n\n return h(\n 'div',\n {\n class: [\n 'tree-view',\n { 'tree-view--pannable': this.pannable, 'tree-view--content': this.size === 'content' },\n ],\n style: this.rootStyle,\n },\n [\n h('div', { ref: 'canvas', class: 'tree-view__canvas', style: this.canvasStyle }, [\n h(\n 'svg',\n {\n class: 'tree-view__links',\n attrs: { width: layout.width, height: layout.height },\n },\n links as VNode[],\n ),\n ...nodes,\n ]),\n ],\n )\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n buildBracketCard,\n type BracketCardMatch,\n type BracketCardRow,\n} from '@bigplay/tree-view-core'\n\n/**\n * Карточка матча для Vue 2. Разметка и классы один в один как в Vue 3 —\n * строки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketCard',\n props: {\n /** Матч: нужны только команды. */\n match: { type: Object as PropType<BracketCardMatch | null>, default: null },\n /** Сколько строк рисовать. У финала — одна. */\n rows: { type: Number, default: undefined },\n /** Подсказка над карточкой с моей командой. Пустая строка — не показывать. */\n myTeamHint: { type: String, default: 'Нажми для перехода в матч 👇' },\n /** Текст в ячейке счёта при техническом поражении. */\n techDefeatLabel: { type: String, default: 'ТП' },\n /** Подсказка по наведению на техническое поражение. */\n techDefeatHint: { type: String, default: 'Техническое поражение' },\n /** У матча есть предыдущие — рисуем кнопку, которая их сворачивает. */\n hasChildren: { type: Boolean, default: false },\n /** Предыдущие матчи сейчас свёрнуты. */\n collapsed: { type: Boolean, default: false },\n /** Подписи кнопки для screen reader. */\n collapseHint: { type: String, default: 'Свернуть предыдущие матчи' },\n expandHint: { type: String, default: 'Показать предыдущие матчи' },\n },\n\n data() {\n return { hoveredKey: null as string | null }\n },\n\n computed: {\n card(): ReturnType<typeof buildBracketCard> {\n return buildBracketCard(this.match, { rows: this.rows, techDefeatLabel: this.techDefeatLabel })\n },\n },\n\n methods: {\n /**\n * Кнопка стоит со стороны предыдущих матчей — туда же уходят линии.\n * data-tv-no-pan: нажатие не должно утаскивать холст.\n */\n renderToggle(h: typeof Vue.prototype.$createElement): VNode {\n const hint = this.collapsed ? this.expandHint : this.collapseHint\n\n return h(\n 'button',\n {\n class: ['tv-bracket__toggle', { 'tv-bracket__toggle--collapsed': this.collapsed }],\n attrs: {\n type: 'button',\n title: hint,\n 'aria-label': hint,\n 'aria-expanded': String(!this.collapsed),\n 'data-tv-no-pan': '',\n },\n on: {\n click: (event: MouseEvent) => {\n event.stopPropagation()\n this.$emit('toggle')\n },\n },\n },\n this.collapsed ? '+' : '−',\n )\n },\n\n renderRow(h: typeof Vue.prototype.$createElement, row: BracketCardRow): VNode {\n const content: VNode[] = []\n\n if (row.team) {\n if (row.isTechDefeat && this.hoveredKey === row.key) {\n content.push(h('span', { class: 'tv-bracket__tooltip' }, this.techDefeatHint))\n }\n content.push(h('span', { class: 'tv-bracket__name' }, row.team.name))\n content.push(h('span', { class: 'tv-bracket__score' }, row.score))\n }\n\n return h(\n 'div',\n {\n key: row.key,\n class: [\n 'tv-bracket__row',\n {\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 ],\n attrs: row.isMyTeam ? { title: 'Моя команда' } : {},\n on: {\n click: () => {\n if (row.team) this.$emit('select', row.team, row)\n },\n mouseenter: () => {\n this.hoveredKey = row.key\n },\n mouseleave: () => {\n this.hoveredKey = null\n },\n },\n },\n content,\n )\n },\n },\n\n render(h): VNode {\n const card = this.card\n const children: VNode[] = []\n\n if (card.hasMyTeam && this.myTeamHint) {\n children.push(h('div', { class: 'tv-bracket__hint' }, this.myTeamHint))\n }\n\n if (this.hasChildren) children.push(this.renderToggle(h))\n\n // Строки лежат в своей обёртке: она держит скругление и контур, поэтому\n // матч читается как один блок, а не как список из четырёх команд.\n // Подсказка и кнопка остаются снаружи — их обрезать нельзя.\n children.push(\n h(\n 'div',\n { class: 'tv-bracket__rows' },\n card.rows.map((row) => this.renderRow(h, row)),\n ),\n )\n\n return h('div', { class: ['tv-bracket', { 'tv-bracket--my-team': card.hasMyTeam }] }, children)\n },\n})\n","import Vue, { type PropType, type VNode } from 'vue'\nimport {\n afterScrollEnd,\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketRounds,\n findScrollParent,\n hideRoundsBefore,\n horizontalBox,\n levelGapForWidth,\n nodesInColumn,\n offsetWithin,\n resolveOptions,\n roundAtScroll,\n roundOfDepth,\n roundScrollLeft,\n roundSiblingGap,\n SWIPE_ELBOW_OFFSET,\n smoothScrollLeft,\n watchChildren,\n watchMedia,\n watchWidth,\n type BracketRound,\n type BracketRoundColumn,\n type BracketRoundsModel,\n type TreeNode,\n type TreeViewOptions,\n} from '@bigplay/tree-view-core'\n\n/**\n * Компоненты, у которых сейчас идёт прокрутка по клику на раунд.\n *\n * Не в `data`: реактивность флагу не нужна, а в options-api лишнее поле ломает\n * вывод типов у `this` в методах. Тот же приём, что у контроллеров в TreeView.\n */\nconst programmatic = new WeakSet<Vue>()\n\n/** Раунд, на котором прокрутка остановилась в прошлый раз. */\nconst anchors = new WeakMap<Vue, number>()\n/** Компоненты, у которых мы ждём, когда прокрутка замрёт. */\nconst settling = new WeakSet<Vue>()\n\n/**\n * Полоса раундов для Vue 2. Разметка и классы один в один как в Vue 3 —\n * колонки считает та же функция из core, стили общие.\n */\nexport default Vue.extend({\n name: 'BracketRounds',\n props: {\n /** Раунды по порядку — от первого к финалу. */\n rounds: { type: Array as PropType<BracketRound[]>, default: () => [] },\n /** Настройки сетки — те же, что уходят в `TreeView`. */\n options: { type: Object as PropType<Partial<TreeViewOptions>>, default: undefined },\n /** Показывать полосу. Содержимое слота рисуется в любом случае. */\n isShow: { type: Boolean, default: true },\n /**\n * Базовое расстояние между матчами одного раунда в свайпере.\n * Раунд с несколькими матчами получает кратно больше — до четырёх раз.\n */\n swipeSiblingGap: { type: Number, default: 12 },\n /** Прятать пройденные раунды и линии, которые из них выходят. */\n hidePassed: { type: Boolean, default: true },\n /** Включить свайпер принудительно. Не передан — решает ширина экрана. */\n swipe: { type: Boolean, default: undefined },\n /** Медиавыражение «узкий экран», при котором включается свайпер. */\n mobileQuery: { type: String, default: MOBILE_MEDIA_QUERY },\n },\n\n data() {\n return {\n // Узкий ли экран. До монтирования — нет: на сервере ширину знать неоткуда.\n isNarrow: false,\n // Сколько места отведено полосе: по нему считается ширина раунда в свайпере.\n available: 0,\n // Раунд, на котором стоит прокрутка. Меняется прямо во время свайпа.\n current: 0,\n stop: [] as Array<() => void>,\n }\n },\n\n computed: {\n /** Полосы нет — свайпать нечего: сетку в это время таскают и масштабируют. */\n isSwipe(): boolean {\n return this.isShow && (this.swipe === undefined ? this.isNarrow : this.swipe)\n },\n\n base(): TreeViewOptions {\n return resolveOptions(this.options)\n },\n\n /**\n * Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —\n * число. Если это всё-таки функция, считать колонки не по чему: берём значение\n * по умолчанию, чтобы полоса не развалилась.\n */\n nodeWidth(): number {\n return typeof this.base.nodeWidth === 'number'\n ? this.base.nodeWidth\n : (DEFAULT_OPTIONS.nodeWidth as number)\n },\n\n levelGap(): number {\n return this.isSwipe\n ? levelGapForWidth(this.available, this.nodeWidth)\n : this.base.levelGap\n },\n\n model(): BracketRoundsModel {\n return buildBracketRounds(this.rounds, {\n nodeWidth: this.nodeWidth,\n levelGap: this.levelGap,\n })\n },\n\n /** Дерево турнирной сетки растёт справа налево: первый раунд — самый глубокий. */\n mirrored(): boolean {\n return this.base.direction === 'right-to-left' || this.base.direction === 'bottom-to-top'\n },\n\n /**\n * Настройки для сетки в свайпере.\n *\n * Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по\n * себе: в дереве место второго раунда выводится из первого, поэтому отдельно\n * его отступ не задать.\n *\n * Внутри пары матчи стоят тесно, а между парами промежуток крупнее — иначе\n * весь раунд читается как один список и непонятно, кто с кем сходится дальше.\n * Линии рисуются скобкой: пара сходится в одну вертикаль сразу за карточками,\n * и уже от её середины идёт линия к следующему матчу. В раскладке `'stack'`\n * родитель стоит не по центру детей, поэтому обычная «ступенька» выходила бы\n * из верхнего матча пары, а не из середины.\n */\n gridOptions(): Partial<TreeViewOptions> {\n if (!this.isSwipe) return { ...this.options }\n\n const base = this.swipeSiblingGap\n\n return {\n ...this.options,\n levelGap: this.levelGap,\n levelLayout: 'stack',\n siblingGap: base,\n groupGap: (_node: TreeNode<unknown>, count: number) => roundSiblingGap(count, base),\n linkStyle: 'bracket',\n elbowOffset: this.options?.elbowOffset ?? SWIPE_ELBOW_OFFSET,\n }\n },\n },\n\n watch: {\n current: 'applyHidden',\n isSwipe: 'applyHidden',\n model: 'applyHidden',\n },\n\n mounted() {\n this.stop.push(\n watchMedia(this.mobileQuery, (matches) => {\n this.isNarrow = matches\n }),\n watchWidth((this.$el as HTMLElement).parentElement, (width) => {\n this.available = width\n }),\n // Сетка перерисовывает карточки — скрытое расставляем заново.\n watchChildren(this.$refs.content as HTMLElement, () => this.applyHidden()),\n )\n\n this.applyHidden()\n },\n\n beforeDestroy() {\n this.stop.forEach((off) => off())\n },\n\n provide(): Record<string, unknown> {\n // Vue 2 не умеет реактивный provide — отдаём объект, у которого геттер\n // всегда возвращает свежее значение. TreeView читает его как .value.\n const self = this as unknown as { isSwipe: boolean; current: number }\n\n return {\n activeRound: {\n get value(): number | null {\n return self.isSwipe ? self.current : null\n },\n },\n }\n },\n\n methods: {\n /** Прячет всё, что осталось позади: карточки пройденных раундов и их линии. */\n applyHidden(): void {\n const content = this.$refs.content as HTMLElement | undefined\n if (!content) return\n\n const hideBefore = this.isSwipe && this.hidePassed ? this.current : 0\n const count = this.model.columns.length\n\n hideRoundsBefore(content, hideBefore, (depth) =>\n roundOfDepth(depth, count, this.mirrored),\n )\n },\n\n onScroll(): void {\n const element = this.$refs.scroll as HTMLElement | undefined\n if (!element || !this.isSwipe) return\n\n // Полоса едет за сеткой: она в отдельном слое и своей прокрутки не имеет.\n const head = this.$refs.head as HTMLElement | undefined\n if (head) head.scrollLeft = element.scrollLeft\n\n const width = this.model.columns[0]?.width ?? 0\n let next = roundAtScroll(element.scrollLeft, width, this.model.columns.length)\n\n /*\n * Резкий свайп не должен проскакивать раунды.\n *\n * На это стоит `scroll-snap-stop: always`, но при инерции он спасает не\n * всегда: событий прокрутки за кадр приходит мало, и палец успевает утащить\n * ленту через несколько раундов раньше, чем браузер решит прилипнуть.\n */\n if (!programmatic.has(this) && width > 0) {\n const anchor = anchors.get(this) ?? 0\n const limited = Math.min(Math.max(next, anchor - 1), anchor + 1)\n\n if (limited !== next) {\n next = limited\n element.scrollLeft = next * width\n }\n\n // Один свайп — один раунд: пока прокрутка не замрёт, точка отсчёта\n // не двигается. Ограничивать шаг между событиями бесполезно — при\n // инерции они приходят подряд по одному раунду.\n if (!settling.has(this)) {\n settling.add(this)\n afterScrollEnd(element, () => {\n settling.delete(this)\n anchors.set(this, this.current)\n })\n }\n }\n\n if (next === this.current) return\n\n this.current = next\n this.$emit('round', next)\n },\n\n /**\n * Подводит сетку к матчам раунда — только по горизонтали.\n *\n * Меряем по самой карточке: так раунд встаёт по центру экрана точно, а у финала\n * прокрутка упирается в правый край и он виден целиком. Карточек не нашли\n * (виртуализация) — считаем по колонке, она той же ширины и на том же месте.\n *\n * Вертикаль не трогаем: страница остаётся там, где была.\n */\n scrollToRound(column: BracketRoundColumn): void {\n const element = this.$el as HTMLElement | null\n if (!element) return\n\n // В свайпере прокручивается блок с сеткой, иначе — блок с overflow-x вокруг.\n const container = this.isSwipe\n ? ((this.$refs.scroll as HTMLElement | undefined) ?? null)\n : findScrollParent(element)\n if (!container) return\n\n const content = this.$refs.content as HTMLElement | undefined\n // Карточки раунда стоят друг под другом, поэтому по горизонтали годится любая.\n const card = content\n ? nodesInColumn(content, column.index, column.width, this.model.offset)[0]\n : undefined\n\n const box = card\n ? horizontalBox(card, container)\n : {\n left: offsetWithin(element, container) + column.index * column.width,\n width: column.width,\n }\n\n // Прокрутка по клику едет сразу к нужному раунду — ограничитель «не дальше\n // соседнего» для неё выключаем, иначе из финала в первый раунд не попасть.\n programmatic.add(this)\n smoothScrollLeft(\n container,\n roundScrollLeft({\n ...box,\n viewport: container.clientWidth,\n scrollWidth: container.scrollWidth,\n }),\n )\n afterScrollEnd(container, () => {\n programmatic.delete(this)\n anchors.set(this, this.current)\n })\n },\n\n onSelect(column: BracketRoundColumn): void {\n this.$emit('select', column)\n this.scrollToRound(column)\n },\n\n renderColumn(h: typeof Vue.prototype.$createElement, column: BracketRoundColumn): VNode {\n return h(\n 'div',\n {\n key: column.key,\n class: 'tv-rounds__item',\n style: { width: `${column.width}px` },\n on: { click: () => this.onSelect(column) },\n },\n this.$scopedSlots.round?.({ round: column }) ?? column.name,\n )\n },\n },\n\n render(h): VNode {\n const children: VNode[] = []\n\n // Полоса лежит ВНЕ прокручиваемого блока — иначе её не прилепить к странице:\n // элемент с `overflow-x` становится скролл-контейнером по обеим осям, и\n // `position: sticky` внутри него липнет к нему же, а не к экрану.\n if (this.isShow) {\n children.push(\n h('div', { ref: 'head', class: 'tv-rounds__head' }, [\n h(\n 'div',\n { class: 'tv-rounds__bar', style: { width: `${this.model.width}px` } },\n this.model.columns.map((column) => this.renderColumn(h, column)),\n ),\n ]),\n )\n }\n\n // Сюда кладут саму сетку. `options` — настройки, подогнанные под экран:\n // в свайпере раунд занимает всю ширину, а матчи внутри стоят ближе.\n const content =\n this.$scopedSlots.default?.({\n options: this.gridOptions,\n isSwipe: this.isSwipe,\n round: this.current,\n }) ?? this.$slots.default\n\n const inner: VNode[] = []\n\n // Точки прилипания раньше висели на подписях раундов, а они уехали из\n // прокручиваемого блока. Здесь их заменяют невидимые якоря той же ширины.\n if (this.isSwipe) {\n inner.push(\n h(\n 'div',\n {\n class: 'tv-rounds__snap',\n style: { width: `${this.model.width}px` },\n attrs: { 'aria-hidden': 'true' },\n },\n this.model.columns.map((column) =>\n h('i', {\n key: column.key,\n class: 'tv-rounds__snap-item',\n style: { width: `${column.width}px` },\n }),\n ),\n ),\n )\n }\n\n inner.push(\n h(\n 'div',\n {\n ref: 'content',\n class: 'tv-rounds__content',\n style: this.isShow ? { paddingLeft: `${this.model.offset}px` } : {},\n },\n content,\n ),\n )\n\n children.push(\n h(\n 'div',\n {\n ref: 'scroll',\n class: 'tv-rounds__scroll',\n on: { scroll: () => this.onScroll() },\n },\n inner,\n ),\n )\n\n return h('div', { class: ['tv-rounds', { 'tv-rounds--swipe': this.isSwipe }] }, children)\n },\n})\n","import type { VueConstructor } from 'vue'\nimport TreeView from './TreeView'\nimport TreeNodeCard from './TreeNodeCard'\nimport BracketCard from './BracketCard'\nimport BracketRounds from './BracketRounds'\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, BracketRounds }\n\nexport type {\n Accessors,\n BracketCardMatch,\n BracketCardModel,\n BracketCardOptions,\n BracketCardRow,\n BracketCardTeam,\n BracketRound,\n BracketRoundColumn,\n BracketRoundsModel,\n BracketRoundsOptions,\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 {\n DEFAULT_OPTIONS,\n MOBILE_MEDIA_QUERY,\n buildBracketCard,\n bracketCardHeight,\n buildBracketRounds,\n} from '@bigplay/tree-view-core'\n\n/** Плагин: `Vue.use(TreeViewPlugin)` регистрирует компоненты глобально. */\nexport const TreeViewPlugin = {\n install(Vue: VueConstructor) {\n Vue.component('TreeView', TreeView)\n Vue.component('TreeNodeCard', TreeNodeCard)\n Vue.component('BracketCard', BracketCard)\n Vue.component('BracketRounds', BracketRounds)\n },\n}\n\nexport default TreeViewPlugin\n"],"names":["TreeNodeCard","Vue","h","children","event","controllers","TreeView","DEFAULT_OVERSCAN","estimateVisibleRect","tree","toTree","layoutTree","applyCollapsed","visibleLayout","round","rounds","max","node","direction","resolveOptions","mirrored","roundContentHeight","toCssTransform","controller","PanZoomController","transform","measureVisibleRect","padding","id","toggleCollapsed","data","slot","layout","drawn","linkSlot","links","link","nodes","BracketCard","buildBracketCard","hint","row","content","card","programmatic","anchors","settling","BracketRounds","MOBILE_MEDIA_QUERY","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","SWIPE_ELBOW_OFFSET","watchMedia","matches","watchWidth","width","watchChildren","off","self","hideBefore","hideRoundsBefore","depth","roundOfDepth","element","head","next","roundAtScroll","anchor","limited","afterScrollEnd","column","container","findScrollParent","nodesInColumn","box","horizontalBox","offsetWithin","smoothScrollLeft","roundScrollLeft","inner","injectStyles","styles","TreeViewPlugin"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigplay/tree-view-vue2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Компонент дерева и турнирных сеток для Vue 2.6 / 2.7",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@bigplay/tree-view-core": "^0.
|
|
38
|
+
"@bigplay/tree-view-core": "^0.6.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"vue": "^2.6.0 || ^2.7.0"
|