@sanity/orderable-document-list 1.0.3 → 1.1.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.
package/lib/index.esm.js CHANGED
@@ -1,2 +1,770 @@
1
- function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?e(Object(i),!0).forEach((function(e){n(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}import{defineField as r,useSchema as i,Preview as o,useClient as s}from"sanity";import{LexoRank as l}from"lexorank";import{DragHandleIcon as d,ChevronUpIcon as a,ChevronDownIcon as c,GenerateIcon as u,SortIcon as p}from"@sanity/icons";import{jsxs as h,jsx as g,Fragment as m}from"react/jsx-runtime";import f,{useContext as y,useState as v,useEffect as b,useCallback as w,useMemo as k,Component as R}from"react";import{Flex as O,Box as x,Text as I,Button as S,useToast as _,Card as D,Spinner as j,Stack as E}from"@sanity/ui";import{DragDropContext as P,Droppable as L,Draggable as T}from"@hello-pangea/dnd";import{usePaneRouter as C}from"sanity/desk";const $=e=>{if(!(null==e?void 0:e.type))throw new Error("\n type must be provided.\n Example: orderRankField({type: 'category'})\n ");const{type:n}=e;return r(t(t({title:"Order Rank",readOnly:!0,hidden:!0},e),{},{name:"orderRank",type:"string",initialValue:async(e,t)=>{let{getClient:r}=t;return function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const t=e?l.parse(e):l.min();return t.genNext().genNext().value}(await r({apiVersion:"2021-09-01"}).fetch("*[_type == $type]|order(@[$order] desc)[0][$order]",{type:n,order:"orderRank"}))}}))},z={title:"Ordered",name:"ordered",by:[{field:"orderRank",direction:"asc"}]},N=f.createContext({});function V(e){let{doc:t,increment:n,entities:r,handleSelect:s,index:l,isFirst:u,isLast:p}=e;const{showIncrements:m}=y(N),f=i();return h(O,{align:"center",children:[g(x,{paddingX:3,style:{flexShrink:0},children:g(I,{size:4,children:g(d,{})})}),m&&h(O,{style:{flexShrink:0},align:"center",gap:1,paddingRight:1,children:[g(S,{padding:2,mode:"ghost",onClick:()=>n(l,l+-1,t._id,r),disabled:u,icon:a}),g(S,{padding:2,mode:"ghost",disabled:p,onClick:()=>n(l,l+1,t._id,r),icon:c})]}),g(S,{style:{width:"100%"},padding:2,mode:"bleed",onClick:e=>s(t._id,l,e.nativeEvent),children:g(O,{flex:1,align:"center",children:g(x,{flex:1,children:g(o,{layout:"default",value:t,schemaType:f.get(t._type)})})})})]})}function B(e,t){return e.orderRank<t.orderRank?-1:e.orderRank>t.orderRank?1:0}function U(){return s({apiVersion:"2021-09-01"})}function F(e){let{data:n,type:r,listIsUpdating:i,setListIsUpdating:o}=e;const s=_(),d=C(),{navigateIntent:a}=d,[c,u]=v(n);b((()=>{i||u(n)}),[n]);const[p,m]=v(""),[f,y]=v([]),R=w((()=>y([])),[y]),O=w(((e,t,n)=>{const i=f.includes(e),o=n.shiftKey,s=-1!==navigator.appVersion.indexOf("Win")?n.ctrlKey:n.metaKey;let l=[];if(!o&&!s)return a("edit",{id:e,type:r}),y([e]);if(o&&!i){const n=f[f.length-1],r=c.findIndex((e=>e._id===n)),i=t<r?t:r,o=t>r?t:r,s=c.filter(((e,t)=>t>i&&t<o)).map((e=>e._id));l=[...f,...s,e]}else l=i?f.filter((t=>t!==e)):[...f,e];return y(l)}),[y,a,c,f,r]),I=U(),S=w((async(e,t)=>{const n=I.transaction();e.forEach((e=>{let[t,r]=e;return n.patch(t,r)})),await n.commit().then((e=>{R(),m(""),o(!1),s.push({title:"".concat(1===e.results.length?"1 Document":"".concat(e.results.length," Documents")," Reordered"),status:"success",description:t})})).catch((()=>{m(""),o(!1),s.push({title:"Reordering failed",status:"error"})}))}),[I,m,R,o,s]),j=w(((e,t)=>{m("");const{source:n,destination:r,draggableId:i}=null!=e?e:{};if((null==n?void 0:n.index)===(null==r?void 0:r.index))return;if(!(null==t?void 0:t.length)||!i)return;const s=(null==f?void 0:f.length)?f:[i];if(!(null==s?void 0:s.length))return;o(!0),y(s);const{newOrder:d,patches:a,message:c}=(e=>{let{entities:t,selectedIds:n,source:r,destination:i,debug:o=!1}=e;const s=r.index,d=i.index,a=s>d,c=t.filter((e=>n.includes(e._id))),u=["Moved",1===c.length?"1 Document":"".concat(c.length," Documents"),a?"up":"down","from position","".concat(s+1," to ").concat(d+1)].join(" "),{all:p,selected:h}=t.reduce(((e,r,i)=>{var o,s,u,p;if(n.includes(r._id))return{all:e.all,selected:e.selected};if(i===d){const n=i-1,d=(null==(o=t[n])?void 0:o.orderRank)?l.parse(null==(s=t[n])?void 0:s.orderRank):l.min(),h=l.parse(t[i].orderRank),g=i+1,m=(null==(u=t[g])?void 0:u.orderRank)?l.parse(null==(p=t[g])?void 0:p.orderRank):l.max();let f=a?d.between(h):h.between(m);for(let e=0;e<c.length;e+=1)c[e].orderRank=f.value,f=a?f.between(h):f.between(m);return{all:a?[...e.all,...c,r]:[...e.all,r,...c],selected:c}}return{all:[...e.all,r],selected:e.selected}}),{all:[],selected:[]}),g=h.map((e=>[e._id,{set:{orderRank:e.orderRank}}]));return{newOrder:p.sort(B),patches:g,message:u}})({entities:t,selectedIds:s,source:n,destination:r});(null==d?void 0:d.length)&&u(d),(null==a?void 0:a.length)&&S(a,c)}),[f,m,y,S,o]),E=w((e=>{const t=e.draggableId;f.includes(t)||R(),m(t)}),[f,R,m]),$=w(((e,t,n,r)=>j({draggableId:n,source:{index:e},destination:{index:t}},r)),[j]),z=w((e=>{"Escape"===e.key&&R()}),[R]);b((()=>(window.addEventListener("keydown",z),()=>{window.removeEventListener("keydown",z)})),[z]);const N=k((()=>{if(!c.length)return[];const e=c.map((e=>e.orderRank));return e.filter(((t,n)=>e.indexOf(t)!==n))}),[c]),F=w((e=>j(e,c)),[c,j]);return g(P,{onDragStart:E,onDragEnd:F,children:g(L,{droppableId:"documentSortZone",children:e=>h("div",t(t({},e.droppableProps),{},{ref:e.innerRef,children:[c.map(((e,n)=>g(T,{draggableId:e._id,index:n,children:(r,o)=>{const s=f.includes(e._id),l=o.isDragging,d=Boolean(!l&&p&&s),a=i&&s,u=Boolean(!e.orderRank),h=(e=>{const{isDuplicate:t,isGhosting:n,isDragging:r,isSelected:i}=e;return n?"transparent":r||i?"primary":t?"caution":void 0})({isDuplicate:N.includes(e.orderRank),isGhosting:d,isDragging:l,isSelected:s});return g("div",t(t(t({ref:r.innerRef},r.draggableProps),r.dragHandleProps),{},{style:u?{opacity:.2,pointerEvents:"none"}:(m=r.draggableProps.style,y=a,t({userSelect:"none",transition:"opacity 500ms ease-in-out",opacity:y?.2:1,pointerEvents:y?"none":void 0},m)),children:g(x,{paddingBottom:1,children:g(D,{tone:h,shadow:l?2:void 0,radius:2,children:g(V,{doc:e,entities:c,handleSelect:O,increment:$,index:n,isFirst:0===n,isLast:n===c.length-1})})})}));var m,y}},"".concat(e._id,"-").concat(e.orderRank)))),e.placeholder]}))})})}function K(e){let{children:t}=e;return g(x,{padding:3,children:g(D,{padding:4,radius:2,shadow:1,tone:"caution",children:g(I,{children:t})})})}const G={};function H(e){let{type:t,filter:n,params:r=G}=e;const[i,o]=v(!0),[s,l]=v(!1),[d,a]=v([]),c=U();b((()=>{const e="*[_type == $type ".concat(n?"&& ".concat(n):"","]|order(@[$order] asc){\n _id, _type, ").concat("orderRank","\n }"),l=Object.assign(r,{type:t,order:"orderRank"});let u;const p=async()=>{c.fetch(e,l).then((e=>{const t=e.reduce(((t,n)=>{if(!n._id.startsWith("drafts.")){return e.some((e=>e._id==="drafts.".concat(n._id)))?t:[...t,n]}return[...t,n]}),[]);a(t),i&&o(!1)}))};return s||d.length||(async()=>{o(!0),await p(),u||(u=c.listen(e,l).subscribe((()=>p())))})(),()=>null==u?void 0:u.unsubscribe()}),[t]);const u=k((()=>d.length?d.filter((e=>!e.orderRank)).length:0),[d]);return i?g(O,{style:{width:"100%",height:"100%"},align:"center",justify:"center",children:g(j,{})}):h(E,{space:1,style:{overflow:"auto",height:"100%"},children:[u>0&&h(K,{children:[u,"/",d.length," Documents have no Order. Select"," ",g("strong",{children:"Reset Order"})," from the Menu above to fix."]}),g(x,{padding:1,children:g(F,{data:d,type:t,listIsUpdating:s,setListIsUpdating:l})})]})}function M(e){let{type:t,showIncrements:n,resetOrderTransaction:r,filter:o,params:s}=e;const l=_(),d=i();b((()=>{(null==r?void 0:r.title)&&(null==r?void 0:r.status)&&l.push(r)}),[r,l]);const a=k((()=>{if(!t)return h(m,{children:["No ",g("code",{children:"type"})," was configured"]});const e=d.get(t);return e?"fields"in e&&e.fields.some((e=>"orderRank"===(null==e?void 0:e.name)))?"fields"in e&&e.fields.some((e=>{var t;return"orderRank"===(null==e?void 0:e.name)&&"string"!==(null==(t=null==e?void 0:e.type)?void 0:t.name)}))?h(m,{children:[g("code",{children:"orderRank"})," field on Schema ",g("code",{children:t})," must be"," ",g("code",{children:"string"})," type"]}):"":h(m,{children:["Schema ",g("code",{children:t})," must have an ",g("code",{children:"orderRank"})," field of type"," ",g("code",{children:"string"})]}):h(m,{children:["Schema ",g("code",{children:t})," not found"]})}),[t,d]);return a?g(K,{children:a}):g(N.Provider,{value:{showIncrements:n},children:g(H,{type:t,filter:o,params:s})})}class W extends R{constructor(e){super(e),this.actionHandlers={showIncrements:()=>{this.setState((e=>({showIncrements:!e.showIncrements})))},resetOrder:async()=>{var e;this.setState((()=>({resetOrderTransaction:{status:"info",title:"Reordering started...",closable:!0}})));const t=await async function(){let e=arguments.length>1?arguments[1]:void 0;const t="*[_type == $type]|order(@[$order] asc)._id",n={type:arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",order:"orderRank"},r=await e.fetch(t,n);if(!r.length)return null;const i=e.transaction();let o=l.min();for(let e=0;e<r.length;e+=1)o=o.genNext().genNext(),i.patch(r[e],{set:{orderRank:o.value}});return i.commit().then((e=>e)).catch((e=>e))}(this.props.options.type,this.props.options.client),n=null==(e=null==t?void 0:t.results)?void 0:e.length;this.setState((()=>({resetOrderTransaction:{status:n?"success":"info",title:n?"Reordered ".concat(1===t.results.length?"Document":"Documents"):"Reordering failed",closable:!0}})))}},this.state={showIncrements:!1,resetOrderTransaction:{}}}render(){var e,t,n,r,i,o;const s=null==(t=null==(e=null==this?void 0:this.props)?void 0:e.options)?void 0:t.type;return s?g(M,{filter:null==(r=null==(n=null==this?void 0:this.props)?void 0:n.options)?void 0:r.filter,params:null==(o=null==(i=null==this?void 0:this.props)?void 0:i.options)?void 0:o.params,type:s,showIncrements:this.state.showIncrements,resetOrderTransaction:this.state.resetOrderTransaction}):null}}function X(e){var t,n;if(!(null==e?void 0:e.type)||!e.context||!e.S)throw new Error("\n type, context and S (StructureBuilder) must be provided.\n context and S are available when configuring structure.\n Example: orderableDocumentListDeskItem({type: 'category'})\n ");const{type:r,filter:i,params:o,title:s,icon:l,id:d,context:a,S:c}=e,{schema:h,getClient:g}=a,m=g({apiVersion:"2021-09-01"}),f=null!=s?s:"Orderable ".concat(r),y=null!=d?d:"orderable-".concat(r),v=null!=l?l:p,b=null!=(n=null==(t=h.get(r))?void 0:t.title)?n:r;return c.listItem().title(f).id(y).icon(v).child(Object.assign(c.documentTypeList(r).serialize(),{__preserveInstance:!0,key:y,type:"component",component:W,options:{type:r,filter:i,params:o,client:m},menuItems:[c.menuItem().title("Create new ".concat(b)).intent({type:"create",params:{type:r}}).serialize(),c.menuItem().title("Reset Order").icon(u).action("resetOrder").serialize(),c.menuItem().title("Show Increments").icon(p).action("showIncrements").serialize()]})).serialize()}export{$ as orderRankField,z as orderRankOrdering,X as orderableDocumentListDeskItem};
1
+ import { defineField, useSchema, PreviewCard, Preview, useClient } from 'sanity';
2
+ import { LexoRank } from 'lexorank';
3
+ import { DragHandleIcon, ChevronUpIcon, ChevronDownIcon, GenerateIcon, SortIcon } from '@sanity/icons';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import React, { useContext, useMemo, useState, useEffect, useCallback, Component } from 'react';
6
+ import { Flex, Box, Text, Button, Card, AvatarCounter, useToast, Spinner, Container, Stack } from '@sanity/ui';
7
+ import { useListeningQuery, Feedback } from 'sanity-plugin-utils';
8
+ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
9
+ import { usePaneRouter } from 'sanity/desk';
10
+ const ORDER_FIELD_NAME = "orderRank";
11
+ function initialRank() {
12
+ let lastRankValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
13
+ const lastRank = lastRankValue ? LexoRank.parse(lastRankValue) : LexoRank.min();
14
+ const nextRank = lastRank.genNext().genNext();
15
+ return nextRank.toString();
16
+ }
17
+ const orderRankField = config => {
18
+ if (!(config == null ? void 0 : config.type)) {
19
+ throw new Error("\n type must be provided.\n Example: orderRankField({type: 'category'})\n ");
20
+ }
21
+ const {
22
+ type
23
+ } = config;
24
+ return defineField({
25
+ title: "Order Rank",
26
+ readOnly: true,
27
+ hidden: true,
28
+ ...config,
29
+ name: ORDER_FIELD_NAME,
30
+ type: "string",
31
+ initialValue: async (p, _ref) => {
32
+ let {
33
+ getClient
34
+ } = _ref;
35
+ const lastDocOrderRank = await getClient({
36
+ apiVersion: "2021-09-01"
37
+ }).fetch("*[_type == $type]|order(@[$order] desc)[0][$order]", {
38
+ type,
39
+ order: ORDER_FIELD_NAME
40
+ });
41
+ return initialRank(lastDocOrderRank);
42
+ }
43
+ });
44
+ };
45
+ const orderRankOrdering = {
46
+ title: "Ordered",
47
+ name: "ordered",
48
+ by: [{
49
+ field: ORDER_FIELD_NAME,
50
+ direction: "asc"
51
+ }]
52
+ };
53
+ const OrderableContext = React.createContext({});
54
+ function Document(_ref2) {
55
+ let {
56
+ doc,
57
+ increment,
58
+ entities,
59
+ index,
60
+ isFirst,
61
+ isLast,
62
+ dragBadge
63
+ } = _ref2;
64
+ var _a, _b;
65
+ const {
66
+ showIncrements
67
+ } = useContext(OrderableContext);
68
+ const schema = useSchema();
69
+ const router = usePaneRouter();
70
+ const {
71
+ ChildLink,
72
+ groupIndex,
73
+ routerPanesState
74
+ } = router;
75
+ const currentDoc = ((_b = (_a = routerPanesState[groupIndex + 1]) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) || false;
76
+ const pressed = currentDoc === doc._id || currentDoc === doc._id.replace("drafts.", "");
77
+ const selected = pressed && routerPanesState.length === groupIndex + 2;
78
+ const Link = useMemo(() => function LinkComponent(linkProps) {
79
+ return /* @__PURE__ */jsx(ChildLink, {
80
+ ...linkProps,
81
+ childId: doc._id
82
+ });
83
+ }, [ChildLink, doc._id]);
84
+ return /* @__PURE__ */jsx(PreviewCard, {
85
+ __unstable_focusRing: true,
86
+ as: Link,
87
+ "data-as": "a",
88
+ "data-ui": "PaneItem",
89
+ radius: 2,
90
+ pressed,
91
+ selected,
92
+ sizing: "border",
93
+ tabIndex: -1,
94
+ tone: "inherit",
95
+ width: "100%",
96
+ flex: 1,
97
+ children: /* @__PURE__ */jsxs(Flex, {
98
+ align: "center",
99
+ children: [/* @__PURE__ */jsx(Box, {
100
+ paddingX: 3,
101
+ style: {
102
+ flexShrink: 0
103
+ },
104
+ children: /* @__PURE__ */jsx(Text, {
105
+ size: 2,
106
+ children: /* @__PURE__ */jsx(DragHandleIcon, {
107
+ cursor: "grab"
108
+ })
109
+ })
110
+ }), showIncrements && /* @__PURE__ */jsxs(Flex, {
111
+ style: {
112
+ flexShrink: 0
113
+ },
114
+ align: "center",
115
+ gap: 1,
116
+ paddingRight: 1,
117
+ children: [/* @__PURE__ */jsx(Button, {
118
+ padding: 2,
119
+ mode: "ghost",
120
+ onClick: () => increment(index, index + -1, doc._id, entities),
121
+ disabled: isFirst,
122
+ icon: ChevronUpIcon
123
+ }), /* @__PURE__ */jsx(Button, {
124
+ padding: 2,
125
+ mode: "ghost",
126
+ disabled: isLast,
127
+ onClick: () => increment(index, index + 1, doc._id, entities),
128
+ icon: ChevronDownIcon
129
+ })]
130
+ }), /* @__PURE__ */jsx(Box, {
131
+ style: {
132
+ width: "100%"
133
+ },
134
+ padding: 2,
135
+ children: /* @__PURE__ */jsx(Flex, {
136
+ flex: 1,
137
+ align: "center",
138
+ children: /* @__PURE__ */jsx(Preview, {
139
+ layout: "default",
140
+ value: doc,
141
+ schemaType: schema.get(doc._type)
142
+ })
143
+ })
144
+ }), dragBadge && /* @__PURE__ */jsx(Card, {
145
+ tone: "default",
146
+ marginRight: 4,
147
+ radius: 5,
148
+ children: /* @__PURE__ */jsx(AvatarCounter, {
149
+ count: dragBadge
150
+ })
151
+ })]
152
+ })
153
+ });
154
+ }
155
+ function lexicographicalSort(a, b) {
156
+ if (!a[ORDER_FIELD_NAME] || !b[ORDER_FIELD_NAME]) {
157
+ return 0;
158
+ } else if (a[ORDER_FIELD_NAME] < b[ORDER_FIELD_NAME]) {
159
+ return -1;
160
+ } else if (a[ORDER_FIELD_NAME] > b[ORDER_FIELD_NAME]) {
161
+ return 1;
162
+ }
163
+ return 0;
164
+ }
165
+ const reorderDocuments = _ref3 => {
166
+ let {
167
+ entities,
168
+ selectedIds,
169
+ source,
170
+ destination
171
+ } = _ref3;
172
+ const startIndex = source.index;
173
+ const endIndex = destination.index;
174
+ const isMovingUp = startIndex > endIndex;
175
+ const selectedItems = entities.filter(item => selectedIds.includes(item._id));
176
+ const message = ["Moved", selectedItems.length === 1 ? "1 document" : "".concat(selectedItems.length, " documents"), isMovingUp ? "up" : "down", "from position", "".concat(startIndex + 1, " to ").concat(endIndex + 1)].join(" ");
177
+ const {
178
+ all,
179
+ selected
180
+ } = entities.reduce((acc, cur, curIndex) => {
181
+ var _a, _b, _c, _d;
182
+ if (selectedIds.includes(cur._id)) {
183
+ return {
184
+ all: acc.all,
185
+ selected: acc.selected
186
+ };
187
+ }
188
+ if (curIndex === endIndex) {
189
+ const prevIndex = curIndex - 1;
190
+ const prevRank = ((_a = entities[prevIndex]) == null ? void 0 : _a[ORDER_FIELD_NAME]) ? LexoRank.parse((_b = entities[prevIndex]) == null ? void 0 : _b[ORDER_FIELD_NAME]) : LexoRank.min();
191
+ const curRank = LexoRank.parse(entities[curIndex][ORDER_FIELD_NAME]);
192
+ const nextIndex = curIndex + 1;
193
+ const nextRank = ((_c = entities[nextIndex]) == null ? void 0 : _c[ORDER_FIELD_NAME]) ? LexoRank.parse((_d = entities[nextIndex]) == null ? void 0 : _d[ORDER_FIELD_NAME]) : LexoRank.max();
194
+ let betweenRank = isMovingUp ? prevRank.between(curRank) : curRank.between(nextRank);
195
+ for (let selectedIndex = 0; selectedIndex < selectedItems.length; selectedIndex += 1) {
196
+ selectedItems[selectedIndex][ORDER_FIELD_NAME] = betweenRank.toString();
197
+ betweenRank = isMovingUp ? betweenRank.between(curRank) : betweenRank.between(nextRank);
198
+ }
199
+ return {
200
+ // The `all` array gets sorted by order field later anyway
201
+ // so that this probably isn't necessary ¯\_(ツ)_/¯
202
+ all: isMovingUp ? [...acc.all, ...selectedItems, cur] : [...acc.all, cur, ...selectedItems],
203
+ selected: selectedItems
204
+ };
205
+ }
206
+ return {
207
+ all: [...acc.all, cur],
208
+ selected: acc.selected
209
+ };
210
+ }, {
211
+ all: [],
212
+ selected: []
213
+ });
214
+ const patches = selected.flatMap(doc => {
215
+ const docPatches = [[doc._id, {
216
+ set: {
217
+ [ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME]
218
+ }
219
+ }]];
220
+ if (doc._id.startsWith("drafts.") && doc.hasPublished) {
221
+ docPatches.push([doc._id.replace("drafts.", ""), {
222
+ set: {
223
+ [ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME]
224
+ }
225
+ }]);
226
+ }
227
+ return docPatches;
228
+ });
229
+ const allSorted = all.sort(lexicographicalSort);
230
+ return {
231
+ newOrder: allSorted,
232
+ patches,
233
+ message
234
+ };
235
+ };
236
+ function useSanityClient() {
237
+ return useClient({
238
+ apiVersion: "2021-09-01"
239
+ });
240
+ }
241
+ const getItemStyle = (draggableStyle, itemIsUpdating) => ({
242
+ userSelect: "none",
243
+ transition: "opacity 500ms ease-in-out",
244
+ opacity: itemIsUpdating ? 0.2 : 1,
245
+ pointerEvents: itemIsUpdating ? "none" : void 0,
246
+ ...draggableStyle
247
+ });
248
+ const cardTone = settings => {
249
+ const {
250
+ isDuplicate,
251
+ isGhosting,
252
+ isDragging,
253
+ isSelected
254
+ } = settings;
255
+ if (isGhosting) return "transparent";
256
+ if (isDragging || isSelected) return "primary";
257
+ if (isDuplicate) return "caution";
258
+ return void 0;
259
+ };
260
+ function DraggableList(_ref4) {
261
+ let {
262
+ data,
263
+ listIsUpdating,
264
+ setListIsUpdating
265
+ } = _ref4;
266
+ var _a, _b;
267
+ const toast = useToast();
268
+ const router = usePaneRouter();
269
+ const {
270
+ groupIndex,
271
+ routerPanesState
272
+ } = router;
273
+ const currentDoc = ((_b = (_a = routerPanesState[groupIndex + 1]) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) || false;
274
+ const [orderedData, setOrderedData] = useState(data);
275
+ useEffect(() => {
276
+ if (!listIsUpdating) setOrderedData(data);
277
+ }, [data]);
278
+ const [draggingId, setDraggingId] = useState("");
279
+ const [selectedIds, setSelectedIds] = useState(currentDoc ? [currentDoc] : []);
280
+ const clearSelected = useCallback(() => setSelectedIds([]), [setSelectedIds]);
281
+ const handleSelect = useCallback((clickedId, index, nativeEvent) => {
282
+ const isSelected = selectedIds.includes(clickedId);
283
+ const selectMultiple = nativeEvent.shiftKey;
284
+ const isUsingWindows = navigator.appVersion.indexOf("Win") !== -1;
285
+ const selectAdditional = isUsingWindows ? nativeEvent.ctrlKey : nativeEvent.metaKey;
286
+ let updatedIds = [];
287
+ if (!selectMultiple && !selectAdditional) {
288
+ return setSelectedIds([clickedId]);
289
+ }
290
+ if (selectMultiple) {
291
+ nativeEvent.preventDefault();
292
+ }
293
+ if (selectMultiple && !isSelected) {
294
+ const lastSelectedId = selectedIds[selectedIds.length - 1];
295
+ const lastSelectedIndex = orderedData.findIndex(item => item._id === lastSelectedId);
296
+ const firstSelected = index < lastSelectedIndex ? index : lastSelectedIndex;
297
+ const lastSelected = index > lastSelectedIndex ? index : lastSelectedIndex;
298
+ const betweenIds = orderedData.filter((item, itemIndex) => itemIndex > firstSelected && itemIndex < lastSelected).map(item => item._id);
299
+ updatedIds = [...selectedIds, ...betweenIds, clickedId];
300
+ } else if (isSelected) {
301
+ updatedIds = selectedIds.filter(id => id !== clickedId);
302
+ } else {
303
+ updatedIds = [...selectedIds, clickedId];
304
+ }
305
+ return setSelectedIds(updatedIds);
306
+ }, [setSelectedIds, orderedData, selectedIds]);
307
+ const client = useSanityClient();
308
+ const transactPatches = useCallback(async (patches, message) => {
309
+ const transaction = client.transaction();
310
+ patches.forEach(_ref5 => {
311
+ let [docId, ops] = _ref5;
312
+ return transaction.patch(docId, ops);
313
+ });
314
+ await transaction.commit().then(updated => {
315
+ clearSelected();
316
+ setDraggingId("");
317
+ setListIsUpdating(false);
318
+ toast.push({
319
+ title: "".concat(updated.results.length === 1 ? "1 Document" : "".concat(updated.results.length, " Documents"), " Reordered"),
320
+ status: "success",
321
+ description: message
322
+ });
323
+ }).catch(() => {
324
+ setDraggingId("");
325
+ setListIsUpdating(false);
326
+ toast.push({
327
+ title: "Reordering failed",
328
+ status: "error"
329
+ });
330
+ });
331
+ }, [client, setDraggingId, clearSelected, setListIsUpdating, toast]);
332
+ const handleDragEnd = useCallback((result, entities) => {
333
+ setDraggingId("");
334
+ const {
335
+ source,
336
+ destination,
337
+ draggableId
338
+ } = result != null ? result : {};
339
+ if ((source == null ? void 0 : source.index) === (destination == null ? void 0 : destination.index)) return;
340
+ if (!(entities == null ? void 0 : entities.length) || !draggableId) return;
341
+ const effectedIds = (selectedIds == null ? void 0 : selectedIds.length) ? selectedIds : [draggableId];
342
+ if (!(effectedIds == null ? void 0 : effectedIds.length)) return;
343
+ setListIsUpdating(true);
344
+ setSelectedIds(effectedIds);
345
+ const {
346
+ newOrder,
347
+ patches,
348
+ message
349
+ } = reorderDocuments({
350
+ entities,
351
+ selectedIds: effectedIds,
352
+ source,
353
+ destination
354
+ });
355
+ if (newOrder == null ? void 0 : newOrder.length) {
356
+ setOrderedData(newOrder);
357
+ }
358
+ if (patches == null ? void 0 : patches.length) {
359
+ transactPatches(patches, message);
360
+ }
361
+ }, [selectedIds, setDraggingId, setSelectedIds, transactPatches, setListIsUpdating]);
362
+ const handleDragStart = useCallback(start => {
363
+ const id = start.draggableId;
364
+ const selected = selectedIds.includes(id);
365
+ if (!selected) clearSelected();
366
+ setDraggingId(id);
367
+ }, [selectedIds, clearSelected, setDraggingId]);
368
+ const incrementIndex = useCallback((shiftFrom, shiftTo, id, entities) => {
369
+ const result = {
370
+ draggableId: id,
371
+ source: {
372
+ index: shiftFrom
373
+ },
374
+ destination: {
375
+ index: shiftTo
376
+ }
377
+ };
378
+ return handleDragEnd(result, entities);
379
+ }, [handleDragEnd]);
380
+ const onWindowKeyDown = useCallback(event => {
381
+ if (event.key === "Escape") {
382
+ clearSelected();
383
+ }
384
+ }, [clearSelected]);
385
+ useEffect(() => {
386
+ window.addEventListener("keydown", onWindowKeyDown);
387
+ return () => {
388
+ window.removeEventListener("keydown", onWindowKeyDown);
389
+ };
390
+ }, [onWindowKeyDown]);
391
+ const duplicateOrders = useMemo(() => {
392
+ if (!orderedData.length) return [];
393
+ const orderField = orderedData.map(item => item[ORDER_FIELD_NAME]);
394
+ return orderField.filter((item, index) => orderField.indexOf(item) !== index);
395
+ }, [orderedData]);
396
+ const onDragEnd = useCallback(result => handleDragEnd(result, orderedData), [orderedData, handleDragEnd]);
397
+ return /* @__PURE__ */jsx(DragDropContext, {
398
+ onDragStart: handleDragStart,
399
+ onDragEnd,
400
+ children: /* @__PURE__ */jsx(Droppable, {
401
+ droppableId: "documentSortZone",
402
+ children: provided => /* @__PURE__ */jsxs("div", {
403
+ ...provided.droppableProps,
404
+ ref: provided.innerRef,
405
+ children: [orderedData.map((item, index) => /* @__PURE__ */jsx(Draggable, {
406
+ draggableId: item._id,
407
+ index,
408
+ children: (innerProvided, innerSnapshot) => {
409
+ const isSelected = selectedIds.includes(item._id);
410
+ const isDragging = innerSnapshot.isDragging;
411
+ const isGhosting = Boolean(!isDragging && draggingId && isSelected);
412
+ const isUpdating = listIsUpdating && isSelected;
413
+ const isDisabled = Boolean(!item[ORDER_FIELD_NAME]);
414
+ const isDuplicate = duplicateOrders.includes(item[ORDER_FIELD_NAME]);
415
+ const tone = cardTone({
416
+ isDuplicate,
417
+ isGhosting,
418
+ isDragging,
419
+ isSelected
420
+ });
421
+ const selectedCount = selectedIds.length;
422
+ const dragBadge = isDragging && selectedCount > 1 ? selectedCount : false;
423
+ return /* @__PURE__ */jsx("div", {
424
+ ref: innerProvided.innerRef,
425
+ ...innerProvided.draggableProps,
426
+ ...innerProvided.dragHandleProps,
427
+ style: isDisabled ? {
428
+ opacity: 0.2,
429
+ pointerEvents: "none"
430
+ } : getItemStyle(innerProvided.draggableProps.style, isUpdating),
431
+ children: /* @__PURE__ */jsx(Box, {
432
+ paddingBottom: 1,
433
+ children: /* @__PURE__ */jsx(Card, {
434
+ tone,
435
+ shadow: isDragging ? 2 : void 0,
436
+ radius: 2,
437
+ onClick: e => handleSelect(item._id, index, e.nativeEvent),
438
+ children: /* @__PURE__ */jsx(Document, {
439
+ doc: item,
440
+ entities: orderedData,
441
+ increment: incrementIndex,
442
+ index,
443
+ isFirst: index === 0,
444
+ isLast: index === orderedData.length - 1,
445
+ dragBadge
446
+ })
447
+ })
448
+ })
449
+ });
450
+ }
451
+ }, "".concat(item._id, "-").concat(item[ORDER_FIELD_NAME]))), provided.placeholder]
452
+ })
453
+ })
454
+ });
455
+ }
456
+ const DEFAULT_PARAMS = {};
457
+ function DocumentListQuery(_ref6) {
458
+ let {
459
+ type,
460
+ filter,
461
+ params = DEFAULT_PARAMS
462
+ } = _ref6;
463
+ const [listIsUpdating, setListIsUpdating] = useState(false);
464
+ const [data, setData] = useState([]);
465
+ const query = "*[_type == $type ".concat(filter ? "&& ".concat(filter) : "", "]|order(@[$order] asc){\n _id, _type, ").concat(ORDER_FIELD_NAME, "\n }");
466
+ const queryParams = {
467
+ ...params,
468
+ type,
469
+ order: ORDER_FIELD_NAME
470
+ };
471
+ const {
472
+ data: queryData,
473
+ loading,
474
+ error
475
+ } = useListeningQuery(query, {
476
+ params: queryParams,
477
+ initialValue: []
478
+ });
479
+ useEffect(() => {
480
+ if (queryData) {
481
+ const filteredDocuments = queryData.reduce((acc, cur) => {
482
+ if (!cur._id.startsWith("drafts.")) {
483
+ const alsoHasDraft = queryData.some(doc => doc._id === "drafts.".concat(cur._id));
484
+ return alsoHasDraft ? acc : [...acc, cur];
485
+ }
486
+ cur.hasPublished = queryData.some(doc => doc._id === cur._id.replace("drafts.", ""));
487
+ return [...acc, cur];
488
+ }, []);
489
+ setData(filteredDocuments);
490
+ } else {
491
+ setData([]);
492
+ }
493
+ }, [queryData]);
494
+ const unorderedDataCount = useMemo(() => (data == null ? void 0 : data.length) ? data.filter(doc => !doc[ORDER_FIELD_NAME]).length : 0, [data]);
495
+ if (loading) {
496
+ return /* @__PURE__ */jsx(Flex, {
497
+ style: {
498
+ width: "100%",
499
+ height: "100%"
500
+ },
501
+ align: "center",
502
+ justify: "center",
503
+ children: /* @__PURE__ */jsx(Spinner, {})
504
+ });
505
+ }
506
+ if (error) {
507
+ return /* @__PURE__ */jsx(Box, {
508
+ padding: 2,
509
+ children: /* @__PURE__ */jsx(Feedback, {
510
+ tone: "critical",
511
+ title: "There was an error",
512
+ description: "Please try again later"
513
+ })
514
+ });
515
+ }
516
+ if (!data || (data == null ? void 0 : data.length) == 0) return /* @__PURE__ */jsx(Flex, {
517
+ align: "center",
518
+ direction: "column",
519
+ height: "fill",
520
+ justify: "center",
521
+ children: /* @__PURE__ */jsx(Container, {
522
+ width: 1,
523
+ children: /* @__PURE__ */jsx(Box, {
524
+ paddingX: 4,
525
+ paddingY: 5,
526
+ children: /* @__PURE__ */jsx(Text, {
527
+ align: "center",
528
+ muted: true,
529
+ children: "No documents of this type"
530
+ })
531
+ })
532
+ })
533
+ });
534
+ return /* @__PURE__ */jsx(Stack, {
535
+ space: 1,
536
+ style: {
537
+ overflow: "auto",
538
+ height: "100%"
539
+ },
540
+ children: /* @__PURE__ */jsxs(Box, {
541
+ padding: 2,
542
+ children: [unorderedDataCount > 0 && /* @__PURE__ */jsx(Box, {
543
+ marginBottom: 2,
544
+ children: /* @__PURE__ */jsx(Feedback, {
545
+ tone: "caution",
546
+ description: /* @__PURE__ */jsxs(Fragment, {
547
+ children: [unorderedDataCount, "/", data == null ? void 0 : data.length, " documents have no order. Select", " ", /* @__PURE__ */jsx("strong", {
548
+ children: "Reset Order"
549
+ }), " from the menu above to fix."]
550
+ })
551
+ })
552
+ }), /* @__PURE__ */jsx(DraggableList, {
553
+ data,
554
+ listIsUpdating,
555
+ setListIsUpdating
556
+ })]
557
+ })
558
+ });
559
+ }
560
+ function DocumentListWrapper(_ref7) {
561
+ let {
562
+ type,
563
+ showIncrements,
564
+ resetOrderTransaction,
565
+ filter,
566
+ params
567
+ } = _ref7;
568
+ const toast = useToast();
569
+ const schema = useSchema();
570
+ useEffect(() => {
571
+ if ((resetOrderTransaction == null ? void 0 : resetOrderTransaction.title) && (resetOrderTransaction == null ? void 0 : resetOrderTransaction.status)) {
572
+ toast.push(resetOrderTransaction);
573
+ }
574
+ }, [resetOrderTransaction, toast]);
575
+ const schemaIsInvalid = useMemo(() => {
576
+ if (!type) {
577
+ return /* @__PURE__ */jsxs(Fragment, {
578
+ children: ["No ", /* @__PURE__ */jsx("code", {
579
+ children: "type"
580
+ }), " was configured"]
581
+ });
582
+ }
583
+ const typeSchema = schema.get(type);
584
+ if (!typeSchema) {
585
+ return /* @__PURE__ */jsxs(Fragment, {
586
+ children: ["Schema ", /* @__PURE__ */jsx("code", {
587
+ children: type
588
+ }), " not found"]
589
+ });
590
+ }
591
+ if (!("fields" in typeSchema) || !typeSchema.fields.some(field => (field == null ? void 0 : field.name) === ORDER_FIELD_NAME)) {
592
+ return /* @__PURE__ */jsxs(Fragment, {
593
+ children: ["Schema ", /* @__PURE__ */jsx("code", {
594
+ children: type
595
+ }), " must have an ", /* @__PURE__ */jsx("code", {
596
+ children: ORDER_FIELD_NAME
597
+ }), " field of type", " ", /* @__PURE__ */jsx("code", {
598
+ children: "string"
599
+ })]
600
+ });
601
+ }
602
+ if ("fields" in typeSchema && typeSchema.fields.some(field => {
603
+ var _a;
604
+ return (field == null ? void 0 : field.name) === ORDER_FIELD_NAME && ((_a = field == null ? void 0 : field.type) == null ? void 0 : _a.name) !== "string";
605
+ })) {
606
+ return /* @__PURE__ */jsxs(Fragment, {
607
+ children: [/* @__PURE__ */jsx("code", {
608
+ children: ORDER_FIELD_NAME
609
+ }), " field on Schema ", /* @__PURE__ */jsx("code", {
610
+ children: type
611
+ }), " must be", " ", /* @__PURE__ */jsx("code", {
612
+ children: "string"
613
+ }), " type"]
614
+ });
615
+ }
616
+ return "";
617
+ }, [type, schema]);
618
+ if (schemaIsInvalid) {
619
+ return /* @__PURE__ */jsx(Box, {
620
+ padding: 2,
621
+ children: /* @__PURE__ */jsx(Feedback, {
622
+ description: schemaIsInvalid,
623
+ tone: "caution"
624
+ })
625
+ });
626
+ }
627
+ return /* @__PURE__ */jsx(OrderableContext.Provider, {
628
+ value: {
629
+ showIncrements
630
+ },
631
+ children: /* @__PURE__ */jsx(DocumentListQuery, {
632
+ type,
633
+ filter,
634
+ params
635
+ })
636
+ });
637
+ }
638
+ async function resetOrder() {
639
+ let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
640
+ let client = arguments.length > 1 ? arguments[1] : undefined;
641
+ const query = "*[_type == $type]|order(@[$order] asc)._id";
642
+ const queryParams = {
643
+ type,
644
+ order: ORDER_FIELD_NAME
645
+ };
646
+ const documents = await client.fetch(query, queryParams);
647
+ if (!documents.length) {
648
+ return null;
649
+ }
650
+ const transaction = client.transaction();
651
+ let aLexoRank = LexoRank.min();
652
+ for (let index = 0; index < documents.length; index += 1) {
653
+ aLexoRank = aLexoRank.genNext().genNext();
654
+ transaction.patch(documents[index], {
655
+ set: {
656
+ [ORDER_FIELD_NAME]: aLexoRank.toString()
657
+ }
658
+ });
659
+ }
660
+ return transaction.commit().then(update => update).catch(err => err);
661
+ }
662
+ var __defProp = Object.defineProperty;
663
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
664
+ enumerable: true,
665
+ configurable: true,
666
+ writable: true,
667
+ value
668
+ }) : obj[key] = value;
669
+ var __publicField = (obj, key, value) => {
670
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
671
+ return value;
672
+ };
673
+ class OrderableDocumentList extends Component {
674
+ constructor(props) {
675
+ super(props);
676
+ __publicField(this, "actionHandlers", {
677
+ showIncrements: () => {
678
+ this.setState(state => ({
679
+ showIncrements: !state.showIncrements
680
+ }));
681
+ },
682
+ resetOrder: async () => {
683
+ var _a;
684
+ this.setState(() => ({
685
+ resetOrderTransaction: {
686
+ status: "info",
687
+ title: "Reordering started...",
688
+ closable: true
689
+ }
690
+ }));
691
+ const update = await resetOrder(this.props.options.type, this.props.options.client);
692
+ const reorderWasSuccessful = (_a = update == null ? void 0 : update.results) == null ? void 0 : _a.length;
693
+ this.setState(() => ({
694
+ resetOrderTransaction: {
695
+ status: reorderWasSuccessful ? "success" : "info",
696
+ title: reorderWasSuccessful ? "Reordered ".concat(update.results.length === 1 ? "Document" : "Documents") : "Reordering failed",
697
+ closable: true
698
+ }
699
+ }));
700
+ }
701
+ });
702
+ this.state = {
703
+ showIncrements: false,
704
+ resetOrderTransaction: {}
705
+ };
706
+ }
707
+ render() {
708
+ var _a, _b, _c, _d, _e, _f;
709
+ const type = (_b = (_a = this == null ? void 0 : this.props) == null ? void 0 : _a.options) == null ? void 0 : _b.type;
710
+ if (!type) {
711
+ return null;
712
+ }
713
+ return /* @__PURE__ */jsx(DocumentListWrapper, {
714
+ filter: (_d = (_c = this == null ? void 0 : this.props) == null ? void 0 : _c.options) == null ? void 0 : _d.filter,
715
+ params: (_f = (_e = this == null ? void 0 : this.props) == null ? void 0 : _e.options) == null ? void 0 : _f.params,
716
+ type,
717
+ showIncrements: this.state.showIncrements,
718
+ resetOrderTransaction: this.state.resetOrderTransaction
719
+ });
720
+ }
721
+ }
722
+ function orderableDocumentListDeskItem(config) {
723
+ var _a, _b;
724
+ if (!(config == null ? void 0 : config.type) || !config.context || !config.S) {
725
+ throw new Error("\n type, context and S (StructureBuilder) must be provided.\n context and S are available when configuring structure.\n Example: orderableDocumentListDeskItem({type: 'category'})\n ");
726
+ }
727
+ const {
728
+ type,
729
+ filter,
730
+ params,
731
+ title,
732
+ icon,
733
+ id,
734
+ context,
735
+ S
736
+ } = config;
737
+ const {
738
+ schema,
739
+ getClient
740
+ } = context;
741
+ const client = getClient({
742
+ apiVersion: "2021-09-01"
743
+ });
744
+ const listTitle = title != null ? title : "Orderable ".concat(type);
745
+ const listId = id != null ? id : "orderable-".concat(type);
746
+ const listIcon = icon != null ? icon : SortIcon;
747
+ const typeTitle = (_b = (_a = schema.get(type)) == null ? void 0 : _a.title) != null ? _b : type;
748
+ return S.listItem().title(listTitle).id(listId).icon(listIcon).child(Object.assign(S.documentTypeList(type).serialize(), {
749
+ // Prevents the component from re-rendering when switching documents
750
+ __preserveInstance: true,
751
+ // Prevents the component from NOT re-rendering when switching listItems
752
+ key: listId,
753
+ type: "component",
754
+ component: OrderableDocumentList,
755
+ options: {
756
+ type,
757
+ filter,
758
+ params,
759
+ client
760
+ },
761
+ menuItems: [S.menuItem().title("Create new ".concat(typeTitle)).intent({
762
+ type: "create",
763
+ params: {
764
+ type
765
+ }
766
+ }).serialize(), S.menuItem().title("Reset Order").icon(GenerateIcon).action("resetOrder").serialize(), S.menuItem().title("Toggle Increments").icon(SortIcon).action("showIncrements").serialize()]
767
+ })).serialize();
768
+ }
769
+ export { orderRankField, orderRankOrdering, orderableDocumentListDeskItem };
2
770
  //# sourceMappingURL=index.esm.js.map