@bigplay/tree-view-vue2 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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/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 l=require("vue"),r=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:i=>{i.stopPropagation(),this.$emit("toggle")}}},this.collapsed?"+":"−")),e("div",{class:"tree-view-card"},t)}}),a=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:r.DEFAULT_OVERSCAN}},data(){return{collapsedIds:[],transform:{x:0,y:0,scale:1},visibleRect:this.virtualize?r.estimateVisibleRect():null}},computed:{layout(){const e=r.toTree(this.data,{getId:this.getId,getChildren:this.getChildren,getParentId:this.getParentId});return r.layoutTree(r.applyCollapsed(e,new Set(this.collapsedIds)),this.options)},drawn(){return this.virtualize?r.visibleLayout(this.layout,this.visibleRect,this.overscan):this.layout},canvasStyle(){return{width:`${this.layout.width}px`,height:`${this.layout.height}px`,transform:r.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 r.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),a.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(){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?r.measureVisibleRect(this.$refs.canvas,this.transform.scale):null},fit(e){a.get(this)?.fit({width:this.layout.width,height:this.layout.height},e)},zoomIn(){a.get(this)?.zoomIn()},zoomOut(){a.get(this)?.zoomOut()},resetTransform(){a.get(this)?.reset()},setTransform(e){a.get(this)?.set(e)},expandAll(){this.collapsedIds=[]},toggle(e){this.collapsedIds=[...r.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 i=this.$scopedSlots.node;return i?i({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,i=this.drawn,o=this.$scopedSlots.link,n=i.links.map(s=>e("g",{key:s.id,attrs:{"data-depth":s.target.depth}},[o?o({link:s})??[]:e("path",{class:"tree-view__link",attrs:{d:s.path}})])),d=i.nodes.map(s=>e("div",{key:s.id,class:["tree-view__node",this.nodeClassOf(s)],style:this.nodeStyle(s),attrs:{"data-depth":s.depth},on:{click:b=>this.onNodeClick(s,b)}},[].concat(this.renderNodeContent(e,s))));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}},n),...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:"Техническое поражение"},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 r.buildBracketCard(this.match,{rows:this.rows,techDefeatLabel:this.techDefeatLabel})}},methods:{renderToggle(e){const t=this.collapsed?this.expandHint:this.collapseHint;return e("button",{class:["tv-bracket__toggle",{"tv-bracket__toggle--collapsed":this.collapsed}],attrs:{type:"button",title:t,"aria-label":t,"aria-expanded":String(!this.collapsed),"data-tv-no-pan":""},on:{click:i=>{i.stopPropagation(),this.$emit("toggle")}}},this.collapsed?"+":"−")},renderRow(e,t){const i=[];return t.team&&(t.isTechDefeat&&this.hoveredKey===t.key&&i.push(e("span",{class:"tv-bracket__tooltip"},this.techDefeatHint)),i.push(e("span",{class:"tv-bracket__name"},t.team.name)),i.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}}},i)}},render(e){const t=this.card,i=[];return t.hasMyTeam&&this.myTeamHint&&i.push(e("div",{class:"tv-bracket__hint"},this.myTeamHint)),this.hasChildren&&i.push(this.renderToggle(e)),i.push(e("div",{class:"tv-bracket__rows"},t.rows.map(o=>this.renderRow(e,o)))),e("div",{class:["tv-bracket",{"tv-bracket--my-team":t.hasMyTeam}]},i)}}),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:r.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 r.resolveOptions(this.options)},nodeWidth(){return typeof this.base.nodeWidth=="number"?this.base.nodeWidth:r.DEFAULT_OPTIONS.nodeWidth},levelGap(){return this.isSwipe?r.levelGapForWidth(this.available,this.nodeWidth):this.base.levelGap},model(){return r.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:e,groupGap:(t,i)=>r.roundSiblingGap(i,e),linkStyle:"bracket",elbowOffset:this.options?.elbowOffset??r.SWIPE_ELBOW_OFFSET}}},watch:{current:"applyHidden",isSwipe:"applyHidden",model:"applyHidden"},mounted(){this.stop.push(r.watchMedia(this.mobileQuery,e=>{this.isNarrow=e}),r.watchWidth(this.$el.parentElement,e=>{this.available=e}),r.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,i=this.model.columns.length;r.hideRoundsBefore(e,t,o=>r.roundOfDepth(o,i,this.mirrored))},onScroll(){const e=this.$el;if(!e||!this.isSwipe)return;const t=this.model.columns[0]?.width??0,i=r.roundAtScroll(e.scrollLeft,t,this.model.columns.length);i!==this.current&&(this.current=i,this.$emit("round",i))},scrollToRound(e){const t=this.$el;if(!t)return;const i=this.isSwipe?t:r.findScrollParent(t);if(!i)return;const o=this.$refs.content,n=o?r.nodesInColumn(o,e.index,e.width,this.model.offset)[0]:void 0,d=n?r.horizontalBox(n,i):{left:r.offsetWithin(t,i)+e.index*e.width,width:e.width};r.smoothScrollLeft(i,r.roundScrollLeft({...d,viewport:i.clientWidth,scrollWidth:i.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(o=>this.renderColumn(e,o))));const i=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`}:{}},i)),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;--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__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 .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;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;scroll-snap-stop:always}.tv-rounds--swipe [data-depth]{transition:opacity var(--tv-rounds-fade, .22s) ease,transform var(--tv-rounds-fade, .22s) var(--tv-rounds-collapse-ease)}.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}}";r.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:()=>r.DEFAULT_OPTIONS});Object.defineProperty(exports,"MOBILE_MEDIA_QUERY",{enumerable:!0,get:()=>r.MOBILE_MEDIA_QUERY});Object.defineProperty(exports,"bracketCardHeight",{enumerable:!0,get:()=>r.bracketCardHeight});Object.defineProperty(exports,"buildBracketCard",{enumerable:!0,get:()=>r.buildBracketCard});Object.defineProperty(exports,"buildBracketRounds",{enumerable:!0,get:()=>r.buildBracketRounds});exports.BracketCard=p;exports.BracketRounds=u;exports.TreeNodeCard=c;exports.TreeView=h;exports.TreeViewPlugin=v;exports.default=v;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -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 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 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 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 * Полоса раундов для 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 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","hint","row","content","card","BracketRounds","MOBILE_MEDIA_QUERY","resolveOptions","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","SWIPE_ELBOW_OFFSET","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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"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import a from "vue";
2
- import { DEFAULT_OVERSCAN as c, toggleCollapsed as p, measureVisibleRect as u, PanZoomController as v, toCssTransform as b, visibleLayout as f, toTree as w, layoutTree as m, applyCollapsed as g, estimateVisibleRect as y, buildBracketCard as k, MOBILE_MEDIA_QUERY as x, findScrollParent as _, nodesInColumn as S, horizontalBox as C, offsetWithin as $, smoothScrollLeft as T, roundScrollLeft as z, roundAtScroll as R, hideRoundsBefore as B, roundOfDepth as O, watchMedia as L, watchWidth as I, watchChildren as N, roundSiblingGap as E, buildBracketRounds as W, levelGapForWidth as D, DEFAULT_OPTIONS as V, resolveOptions as H, injectStyles as M } from "@bigplay/tree-view-core";
3
- import { DEFAULT_OPTIONS as q, MOBILE_MEDIA_QUERY as Z, bracketCardHeight as J, buildBracketCard as X, buildBracketRounds as tt } from "@bigplay/tree-view-core";
4
- const d = a.extend({
1
+ import n from "vue";
2
+ import { DEFAULT_OVERSCAN as h, toggleCollapsed as p, measureVisibleRect as v, PanZoomController as u, toCssTransform as b, visibleLayout as f, toTree as w, layoutTree as g, applyCollapsed as m, estimateVisibleRect as y, buildBracketCard as k, MOBILE_MEDIA_QUERY as x, findScrollParent as _, nodesInColumn as S, horizontalBox as C, offsetWithin as $, smoothScrollLeft as T, roundScrollLeft as z, roundAtScroll as B, hideRoundsBefore as O, roundOfDepth as R, watchMedia as L, watchWidth as I, watchChildren as E, SWIPE_ELBOW_OFFSET as N, roundSiblingGap as W, buildBracketRounds as H, levelGapForWidth as D, DEFAULT_OPTIONS as P, resolveOptions as V, injectStyles as G } from "@bigplay/tree-view-core";
3
+ import { DEFAULT_OPTIONS as Z, MOBILE_MEDIA_QUERY as J, bracketCardHeight as X, buildBracketCard as tt, buildBracketRounds as et } from "@bigplay/tree-view-core";
4
+ const d = n.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
- }), r = /* @__PURE__ */ new WeakMap(), G = a.extend({
33
+ }), r = /* @__PURE__ */ new WeakMap(), M = n.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: c }
73
+ overscan: { type: Number, default: h }
74
74
  },
75
75
  data() {
76
76
  return {
@@ -92,7 +92,7 @@ const d = a.extend({
92
92
  getChildren: this.getChildren,
93
93
  getParentId: this.getParentId
94
94
  });
95
- return m(g(t, new Set(this.collapsedIds)), this.options);
95
+ return g(m(t, new Set(this.collapsedIds)), this.options);
96
96
  },
97
97
  /** То, что реально попадёт в DOM: при `virtualize` — только видимая часть. */
98
98
  drawn() {
@@ -121,7 +121,7 @@ const d = a.extend({
121
121
  }
122
122
  },
123
123
  mounted() {
124
- const t = new v({
124
+ const t = new u({
125
125
  pannable: () => this.pannable,
126
126
  zoomable: () => this.zoomable,
127
127
  limits: this.scaleLimits ? () => this.scaleLimits : void 0,
@@ -137,7 +137,7 @@ const d = a.extend({
137
137
  methods: {
138
138
  /** Пересчитывает, какая часть холста сейчас видна на экране. */
139
139
  updateVisibleRect() {
140
- this.visibleRect = this.virtualize ? u(this.$refs.canvas, this.transform.scale) : null;
140
+ this.visibleRect = this.virtualize ? v(this.$refs.canvas, this.transform.scale) : null;
141
141
  },
142
142
  /** Вписывает дерево в видимую область. */
143
143
  fit(t) {
@@ -202,21 +202,21 @@ const d = a.extend({
202
202
  }
203
203
  },
204
204
  render(t) {
205
- const e = this.layout, i = this.drawn, s = this.$scopedSlots.link, n = i.links.map(
206
- (o) => t("g", { key: o.id, attrs: { "data-depth": o.target.depth } }, [
207
- s ? s({ link: o }) ?? [] : t("path", { class: "tree-view__link", attrs: { d: o.path } })
205
+ const e = this.layout, i = this.drawn, o = this.$scopedSlots.link, a = i.links.map(
206
+ (s) => t("g", { key: s.id, attrs: { "data-depth": s.target.depth } }, [
207
+ o ? o({ link: s }) ?? [] : t("path", { class: "tree-view__link", attrs: { d: s.path } })
208
208
  ])
209
209
  ), l = i.nodes.map(
210
- (o) => t(
210
+ (s) => t(
211
211
  "div",
212
212
  {
213
- key: o.id,
214
- class: ["tree-view__node", this.nodeClassOf(o)],
215
- style: this.nodeStyle(o),
216
- attrs: { "data-depth": o.depth },
217
- on: { click: (h) => this.onNodeClick(o, h) }
213
+ key: s.id,
214
+ class: ["tree-view__node", this.nodeClassOf(s)],
215
+ style: this.nodeStyle(s),
216
+ attrs: { "data-depth": s.depth },
217
+ on: { click: (c) => this.onNodeClick(s, c) }
218
218
  },
219
- [].concat(this.renderNodeContent(t, o))
219
+ [].concat(this.renderNodeContent(t, s))
220
220
  )
221
221
  );
222
222
  return t(
@@ -236,14 +236,14 @@ const d = a.extend({
236
236
  class: "tree-view__links",
237
237
  attrs: { width: e.width, height: e.height }
238
238
  },
239
- n
239
+ a
240
240
  ),
241
241
  ...l
242
242
  ])
243
243
  ]
244
244
  );
245
245
  }
246
- }), P = a.extend({
246
+ }), j = n.extend({
247
247
  name: "BracketCard",
248
248
  props: {
249
249
  /** Матч: нужны только команды. */
@@ -255,7 +255,14 @@ const d = a.extend({
255
255
  /** Текст в ячейке счёта при техническом поражении. */
256
256
  techDefeatLabel: { type: String, default: "ТП" },
257
257
  /** Подсказка по наведению на техническое поражение. */
258
- techDefeatHint: { type: String, default: "Техническое поражение" }
258
+ techDefeatHint: { type: String, default: "Техническое поражение" },
259
+ /** У матча есть предыдущие — рисуем кнопку, которая их сворачивает. */
260
+ hasChildren: { type: Boolean, default: !1 },
261
+ /** Предыдущие матчи сейчас свёрнуты. */
262
+ collapsed: { type: Boolean, default: !1 },
263
+ /** Подписи кнопки для screen reader. */
264
+ collapseHint: { type: String, default: "Свернуть предыдущие матчи" },
265
+ expandHint: { type: String, default: "Показать предыдущие матчи" }
259
266
  },
260
267
  data() {
261
268
  return { hoveredKey: null };
@@ -266,6 +273,32 @@ const d = a.extend({
266
273
  }
267
274
  },
268
275
  methods: {
276
+ /**
277
+ * Кнопка стоит со стороны предыдущих матчей — туда же уходят линии.
278
+ * data-tv-no-pan: нажатие не должно утаскивать холст.
279
+ */
280
+ renderToggle(t) {
281
+ const e = this.collapsed ? this.expandHint : this.collapseHint;
282
+ return t(
283
+ "button",
284
+ {
285
+ class: ["tv-bracket__toggle", { "tv-bracket__toggle--collapsed": this.collapsed }],
286
+ attrs: {
287
+ type: "button",
288
+ title: e,
289
+ "aria-label": e,
290
+ "aria-expanded": String(!this.collapsed),
291
+ "data-tv-no-pan": ""
292
+ },
293
+ on: {
294
+ click: (i) => {
295
+ i.stopPropagation(), this.$emit("toggle");
296
+ }
297
+ }
298
+ },
299
+ this.collapsed ? "+" : "−"
300
+ );
301
+ },
269
302
  renderRow(t, e) {
270
303
  const i = [];
271
304
  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 +333,15 @@ const d = a.extend({
300
333
  },
301
334
  render(t) {
302
335
  const e = this.card, i = [];
303
- e.hasMyTeam && this.myTeamHint && i.push(t("div", { class: "tv-bracket__hint" }, this.myTeamHint));
304
- for (const s of e.rows) i.push(this.renderRow(t, s));
305
- return t("div", { class: ["tv-bracket", { "tv-bracket--my-team": e.hasMyTeam }] }, i);
336
+ return e.hasMyTeam && this.myTeamHint && i.push(t("div", { class: "tv-bracket__hint" }, this.myTeamHint)), this.hasChildren && i.push(this.renderToggle(t)), i.push(
337
+ t(
338
+ "div",
339
+ { class: "tv-bracket__rows" },
340
+ e.rows.map((o) => this.renderRow(t, o))
341
+ )
342
+ ), t("div", { class: ["tv-bracket", { "tv-bracket--my-team": e.hasMyTeam }] }, i);
306
343
  }
307
- }), j = a.extend({
344
+ }), A = n.extend({
308
345
  name: "BracketRounds",
309
346
  props: {
310
347
  /** Раунды по порядку — от первого к финалу. */
@@ -342,7 +379,7 @@ const d = a.extend({
342
379
  return this.isShow && (this.swipe === void 0 ? this.isNarrow : this.swipe);
343
380
  },
344
381
  base() {
345
- return H(this.options);
382
+ return V(this.options);
346
383
  },
347
384
  /**
348
385
  * Карточки матчей одной ширины, поэтому у сетки с раундами `nodeWidth` —
@@ -350,13 +387,13 @@ const d = a.extend({
350
387
  * по умолчанию, чтобы полоса не развалилась.
351
388
  */
352
389
  nodeWidth() {
353
- return typeof this.base.nodeWidth == "number" ? this.base.nodeWidth : V.nodeWidth;
390
+ return typeof this.base.nodeWidth == "number" ? this.base.nodeWidth : P.nodeWidth;
354
391
  },
355
392
  levelGap() {
356
393
  return this.isSwipe ? D(this.available, this.nodeWidth) : this.base.levelGap;
357
394
  },
358
395
  model() {
359
- return W(this.rounds, {
396
+ return H(this.rounds, {
360
397
  nodeWidth: this.nodeWidth,
361
398
  levelGap: this.levelGap
362
399
  });
@@ -370,7 +407,14 @@ const d = a.extend({
370
407
  *
371
408
  * Раунды растягиваются на ширину экрана, а уровни укладываются каждый сам по
372
409
  * себе: в дереве место второго раунда выводится из первого, поэтому отдельно
373
- * его отступ не задать. Сам отступ зависит от того, сколько матчей в раунде.
410
+ * его отступ не задать.
411
+ *
412
+ * Внутри пары матчи стоят тесно, а между парами промежуток крупнее — иначе
413
+ * весь раунд читается как один список и непонятно, кто с кем сходится дальше.
414
+ * Линии рисуются скобкой: пара сходится в одну вертикаль сразу за карточками,
415
+ * и уже от её середины идёт линия к следующему матчу. В раскладке `'stack'`
416
+ * родитель стоит не по центру детей, поэтому обычная «ступенька» выходила бы
417
+ * из верхнего матча пары, а не из середины.
374
418
  */
375
419
  gridOptions() {
376
420
  if (!this.isSwipe) return { ...this.options };
@@ -379,7 +423,10 @@ const d = a.extend({
379
423
  ...this.options,
380
424
  levelGap: this.levelGap,
381
425
  levelLayout: "stack",
382
- siblingGap: (e, i) => E(i, t)
426
+ siblingGap: t,
427
+ groupGap: (e, i) => W(i, t),
428
+ linkStyle: "bracket",
429
+ elbowOffset: this.options?.elbowOffset ?? N
383
430
  };
384
431
  }
385
432
  },
@@ -397,7 +444,7 @@ const d = a.extend({
397
444
  this.available = t;
398
445
  }),
399
446
  // Сетка перерисовывает карточки — скрытое расставляем заново.
400
- N(this.$refs.content, () => this.applyHidden())
447
+ E(this.$refs.content, () => this.applyHidden())
401
448
  ), this.applyHidden();
402
449
  },
403
450
  beforeDestroy() {
@@ -409,16 +456,16 @@ const d = a.extend({
409
456
  const t = this.$refs.content;
410
457
  if (!t) return;
411
458
  const e = this.isSwipe && this.hidePassed ? this.current : 0, i = this.model.columns.length;
412
- B(
459
+ O(
413
460
  t,
414
461
  e,
415
- (s) => O(s, i, this.mirrored)
462
+ (o) => R(o, i, this.mirrored)
416
463
  );
417
464
  },
418
465
  onScroll() {
419
466
  const t = this.$el;
420
467
  if (!t || !this.isSwipe) return;
421
- const e = this.model.columns[0]?.width ?? 0, i = R(t.scrollLeft, e, this.model.columns.length);
468
+ const e = this.model.columns[0]?.width ?? 0, i = B(t.scrollLeft, e, this.model.columns.length);
422
469
  i !== this.current && (this.current = i, this.$emit("round", i));
423
470
  },
424
471
  /**
@@ -435,7 +482,7 @@ const d = a.extend({
435
482
  if (!e) return;
436
483
  const i = this.isSwipe ? e : _(e);
437
484
  if (!i) return;
438
- const s = this.$refs.content, n = s ? S(s, t.index, t.width, this.model.offset)[0] : void 0, l = n ? C(n, i) : {
485
+ const o = this.$refs.content, a = o ? S(o, t.index, t.width, this.model.offset)[0] : void 0, l = a ? C(a, i) : {
439
486
  left: $(e, i) + t.index * t.width,
440
487
  width: t.width
441
488
  };
@@ -470,7 +517,7 @@ const d = a.extend({
470
517
  t(
471
518
  "div",
472
519
  { class: "tv-rounds__bar", style: { width: `${this.model.width}px` } },
473
- this.model.columns.map((s) => this.renderColumn(t, s))
520
+ this.model.columns.map((o) => this.renderColumn(t, o))
474
521
  )
475
522
  );
476
523
  const i = this.$scopedSlots.default?.({
@@ -497,24 +544,24 @@ const d = a.extend({
497
544
  e
498
545
  );
499
546
  }
500
- }), A = ".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}}";
501
- M(A);
502
- const K = {
547
+ }), 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;--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__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 .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;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;scroll-snap-stop:always}.tv-rounds--swipe [data-depth]{transition:opacity var(--tv-rounds-fade, .22s) ease,transform var(--tv-rounds-fade, .22s) var(--tv-rounds-collapse-ease)}.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}}";
548
+ G(F);
549
+ const Q = {
503
550
  install(t) {
504
- t.component("TreeView", G), t.component("TreeNodeCard", d), t.component("BracketCard", P), t.component("BracketRounds", j);
551
+ t.component("TreeView", M), t.component("TreeNodeCard", d), t.component("BracketCard", j), t.component("BracketRounds", A);
505
552
  }
506
553
  };
507
554
  export {
508
- P as BracketCard,
509
- j as BracketRounds,
510
- q as DEFAULT_OPTIONS,
511
- Z as MOBILE_MEDIA_QUERY,
555
+ j as BracketCard,
556
+ A as BracketRounds,
557
+ Z as DEFAULT_OPTIONS,
558
+ J as MOBILE_MEDIA_QUERY,
512
559
  d as TreeNodeCard,
513
- G as TreeView,
514
- K as TreeViewPlugin,
515
- J as bracketCardHeight,
516
- X as buildBracketCard,
517
- tt as buildBracketRounds,
518
- K as default
560
+ M as TreeView,
561
+ Q as TreeViewPlugin,
562
+ X as bracketCardHeight,
563
+ tt as buildBracketCard,
564
+ et as buildBracketRounds,
565
+ Q as default
519
566
  };
520
567
  //# 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 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 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 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 * Полоса раундов для 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 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","hint","row","content","card","BracketRounds","MOBILE_MEDIA_QUERY","resolveOptions","DEFAULT_OPTIONS","levelGapForWidth","buildBracketRounds","base","_node","count","roundSiblingGap","SWIPE_ELBOW_OFFSET","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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigplay/tree-view-vue2",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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.4.0"
38
+ "@bigplay/tree-view-core": "^0.5.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "vue": "^2.6.0 || ^2.7.0"