@sanity/orderable-document-list 1.0.4 → 1.1.1

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