@blockslides/extension-add-slide-button 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3097,6 +3097,7 @@ var PluginKey = class {
3097
3097
  // src/add-slide-button.ts
3098
3098
  var SlideNodeView = class {
3099
3099
  constructor(node, view, getPos, options) {
3100
+ var _a;
3100
3101
  this.node = node;
3101
3102
  this.view = view;
3102
3103
  this.getPos = getPos;
@@ -3122,21 +3123,37 @@ var SlideNodeView = class {
3122
3123
  this.contentDOM.className = "slide";
3123
3124
  this.contentDOM.setAttribute("data-node-type", "slide");
3124
3125
  }
3125
- this.button = document.createElement("button");
3126
- this.button.className = "add-slide-button";
3127
- this.button.innerHTML = options.content;
3128
- this.button.setAttribute("type", "button");
3129
- this.button.contentEditable = "false";
3130
- if (options.buttonStyle) {
3131
- Object.entries(options.buttonStyle).forEach(([key, value]) => {
3132
- const camelKey = key.replace(
3133
- /-([a-z])/g,
3134
- (_, letter) => letter.toUpperCase()
3135
- );
3136
- this.button.style[camelKey] = value;
3137
- });
3138
- }
3139
- this.button.onclick = (event) => {
3126
+ const createButton = (html) => {
3127
+ const btn = document.createElement("button");
3128
+ btn.className = "add-slide-button";
3129
+ btn.innerHTML = html;
3130
+ btn.setAttribute("type", "button");
3131
+ btn.contentEditable = "false";
3132
+ if (options.buttonStyle) {
3133
+ Object.entries(options.buttonStyle).forEach(([key, value]) => {
3134
+ if (key.startsWith("--")) {
3135
+ btn.style.setProperty(key, value);
3136
+ return;
3137
+ }
3138
+ const camelKey = key.replace(
3139
+ /-([a-z])/g,
3140
+ (_, letter) => letter.toUpperCase()
3141
+ );
3142
+ btn.style[camelKey] = value;
3143
+ });
3144
+ }
3145
+ return btn;
3146
+ };
3147
+ const insertEmptySlide = (pos) => {
3148
+ const schema = this.view.state.schema;
3149
+ const slideType = schema.nodes.slide;
3150
+ const paragraphType = schema.nodes.paragraph;
3151
+ const slideContent = paragraphType.create();
3152
+ const slide = slideType.create(null, slideContent);
3153
+ const tr = this.view.state.tr.insert(pos + this.node.nodeSize, slide);
3154
+ this.view.dispatch(tr);
3155
+ };
3156
+ const handlePlusClick = (event) => {
3140
3157
  event.preventDefault();
3141
3158
  event.stopPropagation();
3142
3159
  const pos = this.getPos();
@@ -3153,17 +3170,117 @@ var SlideNodeView = class {
3153
3170
  event
3154
3171
  });
3155
3172
  } else {
3156
- const schema = this.view.state.schema;
3157
- const slideType = schema.nodes.slide;
3158
- const paragraphType = schema.nodes.paragraph;
3159
- const slideContent = paragraphType.create();
3160
- const slide = slideType.create(null, slideContent);
3161
- const tr = this.view.state.tr.insert(pos + this.node.nodeSize, slide);
3162
- this.view.dispatch(tr);
3173
+ insertEmptySlide(pos);
3174
+ }
3175
+ };
3176
+ const openPresetModal = (pos) => {
3177
+ if (this.presetModal) {
3178
+ this.presetModal.remove();
3179
+ }
3180
+ const overlay = document.createElement("div");
3181
+ overlay.className = "add-slide-preset-modal";
3182
+ const dialog = document.createElement("div");
3183
+ dialog.className = "add-slide-preset-dialog";
3184
+ if (options.presetBackground) {
3185
+ dialog.style.setProperty("--add-slide-preset-bg", options.presetBackground);
3163
3186
  }
3187
+ if (options.presetForeground) {
3188
+ dialog.style.setProperty("--add-slide-preset-fg", options.presetForeground);
3189
+ }
3190
+ const search = document.createElement("input");
3191
+ search.className = "add-slide-preset-search";
3192
+ search.type = "search";
3193
+ search.placeholder = "Choose a template";
3194
+ dialog.appendChild(search);
3195
+ const list = document.createElement("div");
3196
+ list.className = "add-slide-preset-list";
3197
+ const items = [];
3198
+ (options.presets || []).forEach((preset) => {
3199
+ var _a2;
3200
+ const item = document.createElement("button");
3201
+ item.className = "add-slide-preset-item";
3202
+ item.setAttribute("type", "button");
3203
+ item.innerHTML = `
3204
+ <span class="add-slide-preset-icon">${(_a2 = preset.icon) != null ? _a2 : ""}</span>
3205
+ <span class="add-slide-preset-label">${preset.label}</span>
3206
+ `;
3207
+ item.onclick = (event) => {
3208
+ event.preventDefault();
3209
+ event.stopPropagation();
3210
+ try {
3211
+ const nodeJSON = preset.build();
3212
+ const newSlide = this.view.state.schema.nodeFromJSON(nodeJSON);
3213
+ const tr = this.view.state.tr.insert(
3214
+ pos + this.node.nodeSize,
3215
+ newSlide
3216
+ );
3217
+ this.view.dispatch(tr);
3218
+ } catch (err) {
3219
+ console.error("Failed to insert preset slide", err);
3220
+ } finally {
3221
+ overlay.remove();
3222
+ this.presetModal = null;
3223
+ }
3224
+ };
3225
+ list.appendChild(item);
3226
+ items.push(item);
3227
+ });
3228
+ search.oninput = () => {
3229
+ const term = search.value.toLowerCase();
3230
+ items.forEach((item) => {
3231
+ var _a2, _b;
3232
+ const label = (_b = (_a2 = item.querySelector(".add-slide-preset-label")) == null ? void 0 : _a2.textContent) == null ? void 0 : _b.toLowerCase();
3233
+ item.style.display = !term || (label == null ? void 0 : label.includes(term)) ? "" : "none";
3234
+ });
3235
+ };
3236
+ dialog.appendChild(list);
3237
+ const close2 = () => {
3238
+ overlay.remove();
3239
+ this.presetModal = null;
3240
+ };
3241
+ overlay.onclick = (e) => {
3242
+ if (e.target === overlay) {
3243
+ close2();
3244
+ }
3245
+ };
3246
+ document.addEventListener(
3247
+ "keydown",
3248
+ (e) => {
3249
+ if (e.key === "Escape") {
3250
+ close2();
3251
+ }
3252
+ },
3253
+ { once: true }
3254
+ );
3255
+ overlay.appendChild(dialog);
3256
+ document.body.appendChild(overlay);
3257
+ this.presetModal = overlay;
3164
3258
  };
3165
- this.dom.appendChild(this.contentDOM);
3166
- this.dom.appendChild(this.button);
3259
+ const plusButton = createButton(options.content);
3260
+ plusButton.onclick = handlePlusClick;
3261
+ this.button = plusButton;
3262
+ if (options.showPresets) {
3263
+ const templateButton = createButton(
3264
+ (_a = options.templateButtonContent) != null ? _a : "\u2728"
3265
+ );
3266
+ templateButton.onclick = (event) => {
3267
+ event.preventDefault();
3268
+ event.stopPropagation();
3269
+ const pos = this.getPos();
3270
+ if (pos === void 0) return;
3271
+ openPresetModal(pos);
3272
+ };
3273
+ const group = document.createElement("div");
3274
+ group.className = "add-slide-button-group";
3275
+ group.appendChild(plusButton);
3276
+ group.appendChild(templateButton);
3277
+ this.dom.appendChild(this.contentDOM);
3278
+ this.dom.appendChild(group);
3279
+ } else {
3280
+ this.button = plusButton;
3281
+ this.dom.appendChild(this.contentDOM);
3282
+ this.dom.appendChild(this.button);
3283
+ }
3167
3284
  }
3168
3285
  update(node) {
3169
3286
  if (node.type.name !== "slide") return false;
@@ -3171,7 +3288,13 @@ var SlideNodeView = class {
3171
3288
  return true;
3172
3289
  }
3173
3290
  destroy() {
3174
- this.button.onclick = null;
3291
+ if (this.button) {
3292
+ this.button.onclick = null;
3293
+ }
3294
+ if (this.presetModal) {
3295
+ this.presetModal.remove();
3296
+ this.presetModal = null;
3297
+ }
3175
3298
  }
3176
3299
  };
3177
3300
  var addSlideButtonStyles = `
@@ -3213,6 +3336,131 @@ var addSlideButtonStyles = `
3213
3336
  outline: 2px solid var(--editor-focus, #3b82f6);
3214
3337
  outline-offset: 2px;
3215
3338
  }
3339
+
3340
+ .add-slide-button-group {
3341
+ display: grid;
3342
+ grid-template-columns: repeat(2, 1fr);
3343
+ width: 180px;
3344
+ margin: 16px auto 32px auto;
3345
+ border: 1px solid var(--slide-border, #e5e5e5);
3346
+ border-radius: 12px;
3347
+ overflow: hidden;
3348
+ background-color: var(--slide-bg, #ffffff);
3349
+ box-shadow: var(--slide-shadow, 0 4px 12px rgba(0, 0, 0, 0.08));
3350
+ }
3351
+
3352
+ .add-slide-button-group .add-slide-button {
3353
+ margin: 0;
3354
+ border: none;
3355
+ border-radius: 0;
3356
+ width: 100%;
3357
+ height: 48px;
3358
+ }
3359
+
3360
+ .add-slide-button-group .add-slide-button:first-child {
3361
+ border-right: 1px solid var(--slide-border, #e5e5e5);
3362
+ border-top-left-radius: 12px;
3363
+ border-bottom-left-radius: 12px;
3364
+ }
3365
+
3366
+ .add-slide-button-group .add-slide-button:last-child {
3367
+ border-top-right-radius: 12px;
3368
+ border-bottom-right-radius: 12px;
3369
+ white-space: nowrap;
3370
+ font-size: 14px;
3371
+ padding: 0 12px;
3372
+ }
3373
+
3374
+ .add-slide-preset-modal {
3375
+ position: fixed;
3376
+ inset: 0;
3377
+ background: rgba(0, 0, 0, 0.35);
3378
+ display: flex;
3379
+ align-items: center;
3380
+ justify-content: center;
3381
+ z-index: 9999;
3382
+ }
3383
+
3384
+ .add-slide-preset-dialog {
3385
+ background: var(--add-slide-preset-bg, #ffffff);
3386
+ color: var(--add-slide-preset-fg, #000000);
3387
+ border-radius: 8px;
3388
+ padding: 16px 16px 12px 16px;
3389
+ min-width: 140px;
3390
+ max-width: 480px;
3391
+ max-height: 60vh;
3392
+ overflow: hidden;
3393
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
3394
+ }
3395
+
3396
+ .add-slide-preset-search {
3397
+ width: 100%;
3398
+ padding: 10px 12px;
3399
+ margin-bottom: 10px;
3400
+ border-radius: 8px;
3401
+ border: 1px solid color-mix(in srgb, var(--add-slide-preset-fg, #000000) 18%, transparent);
3402
+ font-size: 14px;
3403
+ color: inherit;
3404
+ background: color-mix(in srgb, var(--add-slide-preset-bg, #ffffff) 90%, var(--add-slide-preset-fg, #000000) 10%);
3405
+ }
3406
+ .add-slide-preset-search:focus {
3407
+ outline: 2px solid color-mix(in srgb, var(--add-slide-preset-fg, #3b82f6) 35%, transparent);
3408
+ outline-offset: 1px;
3409
+ }
3410
+
3411
+ .add-slide-preset-list {
3412
+ display: flex;
3413
+ flex-direction: column;
3414
+ gap: 6px;
3415
+ max-height: 52vh;
3416
+ overflow: auto;
3417
+ scrollbar-width: none;
3418
+ }
3419
+
3420
+ .add-slide-preset-list::-webkit-scrollbar {
3421
+ display: none;
3422
+ }
3423
+
3424
+ .add-slide-preset-item {
3425
+ display: flex;
3426
+ flex-direction: column;
3427
+ align-items: center;
3428
+ gap: 8px;
3429
+ width: 100%;
3430
+ border: none;
3431
+ background: transparent;
3432
+ color: inherit;
3433
+ padding: 10px 0 12px 0;
3434
+ cursor: pointer;
3435
+ border-bottom: 1px solid color-mix(in srgb, var(--add-slide-preset-fg, #000000) 12%, transparent);
3436
+ }
3437
+
3438
+ .add-slide-preset-item:last-child {
3439
+ border-bottom: none;
3440
+ }
3441
+
3442
+ .add-slide-preset-item:hover {
3443
+ background: rgba(0, 0, 0, 0.03);
3444
+ }
3445
+
3446
+ .add-slide-preset-icon {
3447
+ display: block;
3448
+ width: 100%;
3449
+ height: auto;
3450
+ line-height: 0;
3451
+ }
3452
+ .add-slide-preset-icon > svg {
3453
+ width: 100%;
3454
+ height: auto;
3455
+ display: block;
3456
+ }
3457
+
3458
+ .add-slide-preset-label {
3459
+ text-align: center;
3460
+ font-size: 14px;
3461
+ font-weight: 600;
3462
+ width: 100%;
3463
+ }
3216
3464
  `;
3217
3465
  var AddSlideButton = import_core.Extension.create({
3218
3466
  name: "addSlideButton",
@@ -3222,7 +3470,12 @@ var AddSlideButton = import_core.Extension.create({
3222
3470
  injectNonce: void 0,
3223
3471
  buttonStyle: {},
3224
3472
  content: "+",
3225
- onClick: null
3473
+ showPresets: false,
3474
+ presets: [],
3475
+ templateButtonContent: "Template +",
3476
+ onClick: null,
3477
+ presetBackground: "#ffffff",
3478
+ presetForeground: "#000000"
3226
3479
  };
3227
3480
  },
3228
3481
  addProseMirrorPlugins() {